Wiktionary
gdwiktionary
https://gd.wiktionary.org/wiki/Pr%C3%AComh-Dhuilleag
MediaWiki 1.47.0-wmf.14
case-sensitive
Meadhan
Sònraichte
Deasbaireachd
Cleachdaiche
Deasbaireachd a' chleachdaiche
Wiktionary
An deasbaireachd aig Wiktionary
Faidhle
Deasbaireachd an fhaidhle
MediaWiki
Deasbaireachd MediaWiki
Teamplaid
Deasbaireachd na teamplaid
Cobhair
Deasbaireachd na cobharach
Roinn-seòrsa
Deasbaireachd na roinn-seòrsa
TimedText
TimedText talk
Mòideal
Deasbaireachd mòideil
Event
Event talk
Alba
0
4330
88843
86001
2026-08-06T21:43:42Z
Altronic
4137
fix page
88843
wikitext
text/x-wiki
==Gàidhlig==
[[Faidhle:Scotland in the UK and Europe.svg|thumb|Suidheachadh na h-Alba]]
===Freumhachadh===
Bho {{inh|gd|sga|Albu|t=Breatainn Mhòr}}.
===Fuaimneachadh===
* {{IPA|gd|/ˈaˈl̪ˠa.pə/}}
===Ainmear sònrachaidh===
{{gd-proper noun|b|Alba|-}}
# [[tìr|Tìr]] a th' ann an [[tuath]] na [[Breatainn Mhòr|Breatainne Mòire]].
## Dùthaich a th' ann an [[Rìoghachd Aonaichte]].
## {{lb|gd|eachdraidheil}} [[Rìoghachd na h-Alba]], [[rìoghachd]] gu robh anns a' Bhreatainn Mhòir gu ruige cruthachadh na [[Rìoghachd na Breatainne Mòire]].
===Mùthadh===
{{gd-mut}}
===Barrachd fiosrachaidh===
* {{R:Dwelly}}
oc7m48w0silpm78m6erwbwv00f2gxuf
Mòideal:gd-conj
828
16619
88994
88704
2026-08-06T23:30:18Z
Altronic
4137
88994
Scribunto
text/plain
local export = {}
local wikitext = [=[
! rowspan="2" | taisbeanach
! colspan="2" | neo-eisimeileach
! colspan="2" | eisimeileach
|- class="secondary"
! class="secondary" | [[pearsanta]]
! class="secondary" | [[fulangach]]
! class="secondary" | [[pearsanta]]
! class="secondary" | [[fulangach]]
|-
! caithte
| {{{past-indep}}}
| {{{past-indep-impersonal}}}
| {{{past-dep}}}
| {{{past-dep-impersonal}}}
|-
! teachdail
| {{{fut-indep}}}
| {{{fut-indep-impersonal1}}}</br>{{{fut-indep-impersonal2}}}
| {{{fut-dep}}}
| {{{fut-dep-impersonal1}}}</br>{{{fut-indep-impersonal2}}}
|-
! teachdail dàimheach
| {{{relfut}}}
| {{{relfut-impersonal}}}
| —
| —
|-
| class="separator" colspan=9 |
|- class="inflection-table-cyan"
! rowspan="2" | conditional
! colspan="2" | neo-eisimeileach
! colspan="2" | eisimeileach
|- class="inflection-table-cyan"
! class="secondary" | [[pearsanta]]
! class="secondary" | [[fulangach]]
! class="secondary" | [[pearsanta]]
! class="secondary" | [[fulangach]]
|- class="inflection-table-cyan"
! a' chiad phearsa singilte
| {{{cond-indep-first}}}
| rowspan=3 | {{{cond-indep-first-impersonal}}}<br>{{{cond-indep-first-impersonal-l1}}}<sup>1</sup><br>{{{cond-indep-first-impersonal-l2}}}<sup>1</sup>
| {{{cond-dep-first}}}
| rowspan=3 | {{{cond-dep-first-impersonal}}}<br>{{{cond-dep-first-impersonal-l1}}}<sup>1</sup><br>{{{cond-dep-first-impersonal-l2}}}<sup>1</sup>
|- class="inflection-table-cyan"
! a' chiad phearsa iolra
| {{{cond-indep-first-pl}}}<br>{{{cond-indep-third}}} sinn
| {{{cond-dep-first-pl}}}<br>{{{cond-dep-third}}} sinn
|- class="inflection-table-cyan"
! an dàrna/treas pearsa
| {{{cond-indep-third}}}
| {{{cond-dep-third}}}
|-
| class="separator" colspan=4 |
! colspan=5 rowspan=6 class=blank-end-row |
|- class="inflection-table-green"
! rowspan=2 | àithneach
! colspan=3 | neo-eisimeileach
|- class="inflection-table-green"
! class="secondary" | singilte
! class="secondary" | iolra
! class="secondary" | [[fulangach]]
|- class="inflection-table-green"
! a' chiad phearsa
| {{{imper-first}}}
| {{{imper-first-pl}}}
| rowspan=3 | {{{imper-impersonal1}}}<br>{{{imper-impersonal2}}}
|- class="inflection-table-green"
! an dàrna pearsa
| {{{imper-second}}}
| {{{imper-second-pl}}}
|- class="inflection-table-green"
! an treas pearsa
| colspan=2 | {{{imper-third}}}
|-
| class="separator" colspan=4 |
! colspan=5 rowspan=4 class=blank-end-row |
|- class="inflection-table-amber"
! pàirtear caithte
| colspan=3 | {{{participle}}}
|-
| class="separator" colspan=4 |
|- class="inflection-table-yellow"
! ainmear gnìomhaireach
| colspan=3 | {{{vn}}}
]=]
local participle = nil
local vn = nil
local fut = nil
local langcode = require("Module:languages").getByCode("gd")
local m_links = require("Module:links")
local function mutate(term)
local lower = mw.ustring.lower(term)
local out = term
if mw.ustring.find(lower, "^[bsdfgmpst]h") then
return out
end
if mw.ustring.find(lower, "^[bcdfgmpt]") then
out = mw.ustring.gsub(term, "^(.)", "%1h")
elseif mw.ustring.find(lower, "^s[aeiouàèìòùáéólnr]") then
out = mw.ustring.gsub(term, "^(.)", "%1h")
end
if mw.ustring.find(lower, "^[k]") then
out = mw.ustring.gsub(term, "^(.)", "ch")
end
if mw.ustring.find(lower, "^[aàeèiìoòuùáéó]") then
out = mw.ustring.gsub(term, "^(.)", "dh'%1")
elseif mw.ustring.find(out, "^fh[aeiouàèìòùáéó]") then
out = mw.ustring.gsub(out, "^(.)", "dh'%1")
end
return out
end
local function ending(type, stem, broad, slender)
local out
if type == "broad" then
out = stem .. broad
else
out = stem .. slender
end
return out
end
local function get_vowel_type(string)
local function contains(table, entry)
for _, value in ipairs(table) do
if value == entry then
return true
end
end
return false
end
local len = mw.ustring.len(string)
local broad = { 'a', 'à', 'á', 'o', 'ò', 'ó'}
local slender = { 'e', 'è', 'é', 'i', 'ì' }
for i = len, 1, -1 do
local char = mw.ustring.sub(string, i, i)
if contains(broad, char) then
return "broad"
elseif contains(slender, char) then
return "slender"
end
end
return "no-vowel"
end
local function conjugate(param, term)
local conj = term
local type = get_vowel_type(conj)
if param == "participle" then
if participle then
return m_links.full_link({ lang = langcode, term = participle }, nil, true)
else
return m_links.full_link({ lang = langcode, term = ending(type, conj, "ta", "te") })
end
end
if param == "vn" then
conj = m_links.full_link({ lang = langcode, term = vn }, nil, true)
return conj
end
-- switch statement of all the possible conjugation types
-- indicative
if param == "past-indep" or param == "past-dep" then
conj = mutate(conj)
elseif param == "past-indep-impersonal" or param == "past-dep-impersonal" then
conj = mutate(conj)
conj = ending(type, conj, "adh", "eadh")
elseif param == "fut-indep" then
if fut ~= nil then
conj = fut
type = get_vowel_type(conj)
end
conj = ending(type, conj, "aidh", "idh")
elseif param == "fut-indep-impersonal1" or param == "fut-dep-impersonal1" or param == "imper-impersonal1" then
if fut ~= nil then
conj = fut
type = get_vowel_type(conj)
end
conj = ending(type, conj, "ar", "ear")
elseif param == "fut-indep-impersonal2" or param == "fut-dep-impersonal2" or param == "imper-impersonal2" then
if fut ~= nil then -- TODO: forms with syncope don't have the ~tar forms, see [[labhair]] as an example
conj = fut
type = get_vowel_type(conj)
end
conj = ending(type, conj, "tar", "tear")
elseif param == "fut-dep" then
return m_links.full_link({ term = conj, lang = langcode }, nil, true)
elseif param == "relfut" then
conj = mutate(conj)
conj = ending(type, conj, "as", "eas")
elseif param == "relfut-impersonal" then
conj = mutate(conj)
conj = ending(type, conj, "ar", "ear")
-- conditional
elseif param == "cond-indep-first" then
conj = mutate(conj)
conj = ending(type, conj, "ainn", "inn")
elseif param == "cond-indep-first-impersonal" then
conj = mutate(conj)
conj = ending(type, conj, "tadh", "teadh")
elseif param == "cond-indep-first-impersonal-l1" then
conj = mutate(conj)
conj = ending(type, conj, "aist", "ist")
elseif param == "cond-indep-first-impersonal-l2" then
conj = mutate(conj)
conj = ending(type, conj, "aiste", "iste")
elseif param == "cond-indep-first-pl" then
conj = mutate(conj)
conj = ending(type, conj, "amaid", "eamaid")
elseif param == "cond-indep-third" then
conj = mutate(conj)
conj = ending(type, conj, "adh", "eadh")
elseif param == "cond-dep-first" then
conj = ending(type, conj, "ainn", "inn")
elseif param == "cond-dep-first-impersonal" then
conj = ending(type, conj, "tadh", "teadh")
elseif param == "cond-dep-first-impersonal-l1" then
conj = ending(type, conj, "aist", "ist")
elseif param == "cond-dep-first-impersonal-l2" then
conj = ending(type, conj, "aiste", "iste")
elseif param == "cond-dep-first-pl" then
conj = ending(type, conj, "amaid", "eamaid")
elseif param == "cond-dep-third" then
conj = ending(type, conj, "adh", "eadh")
elseif param == "imper-first" then
conj = ending(type, conj, "am", "eam")
elseif param == "imper-first-pl" then
conj = ending(type, conj, "amaid", "eamaid")
elseif param == "imper-second" then
return m_links.full_link({ term = conj, lang = langcode }, nil, true)
elseif param == "imper-second-pl" then
conj = ending(type, conj, "aibh", "ibh")
elseif param == "imper-third" then
conj = ending(type, conj, "adh", "eadh")
end
return m_links.full_link({ term = conj, lang = langcode }, nil, true)
end
function export.draw(frame)
local parentargs = frame:getParent().args
local params = { [1] = { required = true, type = "parameter" }, ["pp"] = true, ["fut"] = true }
local args = require("Module:parameters").process(parentargs, params)
local term = require("Module:headword/data").pagename
vn = args[1]
if args["pp"] ~= nil then
participle = args["pp"]
end
if args["fut"] ~= nil then
fut = args["fut"]
end
local result = frame:expandTemplate{
title = "inflection-table-top",
args = {
title = "Co-nasgadh dhe " .. m_links.full_link({ lang = langcode, alt = term }, 'term') .. " ([[Appendix:Gnìomhairean na Gàidhlig|riaghailteach]])",
tall = "yes",
palette = "blue"
}
}
result = result .. '\n'
result = result .. mw.ustring.gsub(wikitext, "{{{([a-z0-9_-]+)}}}", function(param) return conjugate(param, term) end)
result = result .. frame:expandTemplate{
title = "inflection-table-bottom",
args = {
notes = "1. Ann an Leòdhas"
}
}
return result
end
return export
7neup56n698nqfkjbse98iwl2bsok4i
seo
0
16840
88846
87974
2026-08-06T21:56:51Z
Altronic
4137
Fix entry
88846
wikitext
text/x-wiki
==Gàidhlig na h-Èireann==
===Cruthan eile===
* {{alter|ga|so||Mumhain}}
* {{alter|ga|seod|seobh||CF}}{{ref|{{R:ga:GCFD|349|page=161}}}}
===Freumhachadh===
Bho {{inh|ga|sga|so}}.
===Fuaimneachadh===
* {{IPA|ga|/ʃɔ(h)/|ref={{R:ga:SjPh|67|section=126}} !!! {{R:ga:Quiggin|120|351}} !!! {{R:ga:Ó Searcaigh|page=8|section=12}}}}
===Riochdair===
{{head|ga|riochdair}}
# seo
#: {{ux|ga|Seo mo theach.|Seo mo thaigh.|inline=1}}
===Cinntiche===
{{head|ga|cinntiche}}
# seo {{gloss|leis {{m|ga|an}}}}
#: {{ux|ga|An bhfuil tú ag iarraidh an leabhar seo a fháil ar iasacht?|A bheil thu ag iarraidh an leabhar seo a ghabhail air iasad?}}
===Co-ghnìomhair===
{{head|ga|co-ghnìomhair}}
# [[an-seo]] {{gloss|a' filleadh gluasad}}
#: {{ux|ga|Cuir é seo.|Cuir e (sìos) an-seo.}}
===Iomraidhean===
{{reflist|size=smaller}}
===Barrachd fiosrachaidh===
* {{R:ga:Ó Dónaill}}
* {{R:ga:NEID}}
* {{R:DIL|38191|so}}
cbctx2bl3q8x9yj73tx9eqqi9bkdnyh
Mòideal:quote
828
17507
88847
87915
2026-08-06T22:00:45Z
Altronic
4137
change text
88847
Scribunto
text/plain
--[=[
This module contains functions to implement quote-* templates.
Author: Benwing2; conversion into Lua of {{quote-meta/source}} template,
written by Sgconlaw with some help from Erutuon and Benwing2.
The main interface is quote_t(). Note that the source display is handled by source(), which reads both the
arguments passed to it *and* the arguments passed to the parent template, with the former overriding the latter.
]=]
local export = {}
--[=[
FIXME:
1. Dates like `1 march 2025` with the day of the month being 1 and the month lowercased are treated as month-year-only.
The relevant code checks for a capitalized word; it should probably instead check for the actual month names or
abbrevs.
]=]
-- Named constants for all modules used, to make it easier to swap out sandbox versions.
local check_isxn_module = "Module:check isxn"
local debug_track_module = "Module:debug/track"
local en_utilities_module = "Module:en-utilities"
local italics_module = "Module:italics"
local labels_module = "Module:labels"
local languages_module = "Module:languages"
local languages_error_module = "Module:languages/error"
local links_module = "Module:links"
local number_utilities_module = "Module:number utilities"
local parameters_module = "Module:parameters"
local parse_utilities_module = "Module:parse utilities"
local qualifier_module = "Module:qualifier"
local roman_numerals_module = "Module:roman numerals"
local scribunto_module = "Module:Scribunto"
local script_utilities_module = "Module:script utilities"
local scripts_module = "Module:scripts"
local string_pattern_escape_module = "Module:string/patternEscape"
local string_replacement_escape_module = "Module:string/replacementEscape"
local string_utilities_module = "Module:string utilities"
local table_module = "Module:table"
local template_parser_module = "Module:template parser"
local usex_module = "Module:usex"
local usex_templates_module = "Module:usex/templates"
local utilities_module = "Module:utilities"
local yesno_module = "Module:yesno"
local concat = table.concat
local insert = table.insert
local new_title = mw.title.new
local remove = table.remove
local require = require
local sort = table.sort
local u = mw.ustring.char
local ugsub = mw.ustring.gsub
local umatch = mw.ustring.match
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
-- Use HTML entities here to avoid parsing issues (esp. with brackets)
local SEMICOLON_SPACE = "; "
local SPACE_LBRAC = " ["
local RBRAC = "]"
local TEMP_LT = u(0xFFF1)
local TEMP_GT = u(0xFFF2)
local TEMP_LBRAC = u(0xFFF3)
local TEMP_RBRAC = u(0xFFF4)
local TEMP_SEMICOLON = u(0xFFF5)
local function apply_subst(...)
apply_subst = require(usex_module).apply_subst
return apply_subst(...)
end
local function check_isbn(...)
check_isbn = require(check_isxn_module).check_isbn
return check_isbn(...)
end
local function check_issn(...)
check_issn = require(check_isxn_module).check_issn
return check_issn(...)
end
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function decode_entities(...)
decode_entities = require(string_utilities_module).decode_entities
return decode_entities(...)
end
local function embedded_language_links(...)
embedded_language_links = require(links_module).embedded_language_links
return embedded_language_links(...)
end
local function escape_wikicode(...)
escape_wikicode = require(parse_utilities_module).escape_wikicode
return escape_wikicode(...)
end
local function find_best_script_without_lang(...)
find_best_script_without_lang = require(scripts_module).findBestScriptWithoutLang
return find_best_script_without_lang(...)
end
local function find_parameters(...)
find_parameters = require(template_parser_module).find_parameters
return find_parameters(...)
end
local function format_categories(...)
format_categories = require(utilities_module).format_categories
return format_categories(...)
end
local function format_processed_labels(...)
format_processed_labels = require(labels_module).format_processed_labels
return format_processed_labels(...)
end
local function format_qualifier(...)
format_qualifier = require(qualifier_module).format_qualifier
return format_qualifier(...)
end
local function format_usex(...)
format_usex = require(usex_module).format_usex
return format_usex(...)
end
local function get_lang(...)
get_lang = require(languages_module).getByCode
return get_lang(...)
end
local function get_number(...)
get_number = require(number_utilities_module).get_number
return get_number(...)
end
local function get_script(...)
get_script = require(scripts_module).getByCode
return get_script(...)
end
local function gsplit(...)
gsplit = require(string_utilities_module).gsplit
return gsplit(...)
end
local function page_should_be_ignored(...)
page_should_be_ignored = require(usex_templates_module).page_should_be_ignored
return page_should_be_ignored(...)
end
local function parse_inline_modifiers(...)
parse_inline_modifiers = require(parse_utilities_module).parse_inline_modifiers
return parse_inline_modifiers(...)
end
local function parse_inline_modifiers_from_segments(...)
parse_inline_modifiers_from_segments = require(parse_utilities_module).parse_inline_modifiers_from_segments
return parse_inline_modifiers_from_segments(...)
end
local function parse_multi_delimiter_balanced_segment_run(...)
parse_multi_delimiter_balanced_segment_run = require(parse_utilities_module).parse_multi_delimiter_balanced_segment_run
return parse_multi_delimiter_balanced_segment_run(...)
end
local function parse_term_with_lang(...)
parse_term_with_lang = require(parse_utilities_module).parse_term_with_lang
return parse_term_with_lang(...)
end
local function pattern_escape(...)
pattern_escape = require(string_pattern_escape_module)
return pattern_escape(...)
end
local function pluralize(...)
pluralize = require(en_utilities_module).pluralize
return pluralize(...)
end
local function process_params(...)
process_params = require(parameters_module).process
return process_params(...)
end
local function remove_links(...)
remove_links = require(links_module).remove_links
return remove_links(...)
end
local function roman_to_arabic(...)
roman_to_arabic = require(roman_numerals_module).roman_to_arabic
return roman_to_arabic(...)
end
local function replacement_escape(...)
replacement_escape = require(string_replacement_escape_module)
return replacement_escape(...)
end
local function scribunto_parameter_key(...)
scribunto_parameter_key = require(scribunto_module).scribunto_parameter_key
return scribunto_parameter_key(...)
end
local function serial_comma_join(...)
serial_comma_join = require(table_module).serialCommaJoin
return serial_comma_join(...)
end
local function shallow_copy(...)
shallow_copy = require(table_module).shallowCopy
return shallow_copy(...)
end
local function split(...)
split = require(string_utilities_module).split
return split(...)
end
local function split_alternating_runs(...)
split_alternating_runs = require(parse_utilities_module).split_alternating_runs
return split_alternating_runs(...)
end
local function split_and_process_raw_labels(...)
split_and_process_raw_labels = require(labels_module).split_and_process_raw_labels
return split_and_process_raw_labels(...)
end
local function split_on_comma(...)
split_on_comma = require(parse_utilities_module).split_on_comma
return split_on_comma(...)
end
local function tag_text(...)
tag_text = require(script_utilities_module).tag_text
return tag_text(...)
end
local function tag_transcription(...)
tag_transcription = require(script_utilities_module).tag_transcription
return tag_transcription(...)
end
local function tag_translit(...)
tag_translit = require(script_utilities_module).tag_translit
return tag_translit(...)
end
local function ulen(...)
ulen = require(string_utilities_module).len
return ulen(...)
end
local function unitalicize_brackets(...)
unitalicize_brackets = require(italics_module).unitalicize_brackets
return unitalicize_brackets(...)
end
local function upper(...)
upper = require(string_utilities_module).upper
return upper(...)
end
local function usub(...)
usub = require(string_utilities_module).sub
return usub(...)
end
local function yesno(...)
yesno = require(yesno_module)
return yesno(...)
end
local function track(page)
debug_track("quote/" .. page)
end
local function maintenance_line(text)
return '<span class="maintenance-line">(' .. text .. ")</span>"
end
local function isbn(text)
return "[[Special:BookSources/"
.. text
.. "|→ISBN]]"
.. check_isbn(
text,
' <span class="error" style="font-size:88%">Invalid ISBN</span>[[Category:Pages with ISBN errors]]'
)
end
local function issn(text)
return "[https://www.worldcat.org/issn/"
.. text
.. " →ISSN]"
.. check_issn(
text,
' <span class="error" style="font-size:88%">Invalid ISSN</span>[[Category:Pages with ISSN errors]]'
)
end
local function lccn(text)
text = text:gsub(" ", "")
if text:find("-") then
-- old-style LCCN; reformat per request by [[User:The Editor's Apprentice]]
local prefix, part1, part2 = text:match("^(.-)(%d+)%-(%d+)$")
if prefix then
if ulen(part2) < 6 then
part2 = ("0"):rep(6 - ulen(part2)) .. part2
end
text = prefix .. part1 .. part2
end
end
return "[https://lccn.loc.gov/" .. mw.uri.encode(text) .. " →LCCN]"
end
local function format_date(text)
return mw.getCurrentFrame():callParserFunction("#formatdate", text, "dmy")
end
-- Parse a raw lb= param (or nil) to individual label info objects and then concatenate them appropriately into a
-- qualifier input, respecting flags like `omit_preComma` and `omit_postSpace` in the label specs.
local function parse_and_format_labels(raw_lb, lang)
if not raw_lb then
return nil
end
local labels = split_and_process_raw_labels{labels = raw_lb, lang = lang, nocat = true}
labels = format_processed_labels{labels = labels, lang = lang, no_ib_content = true}
if labels ~= "" then -- not sure labels can be an empty string but it seems possible in some circumstances
return {labels}
end
end
-- Convert a comma-separated list of language codes to a comma-separated list of language names. `fullname` is the
-- name of the parameter from which the list of language codes was fetched.
local function format_langs(langs)
local names = {}
for i, lang in ipairs(langs) do
names[i] = lang:getCanonicalName()
end
if #names == 1 then
return names[1]
end
return serial_comma_join(names)
end
local function get_first_lang(langs)
return langs[1] or get_lang("und")
end
--[=[
Normally we parse off inline modifiers and language code prefixes in various places, e.g. he:מרים<tr:Miryem>. But we
exclude HTML entries with <span ...>, <i ...>, <br/> or similar in it, caused by wrapping an argument in {{l|...}},
{{lang|...}} or similar. Basically, all tags of the sort we parse here should consist of a less-than sign, plus letters,
plus a colon, e.g. <tr:...>, so if we see a tag on the outer level that isn't in this format, we don't try to parse it.
The restriction to the outer level is to allow generated HTML inside of e.g. qualifier modifiers, such as
foo<q:similar to {{m|fr|bar}}> (if we end up supporting such modifiers).
Also exclude things that look like URL's from being parsed as having language code prefixes.
]=]
local function val_should_not_be_parsed_for_annotations(val)
return val:find("^[^<]*<%l*[^%l:]") or val:find("^%l+://")
end
local param_mods = {
t = {
-- <t:...> and <gloss:...> are aliases.
item_dest = "gloss",
},
gloss = {},
alt = {},
tr = {},
ts = {},
subst = {},
sc = {type = "script"},
f = {
convert = function(arg, parse_err)
local prefix, val = arg:match("^(.-):([^ ].*)$")
if not prefix then
prefix = ""
val = arg
end
local tags, sc_code, sc = prefix:match("^(.*)/(.-)$")
if sc_code then
sc = get_script(sc_code) or
require(languages_error_module)(sc_code, parse_err, "script code", nil, "not real lang")
else
tags = prefix
end
local quals
if tags ~= "" then
quals = split_on_comma(tags)
for i, qual in ipairs(quals) do
local obj = get_lang(qual, nil, "allow etym") or get_script(qual)
quals[i] = obj or qual
end
end
return {
quals = quals,
sc = sc,
val = val,
}
end,
store = "insert",
},
q = {},
qq = {},
}
local function generate_obj_annotated_text(text, parse_err, paramname)
local obj = {}
if text:find(":[^ ]") or text:find("%[%[") then
local display, is_wikipedia_wikisource
obj.text, obj.lang, obj.link, display, is_wikipedia_wikisource =
parse_term_with_lang {
term = text,
parse_err = parse_err,
paramname = paramname
}
-- HACK: If object is a Wikipedia or Wikisource link, we need to convert it to a two-part link
-- to avoid the w: or s: prefix showing. We should probably avoid this by calling
-- language_link() (in [[Module:links]]) in format_annotated_text() instead of the hacky stuff
-- that we currently do; but we need to test that carefully to make sure it doesn't do things like
-- munge % signs.
if is_wikipedia_wikisource and not obj.text:find("%[%[") and not obj.text:find("%]%]") then
-- Don't directly set .alt, because then if the user specifies <alt:...>, it will cause an error in
-- parse_inline_modifiers().
obj.display = display
end
else
obj.text = text
obj.link = text
end
return obj
end
--[=[
Parse a textual property that may be in a foreign language or script and may be annotated with a language prefix and/or
inline modifiers. `val` is the value of the parameter and `fullname` is the name of the parameter from which the value
was retrieved. `explicit_gloss`, if specified and non-nil, overrides any gloss specified using the <t:...> or
<gloss:...> inline modifier.
If `val` is nil, the return value of this function is nil. Otherwise it is parsed for a language prefix (e.g.
'ar:مُؤَلِّف') and inline modifiers (e.g. 'ar:مُؤَلِّف<t:Author>'), and the return value is an object with the following
fields:
`lang`: The language object corresponding to the language prefix, if specified, or nil if no language prefix is
given.
`text`: The text after stripping off any language prefix and inline modifiers.
`link`: The link part of the text if it consists of a two-part link; otherwise, same as `text`.
`alt`: Display text specified using the <alt:...> modifier, if given; otherwise, nil.
`subst`: Substitutions used to generate the transliteration, in the same format as the subst= parameter.
`sc`: The script object corresponding to the <sc:...> modifier, if given; otherwise nil.
`tr`: The transliteration corresponding to the <tr:...> modifier, if given; otherwise nil.
`ts`: The transcription corresponding to the <ts:...> modifier, if given; otherwise nil.
`gloss`: The gloss/translation corresponding to the `explicit_gloss` parameter (if given and non-nil), otherwise
the <t:...> or <gloss:...> modifiers if given, otherwise nil.
`f`: Foreign versions of the text.
`q`: Left qualifiers.
`qq`: Right qualifiers.
Note that as a special case, if `val` contains HTML tags at the top level (e.g. '<span class="Arab">...</span>', as
might be generated by specifying {{lang|ar|مُؤَلِّف}}), no language prefix or inline modifiers are parsed, and the return
value has the `noscript` field set to true, which tells format_annotated_text() not to try to identify the script of
the text and CSS-tag the text accordingly, but to leave the text untagged.
This object can be passed to format_annotated_text() to format a string displaying the text (appropriately
script-tagged, unless `noscript` is set, as described above) and modifiers.
]=]
local function parse_annotated_text(val, fullname, explicit_gloss)
if not val then
return nil
end
-- When checking for inline modifiers, exclude HTML entry with <span ...>, <i ...>, <br/> or similar in it, caused
-- by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Also exclude URL's from being parsed as having
-- language code prefixes. See val_should_not_be_parsed_for_annotations() for more information. If we find a
-- parameter value with top-level HTML in it, add 'noscript = true' to indicate that we should not try to do script
-- inference and tagging. (Otherwise, e.g. if you specify {{lang|ar|مُؤَلِّف}} as the author, you'll get an extra big
-- font coming from the fact that {{lang|...}} wraps the Arabic text in CSS that increases the size from the
-- default, and then we do script detection and again wrap the text in the same CSS, which increases the size even
-- more.)
if val_should_not_be_parsed_for_annotations(val) then
return {text = val, link = val, noscript = true, gloss = explicit_gloss}
end
local obj
if val:find("<") then
-- Check for inline modifier.
obj = parse_inline_modifiers(val, {
paramname = fullname,
param_mods = param_mods,
generate_obj = generate_obj_annotated_text,
})
else
obj = generate_obj_annotated_text(val, nil, fullname)
end
if explicit_gloss then
obj.gloss = explicit_gloss
end
return obj
end
local html_entity_char_to_replacement = {
["<"] = TEMP_LT,
[">"] = TEMP_GT,
["["] = TEMP_LBRAC,
["]"] = TEMP_RBRAC,
}
local function html_entity_replacement(entity, code_without_semicolon, hash, xcode, x, code)
-- Try to decode the entity. If successful, Replace certain special HTML entities (those that are bracket-like)
-- with single Unicode characters; otherwise, replace the semicolon with a special character so it won't get
-- interpreted as a delimiter.
local ch = decode_entities(entity)
if ch ~= entity then
return html_entity_char_to_replacement[ch] or code_without_semicolon .. TEMP_SEMICOLON
end
-- If the entity doesn't decode, escape it anyway iff it follows a valid format.
if hash == "" then
-- Any nonstandard MediaWiki-only entities have now been filtered off, so treat any non-ASCII characters as
-- invalid.
return xcode:match("^[^\128-\255]+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
elseif x == "" then
return xcode:match("^%d+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
end
return code:match("^%x+$") and code_without_semicolon .. TEMP_SEMICOLON or entity
end
local html_entity_replacement_to_char = {
[TEMP_LT] = "<",
[TEMP_GT] = ">",
[TEMP_LBRAC] = "[",
[TEMP_RBRAC] = "]",
[TEMP_SEMICOLON] = ";",
}
local function undo_html_entity_replacement(txt)
-- Pattern covers everything in html_entity_replacement_to_char.
return (txt:gsub("\239\191[\177-\181]", html_entity_replacement_to_char))
end
-- NOTE: We try hard to optimize this function for the common cases and avoid loading [[Module:parse utilities]]
-- in such cases. The cases we can handle without loading [[Module:parse utilities]] are single values (no
-- semicolons present) without inline modifiers or language prefixes, and multi-entity values (semicolons present)
-- without (a) brackets of any kind (including parens, braces and angle brackets; angle brackets typically indicate
-- inline modifiers and other brackets may protect a semicolon from being interpreted as a delimiter);
-- (b) ampersands (which may indicate HTML entities, which protect a semicolon from being interpreted as a
-- delimiter); and (c) colons not followed by a space (which may indicate a language prefix).
local function generate_obj_multivalued_annotated_text(text, parse_err, paramname, no_undo_html_entity_replacement)
local obj = generate_obj_annotated_text(text, parse_err, paramname)
if not no_undo_html_entity_replacement then
obj.text = undo_html_entity_replacement(obj.text)
obj.link = undo_html_entity_replacement(obj.link)
end
return obj
end
--[=[
Similar to parse_annotated_text() but the parameter value may contain multiple semicolon-separated entities, each with
their own inline modifiers. Some examples:
* mainauthor=Paula Pattengale; Terea Sonsthagen
* author=Katie Brick; J. Cody Nielsen; Greg Jao; Eric Paul Rogers; John A. Monson
* author=Suzanne Brockmann; Patrick G. Lawlor (Patrick Girard); Melanie Ewbank
* author=G Ristori; et al.
* author=Jason Scott; zh:王晰宁<t:Wang Xining>
* editors=zh:包文俊; zh:金心雯
* quotee=zh:張福運<t:Chang Fu-yun>; zh:張景文<t:Chang Ching-wen>
There may be embedded semicolons within brackets, braces or parens that should not be treated as delimiters, e.g.:
* author=Oliver Optic [pseudonym; {{w|William Taylor Adams}}]
* author=author=Shannon Drake (pen name; {{w|Heather Graham Pozzessere}})
* author=James (the Elder;) Humphrys
There may also be HTML entities with semicolons in them:
* author=[{{w|Gilbert Clerke}}]
* 2ndauthor=Martin Biddle & Sally Badham
* author=Peter Christen Asbjørnsen
There may be both embedded semicolons and HTML entities with semicolons in them:
* author=[{{w|Voltaire}} [pseudonym; François-Marie Arouet]]
In general we want to treat [ like an opening bracket and ] like a closing bracket. Beware that they may be
mismatched:
* author=Anonymous [{{w|Karl Maria Kertbeny}}]
Here, `val` is the value of the parameter and `fullname` is the name of the parameter from which the value was
retrieved. `explicit_gloss`, if specified and non-nil, overrides any gloss specified using the <t:...> or <gloss:...>
inline modifier, and `explicit_gloss_fullname` is the name of the parameter from which this value was retrieved. (If
`explicit_gloss` is specified and multiple values were seen, an error results.)
Return value is a list of objects of the same sort as returned by parse_annotated_text().
]=]
local function parse_multivalued_annotated_text(val, fullname, explicit_gloss, explicit_gloss_fullname)
if not val then
return nil
end
-- NOTE: In the code that follows, we use `entity` most of the time to refer to one of the semicolon-separated
-- values in the multivalued param. Entities are most commonly people (typically authors, editors, translators or
-- the like), but may be the names of publishers, locations, or other entities. "Entity" can also refer to HTML
-- entities; in the places where this occurs, the variable name contains 'html' in it.
local splitchar, english_delim
if val:find("^,") then
splitchar = ","
english_delim = "comma"
val = val:gsub("^,", "")
else
splitchar = ";"
english_delim = "semicolon"
end
-- Optimization #1: No semicolons/commas or angle brackets (indicating inline modifiers).
if not val:find("[<" .. splitchar .. "]") then
if val_should_not_be_parsed_for_annotations(val) then
return {{text = val, link = val, noscript = true}}
else
return {generate_obj_multivalued_annotated_text(val, nil, fullname, "no undo html entity replacement")}
end
end
-- Optimization #2: Semicolons/commas but no angle brackets (indicating inline modifiers), braces, brackets, or
-- parens (any of which would protect the semicolon/comma from interpretation as a delimiter), and no ampersand
-- (which might indicate an HTML entity with a terminating semicolon, which should not be interpreted as a
-- delimiter).
if not val:find("[<>%[%](){}&]") then
local entity_objs = {}
for entity in gsplit(val, "%s*" .. splitchar .. "%s*") do
if val_should_not_be_parsed_for_annotations(entity) then
insert(entity_objs, {
text = entity,
link = entity,
noscript = true
})
else
insert(entity_objs, generate_obj_multivalued_annotated_text(entity, nil, fullname, "no undo html entity replacement"))
end
end
return entity_objs
end
-- Escape HTML entities, and get rid of directionality markers.
local amp = val:find("&", nil, true)
if amp then
-- The pattern is more permissive than the usual entity pattern, as MediaWiki has some nonstandard entities
-- that have non-ASCII characters in their codes.
val = val:gsub("((&(#?)(([xX]?)([%w\128-\255]+)));)", html_entity_replacement)
end
-- Pattern covers left-to-right (U+200E) and right-to-left (U+200F).
val = val:gsub("\226\128[\142\143]", "")
-- Parse balanced segment runs, treating HTML entities for left and right bracket and left and right angle bracket
-- as matching literal versions of the same characters.
local entity_runs = parse_multi_delimiter_balanced_segment_run(
val,
{{"[" .. TEMP_LBRAC, "]" .. TEMP_RBRAC}, {"(", ")"}, {"{", "}"}, {"<" .. TEMP_LT, ">" .. TEMP_GT}},
true
)
if type(entity_runs) == "string" then
local undo_val = undo_html_entity_replacement(val)
-- Parse error due to unbalanced delimiters. Don't throw an error here; instead, don't attempt to parse off
-- any annotations, but return the value directly, maybe allowing script tagging (not allowing it if it appears
-- the text is already script-tagged).
return {{text = undo_val, link = undo_val, noscript = not not val_should_not_be_parsed_for_annotations(val)}}
end
-- Split on semicolon (or comma), possibly surrounded by whitespace.
local separated_groups = split_alternating_runs(entity_runs, "%s*" .. splitchar .. "%s*")
-- Process each value.
local entity_objs = {}
for _, entity_group in ipairs(separated_groups) do
-- Rejoin runs that don't involve <...>.
local j = 2
while j <= #entity_group do
if not entity_group[j]:find("^<.*>$") then
entity_group[j - 1] = entity_group[j - 1] .. entity_group[j] .. entity_group[j + 1]
remove(entity_group, j)
remove(entity_group, j)
else
j = j + 2
end
end
local oneval = undo_html_entity_replacement(concat(entity_group))
-- When checking for inline modifiers, exclude HTML entry with <span ...>, <i ...>, <br/> or similar in it,
-- caused by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Also exclude URL's from being parsed
-- as having language code prefixes. This works analogously to parse_annotated_text(); see there for more.
if val_should_not_be_parsed_for_annotations(oneval) then
insert(entity_objs, {
text = oneval,
link = oneval,
noscript = true
})
else
local obj
if #entity_group > 1 then
-- Check for inline modifier.
obj = parse_inline_modifiers_from_segments({
group = entity_group,
arg = oneval,
props = {
paramname = fullname,
param_mods = param_mods,
generate_obj = generate_obj_multivalued_annotated_text,
},
})
else
obj = generate_obj_multivalued_annotated_text(entity_group[1], nil, fullname)
end
insert(entity_objs, obj)
end
end
if explicit_gloss then
if #entity_objs > 1 then
error(
(
"Can't specify |%s= along with multiple %s-separated entities in |%s=; use the <t:...> "
.. "inline modifier attached to the individual entities"
):format(explicit_gloss_fullname, english_delim, fullname)
)
end
entity_objs[1].gloss = explicit_gloss
end
return entity_objs
end
--[=[
Format a text property that may be in a foreign language or script, along with annotations. This is conceptually
similar to the full_link() function in [[Module:links]], but displays the annotations in a different format that is
more appropriate for bibliographic entries. The output looks like this:
TEXT [TRANSLIT /TRANSCRIPTION/, GLOSS]
`textobj` is as returned by parse_annotated_text(). `tag_text_func`, if supplied, is a function of one argument to further
wrap the text after it has been processed and CSS-tagged appropriately, directly before insertion. `tag_gloss_func` is a
similar function for the gloss.
]=]
local function format_annotated_text(textobj, tag_text_func, tag_gloss_func)
if not textobj then
return nil
end
local text, link = textobj.text, textobj.link
local subst, tr, ts, f, gloss = textobj.subst, textobj.tr, textobj.ts, textobj.f, textobj.gloss
-- Retrieve the display text, either specified by the user (.alt) or by parse_annotated_text() (.display) when a
-- Wikipedia link is given.
local alt = textobj.alt or textobj.display
if alt then
if link:find("%[%[") or link:find("%]%]") then
local errmsg = ("Can't currently handle embedded links in '%s', with <alt:...> text '%s'"):format(link, alt)
error(escape_wikicode(errmsg))
end
text = ("[[%s|%s]]"):format(link, alt)
end
-- See above for `noscript`, meaning HTML was found in the text value, probably generated using {{lang|...}}.
-- {{lang}} already script-tags the text and processes embedded language links, so we don't want to do it again (in
-- fact, the code below within the if-clause is similar to what {{lang}} does). In such a case, an explicit language
-- won't be available and find_best_script_without_lang() may not be accurate, so we can't do automatic transliteration.
if not textobj.noscript then
local lang = textobj.lang
-- As an optimization, don't do script detection on an argument that contains only ASCII.
local sc = textobj.sc
or lang and lang:findBestScript(text)
or not text:find("^[ -~]$") and find_best_script_without_lang(text)
or nil
-- As an optimization, don't do any of the following if there's no language, script, translit or transcription,
-- as will be the case with simple ASCII values.
if lang or sc or tr or ts then
if not lang then
lang = get_lang("und")
end
if tr == "-" then
tr = nil
elseif not tr and sc and not sc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn
-- might return nil
local text_for_tr = text
if subst then
text_for_tr = apply_subst(text_for_tr, subst)
else
text_for_tr = remove_links(text)
end
tr = (lang:transliterate(text_for_tr, sc))
end
if text:find("%[%[") then
-- FIXME: embedded_language_links() replaces % signs with their URL-encoded equivalents,
-- which messes up URL's that may be present (e.g. if chapterurl= is given). IMO this
-- should not happen, and embedded_language_links() should do nothing if no embedded links
-- are present. To work around this, only call embedded_language_links() when there are
-- embedded links present.
text = embedded_language_links({
term = text,
lang = lang,
sc = sc,
})
end
if lang:getCode() ~= "und" or sc:getCode() ~= "Latn" then
text = tag_text(text, lang, sc)
end
if tr then
-- Should we link to the transliteration of languages with lang:link_tr()? Probably not because `text` is not
-- likely to be a term that has an entry.
tr = tag_translit(tr, lang, "usex")
end
if ts then
ts = tag_transcription(ts, lang, "usex")
end
end
end
text = unitalicize_brackets(text)
if tag_text_func then
text = tag_text_func(text)
end
local parts = {}
if textobj.q then
insert(parts, format_qualifier(textobj.q) .. " ")
end
insert(parts, text)
if tr or ts or f or gloss then
insert(parts, SPACE_LBRAC)
local subparts = {}
if tr or ts then
local tr_ts
if ts then
ts = "/" .. ts .. "/"
end
if tr and ts then
tr_ts = tr .. " " .. ts
else
tr_ts = tr or ts
end
insert(subparts, tr_ts)
end
if f then
for _, ff in ipairs(f) do
local sc = ff.sc
local lang
if not sc and ff.quals then
local qual = ff.quals[1]
if type(qual) == "string" then
-- do nothing; we'll do script detection farther down
elseif qual:hasType("script") then
sc = qual
else -- language
sc = qual:findBestScript(ff.val)
lang = qual
end
end
if not lang then
lang = get_lang("und")
end
sc = sc or find_best_script_without_lang(ff.val)
local val = embedded_language_links({
term = ff.val,
lang = lang,
sc = sc,
})
if lang:getCode() ~= "und" or sc:getCode() ~= "Latn" then
val = tag_text(val, lang, sc)
end
local qual_prefix
if ff.quals then
for i, qual in ipairs(ff.quals) do
if type(qual) ~= "string" and (qual:hasType("script") or qual:hasType("language")) then
ff.quals[i] = qual:getCanonicalName()
end
end
qual_prefix = concat(ff.quals, "/") .. ": "
else
qual_prefix = ""
end
insert(subparts, qual_prefix .. val)
end
end
if gloss then
gloss = '<span class="e-translation">' .. gloss .. "</span>"
gloss = unitalicize_brackets(gloss)
if tag_gloss_func then
gloss = tag_gloss_func(gloss)
end
insert(subparts, gloss)
end
insert(parts, concat(subparts, ", "))
insert(parts, RBRAC)
end
if textobj.qq then
insert(parts, " " .. format_qualifier(textobj.qq))
end
return concat(parts)
end
--[=[
Format a multivalued text property that may be in a foreign language or script, along with annotations. This is the
multivalued analog to format_annotated_text(), and formats each individual entity using format_annotated_text(),
joining the results with `delimiter`, which defaults to ", ". It `delimiter` is "and" or "or", join the results using
serial_comma_join() with the specified conjunction.
`textobjs` is as returned by parse_multivalued_annotated_text(). `tag_text_func` and `tag_gloss_func` are as in
format_annotated_text().
]=]
local function format_multivalued_annotated_text(textobjs, delimiter, tag_text_func, tag_gloss_func)
if not textobjs then
return nil
end
if #textobjs == 1 then
return format_annotated_text(textobjs[1], tag_text_func, tag_gloss_func)
end
local parts = {}
for _, textobj in ipairs(textobjs) do
insert(parts, format_annotated_text(textobj, tag_text_func, tag_gloss_func))
end
-- Change delimiter to semicolon if the items themselves contain commas (e.g., in ", Jr.")
local use_semicolon = false
for _, obj in ipairs(textobjs) do
-- Use remove_links to ignore commas in links
if obj.text and remove_links(obj.text):find(", ") then
use_semicolon = true
break
end
end
local n = #parts
if n > 0 and parts[n]:match("^'*et al[.']*$") then
-- Special handling for 'et al.'
parts[n] = "''et al.''"
if n == 2 then
-- author et al.
return concat(parts, " ")
else
-- author 1, author 2, et al.
return concat(parts, (use_semicolon and "; ") or ", ")
end
if delimiter == "and" or delimiter == "or" then
delimiter = ", "
end
return concat(parts, delimiter)
end
if delimiter == "and" or delimiter == "or" then
return serial_comma_join(parts, {conj = delimiter})
end
return concat(parts, delimiter or ((use_semicolon and "; ") or ", "))
end
-- Fancy version of ine() (if-not-empty). Converts empty string to nil, but also strips leading/trailing space.
local function ine(arg)
if not arg then
return nil
elseif type(arg) ~= "string" then
return arg
end
arg = mw.text.trim(arg)
if arg == "" then
return nil
end
return arg
end
local abbrs = {
["a."] = {anchor = "a.", full = "ante"},
["c."] = {anchor = "c.", full = "circa"},
["p."] = {anchor = "p.", full = "post"},
}
-- Process prefixes 'a.' (ante), 'c.' (circa) and 'p.' (post) at the beginning of an arbitrary date or year spec.
-- Returns two values, the formatted version of the prefix and the date spec minus the prefix. If no prefix is found,
-- returns an empty string and the full date.
local function process_ante_circa_post(date)
local prefix = usub(date, 1, 2)
local abbr = abbrs[prefix]
local abbr_prefix = ""
if abbr then
abbr_prefix = "''[[Appendix:Glossary#"
.. abbr.anchor
.. '|<abbr title="'
.. abbr.full
.. '">'
.. abbr.anchor
.. "</abbr>]]'' "
-- Remove lowercase letter, period, and space from beginning of date parameter.
date = ugsub(date, "^%l%.%s*", "")
end
return abbr_prefix, date
end
-- Format the arguments that specify the date of the quotation. These include the following:
-- |date=: The date. If |start_date= is given, this is the end date.
-- |year=, |month=: Year and month of quotation date or end of range, if |date= isn't given.
-- |start_date=: The start date, to specify a range.
-- |start_year=, |start_month=: Year and month of start of range, if |start_date= isn't given.
-- |accessdate=: Date a website was accessed; processed if no other date was given.
-- |nodate=: Indicate that no date is present; otherwise a maintenance line will be displayed if there is no date.
--
-- If `parampref` and/or `paramsuf` are given, this modifies all the date arguments accordingly. For example, if
-- `parampref` == "orig" and `paramsuf` is omitted, the date is specified using |origdate= or |origyear=/|origmonth=,
-- and the start of the range is |origstart_date=, etc. Similarly, if `parampref` is omitted and `paramsuf` is
-- "_published", the date is specified using |date_published= or |year_published=/|month_published=, and the start of
-- the range is |start_date_published=, etc.
--
-- `a` and `get_full_paramname` are functions with the same interpretation as the local functions of the same name in
-- source(). These are used to fetch parameters and get their full names. Note that this may cause all arguments to
-- have an index added to them (|date2=, |year2=, |month2=, etc.).
--
-- `alias_map` is as in source() and is used to map canonical arguments to their aliases when aliases were used.
--
-- If `bold_year` is given, displayed years are boldfaced unless boldface is present in the parameter value.
--
-- If `maintenance_line_no_date` is specified, it should be a string that will be returned if no date is found (i.e.
-- neither |date= nor |year=, or their appropriate equivalents per `parampref` and `paramsuf`, are specified, and
-- neither |nodate= is given to indicate that there is no date, or |accessdate= is given).
--
-- Returns two values: the formatted date and a boolean indicating whether to add a maintenance category
-- [[:Category:Requests for date in LANG entries]]. The first return value will be nil if nothing is to be added
-- (in which case the scond return value will always be nil).
local function format_date_args(
a,
get_full_paramname,
alias_map,
parampref,
paramsuf,
bold_year,
maintenance_line_no_date,
year_last
)
local output = {}
parampref = parampref or ""
paramsuf = paramsuf or ""
local function getp(param)
return a(parampref .. param .. paramsuf)
end
local function pname(param)
local fullname = get_full_paramname(parampref .. param .. paramsuf)
return alias_map[fullname] or fullname
end
-- Format `timestamp` (a timestamp referencing a date) according to the spec in `code`. `param` is the base name of
-- the parameter from which the timestamp was fetched, for error messages.
local function format_date_with_code(code, timestamp, param)
local language = mw.getContentLanguage()
local ok, date = pcall(language.formatDate, language, code, timestamp)
if ok then
return date
else
-- All the formats used in format_date_args() are fine, so the timestamp must be at fault.
error(
(
"Timestamp |%s=%s (possibly canonicalized from its original format) could not be parsed; see the "
.. "[[mw:Help:Extension:ParserFunctions##time|documentation for the #time parser function]]"
):format(pname(param), tostring(timestamp))
)
end
end
-- Try to figure out if the given timestamp has the day of the month explicitly given. We use the following
-- algorithm:
-- 1. Format as year-month-day; if the day is not 1, the day was explicitly given, since if only the year/month are
-- given, the day shows up as 1.
-- 2. If the day shows up as 1 and there isn't a 1 or 01 in the timestamp, the day wasn't explicitly given.
-- 3. Otherwise, if there are three separate numbers (e.g. 2022-07-01), or two separate numbers plus a capitalized
-- letter (taken as an English month, e.g. 2022 July 1), the day was explicitly given, otherwise not.
--
-- `param` is the base name of the parameter from which the timestamp was fetched.
local function date_has_day_specified(timestamp, param)
local day = format_date_with_code("j", timestamp, param)
if day ~= "1" then
return true
end
local english_month = timestamp:find("%u")
local canon_timestamp = mw.text.trim((timestamp:gsub("%D+", " ")))
local seen_nums = split(canon_timestamp, " ", true)
local saw_one = false
for _, num in ipairs(seen_nums) do
if num == "1" or num == "01" then
saw_one = true
break
end
end
if not saw_one then
return false
end
return #seen_nums >= 3 or english_month and #seen_nums >= 2
end
-- Format a date with boldfaced year, as e.g. '''2023''' August 3. `explicit_day_given` indicates whether to include
-- the day; if false, the return value will be e.g. '''2023''' August. `date_param` is the base name of the param
-- from which the date was fetched, for error messages.
local function format_bold_date(date, explicit_day_given, date_param)
local day_month_code = explicit_day_given and "j F" or "F"
local month_day_code = explicit_day_given and "F j" or "F"
if year_last then
if bold_year then
-- This formats like "3 August '''2023'''" (or "August '''2023'''" if day not explicitly given).
return format_date_with_code(day_month_code .. " '''Y'''", date, date_param)
else
-- This formats like "3 August 2023" (or "August 2023" if day not explicitly given).
return format_date_with_code(day_month_code .. " Y", date, date_param)
end
else
if bold_year then
-- This formats like "'''2023''' August 3" (or "'''2023''' August" if day not explicitly given).
return format_date_with_code("'''Y''' " .. month_day_code, date, date_param)
else
-- This formats like "2023 August 3" (or "2023 August" if day not explicitly given).
return format_date_with_code("Y " .. month_day_code, date, date_param)
end
end
end
-- The formatDate method of the mw.language object behaves like the {{#time:}} parser function, which doesn't
-- accept the formats "monthday monthname, year" or "year monthname monthday", but outputs garbage when it receives
-- them, behavior inherited from PHP. {{#formatdate:}} magic word is more forgiving. Fix dates so that, for
-- instance, the |date= parameter of {{quote-journal}} (which uses this code) and the |accessdate= parameter (which
-- uses {{#formatdate:}}) accept similar date formats. See:
-- * [[mw:Extension:Scribunto/Lua_reference_manual#mw.language:formatDate]]
-- * [[mw:Help:Extension:ParserFunctions##time]]
-- * [[mw:Help:Magic_words#Formatting]]
-- `date` is the date spec from the user, which is assumed to come from a parameter whose base name ends in "date";
-- `parampref` is the prefix added to "date" to get the parameter name.
local function fix_date(date, param_pref)
if tonumber(date) ~= nil then
error(
("|%s= should contain a full date (year, month, day of month); use |%s= for year"):format(
pname(param_pref .. "date"),
pname(param_pref .. "year")
)
)
elseif date and date:find("%s*%a+,%s*%d+%s*$") then
error(
("|%s= should contain a full date (year, month, day of month); use |%s=, |%s= for month and year"):format(
pname(param_pref .. "date"),
pname(param_pref .. "month"),
pname(param_pref .. "year")
)
)
end
if date then
-- Commas are replaced with spaces to prevent parsing issues
local fixed_date = ugsub(date, ", *", " ")
return (ugsub(ugsub(fixed_date or date, "(%d+ %a+),", "%1"), "^(%d%d%d%d) (%a+ %d%d?)$", "%2 %1"))
end
end
local start_date, date = fix_date(getp("start_date"), "start_"), fix_date(getp("date"), "")
local year = getp("year")
local month = getp("month")
local start_year = getp("start_year")
local start_month = getp("start_month")
if date and year then
error(("Only one of |%s= or |%s= should be specified"):format(pname("date"), pname("year")))
end
if date and month then
error(
("|%s= should only be specified in conjunction with |%s=, not with |%s="):format(
pname("month"),
pname("year"),
pname("date")
)
)
end
if start_date and start_year then
error(("Only one of |%s= or |%s= should be specified"):format(pname("start_date"), pname("start_year")))
end
if start_date and start_month then
error(
("|%s= should only be specified in conjunction with |%s=, not with |%s="):format(
pname("start_month"),
pname("start_year"),
pname("start_date")
)
)
end
if (start_date or start_year) and not (date or year) then
error(
("|%s= or |%s=/|%s= cannot be specified without specifying |%s= or |%s=/|%s="):format(
pname("start_date"),
pname("start_year"),
pname("start_month"),
pname("date"),
pname("year"),
pname("month")
)
)
end
local dash = " – "
local day_explicitly_given = date and date_has_day_specified(date, "date")
local start_day_explicitly_given = start_date and date_has_day_specified(start_date, "start_date")
-- Format a date with boldfaced year, as e.g. '''2023''' August 3 (if `explicit_day_given` specified) or
-- '''2023''' August (if `explicit_day_given` not specified). If no date specified, fall back to formatting based
-- on the year and (optionally) month params given in `yearobj` and `monthobj`, boldfacing the year if not already.
-- `date_param` is the base name of the param from which the date was fetched, for error messages.
local function format_date_or_year_month(date, yearobj, monthobj, explicit_day_given, date_param)
if date then
return format_bold_date(date, explicit_day_given, date_param)
else
-- Boldface a year spec if it's not already boldface.
if bold_year and not yearobj.text:find("'''") then
-- Clone the year object before modifying it because we may use it later to check against the current
-- year (if we're dealing with start_year).
yearobj = shallow_copy(yearobj)
yearobj.text = "'''" .. yearobj.text .. "'''"
if yearobj.alt then
yearobj.alt = "'''" .. yearobj.alt .. "'''"
end
end
if year_last then
return (monthobj and format_annotated_text(monthobj) .. " " or "") .. format_annotated_text(yearobj)
else
return format_annotated_text(yearobj) .. (monthobj and " " .. format_annotated_text(monthobj) or "")
end
end
end
local yearobj = parse_annotated_text(year, pname("year"))
local monthobj = parse_annotated_text(month, pname("month"))
local start_yearobj = parse_annotated_text(start_year, pname("start_year"))
local start_monthobj = parse_annotated_text(start_month, pname("start_month"))
if start_yearobj then
local abbr_prefix
abbr_prefix, start_yearobj.text = process_ante_circa_post(start_yearobj.text)
start_yearobj.link = select(2, process_ante_circa_post(start_yearobj.link))
insert(output, abbr_prefix)
elseif yearobj then
local abbr_prefix
abbr_prefix, yearobj.text = process_ante_circa_post(yearobj.text)
yearobj.link = select(2, process_ante_circa_post(yearobj.link))
insert(output, abbr_prefix)
end
if start_date or start_year then
local cur_year = yearobj and yearobj.text or format_date_with_code("Y", date, "date")
local cur_month = monthobj and monthobj.text or date and format_date_with_code("F", date, "date") or nil
local cur_day = date and day_explicitly_given and format_date_with_code("j", date, "date") or nil
local beg_year = start_yearobj and start_yearobj.text or format_date_with_code("Y", start_date, "start_date")
local beg_month = start_monthobj and start_monthobj.text
or start_date and format_date_with_code("F", start_date, "start_date")
or nil
local beg_day = start_date
and start_day_explicitly_given
and format_date_with_code("j", start_date, "start_date")
or nil
if year_last then
if beg_day then
insert(output, beg_day .. " " .. beg_month)
else
insert(output, beg_month)
end
if beg_year ~= cur_year then
insert(output, " " .. beg_year)
end
else
insert(output, format_date_or_year_month(
start_date,
start_yearobj,
start_monthobj,
start_day_explicitly_given,
"start_date"
))
end
if cur_year ~= beg_year then
-- Different years; insert current date in full.
if beg_month or cur_month then
insert(output, dash)
else
insert(output, "–")
end
insert(output, format_date_or_year_month(date, yearobj, monthobj, day_explicitly_given, "date"))
elseif cur_month and cur_month ~= beg_month then
local month_ins = monthobj and format_annotated_text(monthobj) or cur_month
-- Same year but different months; insert current month and (if available) current day.
if cur_day then
insert(output, dash)
if year_last then
insert(output, cur_day .. " " .. month_ins)
else
insert(output, month_ins .. " " .. cur_day)
end
else
if beg_day then
insert(output, dash)
else
insert(output, "–")
end
insert(output, month_ins)
end
elseif cur_day and cur_day ~= beg_day then
-- Same year and month but different days; insert current day.
insert(output, "–")
insert(output, cur_day)
else
-- Same year, month and day; or same year and month, and day not available; or same year, and month and
-- day not available. Do nothing. FIXME: Should we throw an error?
end
if year_last and beg_year == cur_year then
if cur_month then
insert(output, " " .. cur_year)
else
insert(output, " " .. cur_year)
end
end
elseif date or yearobj then
insert(output, format_date_or_year_month(date, yearobj, monthobj, day_explicitly_given, "date"))
elseif not maintenance_line_no_date then
-- Not main quote date. Return nil, caller will handle.
return nil, nil
elseif not getp("nodate") then
local accessdate = getp("accessdate")
if accessdate then
local explicit_day_given = date_has_day_specified(accessdate, "accessdate")
insert(output, format_bold_date(accessdate, explicit_day_given, "accessdate") .. " (last accessed)")
else
if mw.title.getCurrentTitle().namespace ~= 10 then
return maintenance_line(maintenance_line_no_date), true
end
return nil, nil
end
end
return ine(concat(output)), nil
end
local function tag_with_cite(txt)
return "<cite>" .. txt .. "</cite>"
end
-- Display the source line of the quote, above the actual quote text. This contains the majority of the logic of this
-- module (formerly contained in {{quote-meta/source}}).
function export.source(args, alias_map, format_as_cite, other_controls)
local tracking_categories = {}
local argslang = args[1] or args.lang
if not argslang then
-- For the moment, only trigger an error on mainspace pages and
-- other pages that are not user pages or pages containing discussions.
-- These are the same pages that appear in the appropriate tracking
-- categories. User and discussion pages have not generally been
-- fixed up to include a language code and so it's more helpful
-- to use a maintenance line than signal an error.
local current_title = mw.title.getCurrentTitle()
if not (current_title.namespace == 10 or page_should_be_ignored(current_title.fullText)) then
require(languages_error_module)(nil, 1)
end
end
-- Given a canonical param, convert it to the original parameter specified by the user (which may have been an
-- alias).
local function alias(param)
return alias_map[param] or param
end
local output, sep = {}
local overrides = other_controls and other_controls.overrides or {}
-- Add text to the output. The text goes into a list, and we concatenate all the list components together at the
-- end. To make it easier to handle comma-separated items, we keep track (in `sep`) of the separator (if any) that
-- needs to be inserted before the next item added. For example, if we're in the "newversion" code (ind ~= ""), and
-- there's no title and no URL, then the first time we add anything after the title, we don't want to add a
-- separating comma because the preceding text will say "republished " or "republished as " or "translated as " or
-- similar. In all- other cases, we do want to add a separating comma. The bare add() function reset the separator
-- to be nothing, while the add_with_sep() function resets the separator to be the value of `next_sep` (defaulting
-- to ", "), so the next time around we do add a comma to separate `text` from the preceding piece of text.
local function add(text)
if sep then
insert(output, sep)
end
insert(output, text)
sep = nil
end
local function add_with_sep(text, next_sep)
add(text)
sep = next_sep or ", "
end
-- FIXME: This is all very over-engineered, which makes it slow and memory-inefficient.
-- Return a function that generates the actual parameter name associated with a base param (e.g. "author", "last").
-- The actual parameter name may have an index added (an empty string for the first set of params, e.g. author=,
-- last=, or a numeric index for further sets of params, e.g. author2=, last2=, etc.).
local function make_get_full_paramname(ind)
return function(param)
return param .. ind
end
end
-- Function to fetch the actual parameter name associated with a base param (see make_get_full_paramname() above).
-- Assigned at various times below by calling make_get_full_paramname(). We do it this way so that we can have
-- wrapper functions that access params and define them only once.
local get_full_paramname
-- Return two values: the value of a parameter given the base param name (which may have a numeric index added),
-- and the parameter name from which the value was fetched (which may be an alias, i.e. you can't necessarily fetch
-- the parameter value from args[] given this name). The base parameter can be a list of such base params, which
-- are checked in turn, or nil, in which case nil is returned.
local function a_with_name(param)
if not param then
return nil
elseif type(param) ~= "table" then
local fullname = get_full_paramname(param)
return args[fullname], alias(fullname)
end
for _, par in ipairs(param) do
local val, fullname = a_with_name(par)
if val then
return val, alias(fullname)
end
end
return nil
end
-- Fetch the value of a parameter given the base param name (which may have a numeric index added). The base
-- parameter can be a list of such base params, which are checked in turn, or nil, in which case nil is returned.
local function a(param)
return (a_with_name(param))
end
-- Identical to a_with_name(param) except that it verifies that no space is present. Should be used for URL's.
local function aurl_with_name(param)
local value, fullname = a_with_name(param)
if value and value:find(" ") and not value:find("%[") then
error(("URL not allowed to contain a space, but saw |%s=%s"):format(fullname, value))
end
return value, fullname
end
-- Identical to a(param) except that it verifies that no space is present. Should be used for URL's.
local function aurl(param)
return (aurl_with_name(param))
end
-- Convenience function to fetch a parameter that may be in a foreign language or text (and may consequently have
-- a language prefix and/or inline modifiers), parse the annotations and convert the result into a formatted string.
-- This is the same as parse_and_format_annotated_text() below but also returns the full param name as the second
-- return value.
local function parse_and_format_annotated_text_with_name(param, tag_text_func, tag_gloss_func)
local val, fullname = a_with_name(param)
local obj = parse_annotated_text(val, fullname)
return format_annotated_text(obj, tag_text_func, tag_gloss_func), fullname
end
-- Convenience function to fetch a parameter that may be in a foreign language or text (and may consequently have
-- a language prefix and/or inline modifiers), parse the modifiers and convert the result into a formatted string.
-- This is a wrapper around parse_annotated_text() and format_annotated_text(). `param` is the base parameter name (see
-- a_with_name()), `tag_text_func` is an optional function to tag the parameter text after all other processing (e.g.
-- wrap in <cite>...</cite> tags), and `tag_gloss_func` is a similar function for the parameter translation/gloss.
local function parse_and_format_annotated_text(param, tag_text_func, tag_gloss_func)
return (parse_and_format_annotated_text_with_name(param, tag_text_func, tag_gloss_func))
end
-- Convenience function to fetch a multivalued parameter that may be in a foreign language or text (and may
-- consequently have a language prefix and/or inline modifiers), parse the modifiers and convert the result into a
-- formatted string. This is the multivalued analog to parse_and_format_annotated_text_with_name() and returns two
-- values, the formatted string and the full name of the parameter fetched. `delimiter` is as in
-- format_multivalued_annotated_text().
local function parse_and_format_multivalued_annotated_text_with_name(param, delimiter, tag_text_func, tag_gloss_func)
local val, fullname = a_with_name(param)
local objs = parse_multivalued_annotated_text(val, fullname)
local num_objs = objs and #objs or 0
return format_multivalued_annotated_text(objs, delimiter, tag_text_func, tag_gloss_func), fullname, num_objs
end
-- Convenience function to fetch a multivalued parameter that may be in a foreign language or text (and may
-- consequently have a language prefix and/or inline modifiers), parse the modifiers and convert the result into a
-- formatted string. This is the multivalued analog to parse_and_format_annotated_text(). `delimiter` is as in
-- format_multivalued_annotated_text().
local function parse_and_format_multivalued_annotated_text(param, delimiter, tag_text_func, tag_gloss_func)
return (parse_and_format_multivalued_annotated_text_with_name(param, delimiter, tag_text_func, tag_gloss_func))
end
-- This determines whether to display "Mary Bloggs, transl." (if there's no author preceding) or "translated by
-- Mary Bloggs" (if there's an author preceding).
local author_outputted = false
-- When formatting as a citation, the priority is to display a name and a date before the book/chapter title
-- this tracks whether or not the author/date has been displayed
local date_outputted, formatted_date, formatted_origdate = false
local function add_date(no_paren)
if not date_outputted then
if no_paren then
sep = ", "
else
sep = " "
end
if formatted_date then
if no_paren then
add(formatted_date)
else
add("(" .. formatted_date .. ")")
end
end
if formatted_origdate then
add(SPACE_LBRAC .. formatted_origdate .. RBRAC)
end
sep = ", "
date_outputted = true
end
end
local function is_anonymous(val)
return val:match("^[Aa]nonymous$") or val:match("^[Aa]non%.?$")
end
-- Add a formatted author (whose values may be specified using `author` or, for compatibility purposes, split
-- among various parameters):
-- * `author` is the value of the author param (e.g. "author", "author2" or "2ndauthor"), and `author_fullname` is
-- the full parameter name holding that value;
-- * `trans_author` is the optional value of the param holding the gloss/translation of the author, and
-- `trans_author_fullname` is the full parameter name holding that value (or nil for no such parameter);
-- * `authorlink` is the value of the authorlink param, which holds the Wikipedia link of the author(s) in `author`,
-- and `authorlink_fullname` is the full parameter name holding that value;
-- * `trans_authorlink` is the optional value of the param holding the Wikipedia link of the gloss/translation of
-- the author, and `trans_authorlink_fullname` is the full parameter name holding that value (or nil for no such
-- parameter);
-- * `first` is the value of the parameter holding the first name of the author, and `first_fullname` is the full
-- parameter name holding that value;
-- * `trans_first` is the value of the corresponding parameter holding the gloss/translation of the first name
-- (e.g. "trans-first"), and `trans_first_fullname` is the full parameter name holding that value (or nil for
-- no such parameter);
-- * `last` is the value of the parameter holding the last name of the author, and `last_fullname` is the full
-- parameter name holding that value;
-- * `trans_last` is the value of the corresponding parameter holding the gloss/translation of the last name
-- (e.g. "trans-last"), and `trans_last_fullname` is the full parameter name holding that value (or nil for
-- no such parameter).
-- * `last_first` if set, when parameters `first` and `last` are used, display the author name as "last, first"
local function add_author(
author,
author_fullname,
trans_author,
trans_author_fullname,
authorlink,
authorlink_fullname,
trans_authorlink,
trans_authorlink_fullname,
first,
first_fullname,
trans_first,
trans_first_fullname,
last,
last_fullname,
trans_last,
trans_last_fullname,
last_first
)
local function make_author_with_url(txt, txtparam, authorlink, authorlink_param)
if authorlink then
if authorlink:find("%[%[") then
error(("Can't specify links in |%s=%s"):format(authorlink_param, authorlink))
end
if txt:find("%[%[") then
error(("Can't specify links in %s=%s"):format(txtparam, txt))
end
return "[[w:" .. authorlink .. "|" .. txt .. "]]"
else
return txt
end
end
local num_authorobjs
if author then
local authorobjs =
parse_multivalued_annotated_text(author, author_fullname, trans_author, trans_author_fullname)
num_authorobjs = #authorobjs
if num_authorobjs == 1 then
if is_anonymous(authorobjs[1].text) then
authorobjs[1].text = "anonymous author"
authorobjs[1].link = "anonymous author"
end
if authorlink then
authorobjs[1].text = make_author_with_url(
authorobjs[1].text,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
authorobjs[1].link = make_author_with_url(
authorobjs[1].link,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
end
if authorobjs[1].gloss and trans_authorlink then
authorobjs[1].gloss = make_author_with_url(
authorobjs[1].gloss,
("<t:...> in |%s"):format(author_fullname),
trans_authorlink,
"|" .. trans_author_fullname
)
end
add(format_multivalued_annotated_text(authorobjs))
elseif trans_authorlink then
error(
(
"Can't specify |%s= along with multiple semicolon-separated entities in |%s=; use the "
.. "<t:...> inline modifier attached to the individual entities and put the link directly "
.. "in the value of the inline modifier"
):format(trans_authorlink_fullname, author_fullname)
)
else
-- Allow an authorlink with multiple authors, e.g. for use with |author=Max Mills; Harvey Mills
-- with |authorlink=Max and Harvey. For this we have to generate the entire text and link it
-- all.
local formatted_text = format_multivalued_annotated_text(authorobjs)
if authorlink then
formatted_text = make_author_with_url(
formatted_text,
"|" .. author_fullname,
authorlink,
"|" .. authorlink_fullname
)
end
add(formatted_text)
end
else
num_authorobjs = 1
-- Author separated into first name + last name. We don't currently support non-Latin-script
-- authors separated this way and probably never will.
if first then
if last_first then
author = last .. ", " .. first
else
author = first .. " " .. last
end
else
author = last
end
if authorlink then
local authorparam = first and ("|%s |%s"):format(first_fullname, last_fullname) or "|" .. last_fullname
author = make_author_with_url(author, authorparam, authorlink, authorlink_fullname)
end
local trans_author
if trans_last then
if trans_first then
trans_author = trans_first .. " " .. trans_last
else
trans_author = trans_last
end
if trans_authorlink then
local trans_authorparam = trans_first
and ("|%s |%s"):format(trans_first_fullname, trans_last_fullname)
or "|" .. trans_last_fullname
trans_author = make_author_with_url(
trans_author,
trans_authorparam,
trans_authorlink,
trans_authorlink_fullname
)
end
end
add(author)
if trans_author then
add(SPACE_LBRAC)
add(trans_author)
add(RBRAC)
end
end
author_outputted = true
return num_authorobjs
end
local function add_authorlike(
param,
prefix_with_preceding_authors,
suffix_without_preceding_authors,
suffix_if_multiple,
anonymous_suffix
)
local delimiter = author_outputted and "and" or ", "
local entities, _, num_entities = parse_and_format_multivalued_annotated_text_with_name(param, delimiter)
if not entities then
return
end
if is_anonymous(entities) then
-- If tlr=anonymous or similar given, display as "anonymous translator" or similar. If a specific
-- anonymous suffix not given, try to derive the anonymous suffix from the non-preceding-author suffix.
if not anonymous_suffix then
local cleaned_suffix = suffix_without_preceding_authors
:gsub(" ", " ")
:gsub(" ", " ")
:gsub(" ", " ")
:gsub("[", "[")
:gsub("]", "]")
cleaned_suffix = mw.text.trim(cleaned_suffix)
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^, (.*)$")
end
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^%((.*)%)$")
end
if not anonymous_suffix then
anonymous_suffix = " " .. cleaned_suffix:match("^%[(.*)%]$")
end
if not anonymous_suffix then
anonymous_suffix = suffix_without_preceding_authors
end
end
add_with_sep("anonymous" .. anonymous_suffix)
elseif prefix_with_preceding_authors and (author_outputted or not suffix_without_preceding_authors) then
add_with_sep(prefix_with_preceding_authors .. entities)
elseif suffix_if_multiple and num_entities > 1 then
add_with_sep(entities .. suffix_if_multiple)
else
add_with_sep(entities .. suffix_without_preceding_authors)
end
author_outputted = true
end
local function add_authorlabel()
local default_authorlabel = a("default-authorlabel")
if default_authorlabel and yesno(a("authorlabel"), true) then
sep = nil
add_with_sep(" " .. default_authorlabel)
end
end
local function has_new_title_or_author()
return args["2ndauthor"]
or args["2ndlast"]
or args.chapter2
or args.title2
or args.tlr2
or args.mainauthor2
or args.editor2
or args.editors2
or args.compiler2
or args.compilers2
or args.director2
or args.directors2
end
local function has_newversion()
return args.newversion or args.location2 or has_new_title_or_author()
end
-- Handle chapter=, section=, etc. `param` is the base name of the parameter in question, e.g. "chapter" or
-- "section". If numeric (either Arabic or Roman), add `numeric_prefix`; otherwise, parse as textual (allowing for
-- language prefixes, inline modifiers, etc.), prefix with `textual_prefix` (if given) and suffix with
-- `textual_suffix` (if given). Also checks for and handles the following (assuming param == "chapter"):
-- * chapterurl=: URL of the chapter.
-- * trans-chapter=: Chapter translation (can be given using an inline modifier <t:...>).
-- * chapter_number=: Chapter number, when chapter= is also given (otherwise put the chapter number in chapter=).
-- * chapter_plain=: Plain version of the chapter number; the "chapter " prefix isn't added.
-- * chapter_series=: Series that the chapter is within (used e.g. for journal articles part of a series).
-- * chapter_seriesvolume=: Volume of the series (compare seriesvolume=).
--
-- Returns nil if no value specified for the main parameter, otherwise the formatted value.
local function format_chapterlike(param, numeric_prefix, textual_prefix, textual_suffix)
local chap, chap_fullname = a_with_name(param)
local chap_num, chap_num_fullname = a_with_name(param .. "_number")
local chap_plain, chap_plain_fullname = parse_and_format_annotated_text_with_name(param .. "_plain")
if chap_num and chap_plain then
error(("Specify only one of |%s= or %s="):format(chap_num_fullname, chap_plain_fullname))
end
local chap_series, chap_series_fullname =
parse_and_format_annotated_text_with_name(param .. "_series", tag_with_cite, tag_with_cite)
local chap_seriesvolume, chap_seriesvolume_fullname =
parse_and_format_annotated_text_with_name(param .. "_seriesvolume")
if chap_series then
chap_series = ", " .. chap_series
end
if chap_seriesvolume then
if not chap_series then
error(("Cannot specify |%s= without %s="):format(chap_series_fullname, chap_seriesvolume_fullname))
end
chap_series = chap_series .. " (" .. chap_seriesvolume .. ")"
end
local chapterurl
local function make_chapter_with_url(chap)
if chapterurl then
return "[" .. chapterurl .. " " .. chap .. "]"
else
return chap
end
end
if not chap then
if chap_num then
error(
("Cannot specify |%s= without |%s=; put the numeric value in |%s= directly"):format(
chap_num_fullname,
chap_fullname,
chap_fullname
)
)
end
if chap_plain then
chapterurl = aurl(param .. "url")
return make_chapter_with_url(chap_plain .. (chap_series or ""))
end
return nil
end
local cleaned_chap = chap:gsub("<sup>[^<>]*</sup>", ""):gsub("[*+#]", "")
chapterurl = aurl(param .. "url")
local formatted
if numeric_prefix and get_number(cleaned_chap) then
-- Arabic chapter number
formatted = numeric_prefix .. make_chapter_with_url(chap)
elseif
numeric_prefix
and cleaned_chap:match("^[mdclxviMDCLXVI]+$")
and roman_to_arabic(cleaned_chap, true)
and (not overrides[param] or not overrides[param].noroman)
then
-- Roman chapter number
formatted = numeric_prefix .. make_chapter_with_url(upper(chap))
else
-- strip leading ! (used to force text-mode for titles that look like Roman numerals like "mil" and "dill")
if chap:find("^!") then
chap = chap:gsub("^!", "")
end
-- Must be a chapter name
local chapterobj = parse_annotated_text(chap, chap_fullname, a("trans-" .. param))
chapterobj.text = make_chapter_with_url(chapterobj.text)
chapterobj.link = make_chapter_with_url(chapterobj.link)
formatted = (textual_prefix or "") .. format_annotated_text(chapterobj) .. (textual_suffix or "")
end
if chap_num or chap_plain then
-- NOTE: Up above we throw an error if both chap_num and chap_plain are specified.
formatted = formatted .. " (" .. (chap_plain or numeric_prefix .. chap_num) .. ")"
end
if chap_series then
formatted = formatted .. chap_series
end
return formatted
end
-- This handles everything after displaying the author, starting with the chapter and ending with page, column,
-- line and then other=. It is currently called twice: Once to handle the main portion of the citation, and once to
-- handle a "newversion" citation. `ind` is either "" for the main portion or a number (currently only 2) for a
-- "newversion" citation. In a few places we conditionalize on `ind` to take actions depending on its value.
local function postauthor(ind, num_authors, format_as_cite)
get_full_paramname = make_get_full_paramname(ind)
if author_outputted then
add_authorlabel()
end
local coauthors = parse_and_format_multivalued_annotated_text("coauthors", "and")
if coauthors then
local with_prefix = ""
if author_outputted then
with_prefix = "with "
if num_authors == 1 then
sep = " "
end
end
add_with_sep(with_prefix .. coauthors)
author_outputted = true
end
add_authorlike("quotee", "quoting ", ", quotee", ", quotees")
if format_as_cite and author_outputted and not date_outputted then
add_date()
sep = ", "
end
add_authorlike("chapter_tlr", "translated by ", ", transl.", nil, " translator")
local function add_sg_and_pl_authorlike(noun, verbed)
local sgparam = noun
local plparam = noun .. "s"
local sgval, sgval_fullname = a_with_name(sgparam)
local plval, plval_fullname = a_with_name(plparam)
if sgval and plval then
error(("Can't specify both |%s= and |%s="):format(sgval_fullname, plval_fullname))
end
if sgval or plval then
local verbed_by = verbed .. " by "
local comma_sgnoun = ", " .. noun
local comma_plnoun = ", " .. noun .. "s"
add_authorlike(sgparam, verbed_by, comma_sgnoun, comma_plnoun)
add_authorlike(plparam, verbed_by, comma_plnoun)
end
end
local formatted_entry = format_chapterlike("entry", nil, "“", "”")
local formatted_chapter = format_chapterlike("chapter", "chapter ", "“", "”")
local function add_entry()
if formatted_entry then
add_with_sep(formatted_entry)
if not a("notitle") then
add("ann an ")
author_outputted = false
else
author_outputted = true
end
formatted_entry = nil
end
end
local function add_chapter()
add_entry()
if formatted_chapter then
add_with_sep(formatted_chapter)
if not a("notitle") then
add("ann an ")
author_outputted = false
end
formatted_chapter = nil
end
end
local function add_actor_role(format_as_cite)
local role = parse_and_format_multivalued_annotated_text("role", "and")
local actor_val, actor_fullname = a_with_name("actor")
local actor_objs = parse_multivalued_annotated_text(actor_val, actor_fullname)
local actor = format_multivalued_annotated_text(actor_objs, "and")
if format_as_cite then
if role then
if actor then
add_with_sep(actor .. " as ")
end
sep = nil
add_with_sep(role)
elseif actor then
add_with_sep(actor .. " (" .. (#actor_objs > 1 and "actors" or "actor") .. ")")
end
else
if role then
add_with_sep("spoken by " .. role)
if actor then
sep = nil
add_with_sep(" (" .. actor .. ")")
end
elseif actor then
add_with_sep(actor .. " (" .. (#actor_objs > 1 and "actors" or "actor") .. ")")
end
end
end
if format_as_cite then
if date_outputted then
add_chapter()
end
local output_len = #output
local mainauthor = parse_and_format_multivalued_annotated_text("mainauthor")
if mainauthor then
add_with_sep(mainauthor)
end
-- quote-* templates display "jobbed by name" after the author, controlled by the author_outputted flag
author_outputted = false
add_authorlike("tlr", "translated by ", ", transl.", nil, " translator")
author_outputted = false
add_sg_and_pl_authorlike("editor", "edited")
add_sg_and_pl_authorlike("compiler", "compiled")
add_sg_and_pl_authorlike("director", "directed")
add_authorlike("lyricist", nil, " (lyrics)", nil, " lyricist")
add_authorlike("lyrics-translator", nil, " (translation)", nil, " lyrics translator")
add_authorlike("composer", nil, " (music)", nil, " composer")
add_actor_role("format_as_cite")
-- if the output length has changed, a credit name has been printed
-- and we can print the date
if output_len ~= #output then
author_outputted = true
add_date()
end
add_chapter()
else
add_chapter()
local mainauthor = parse_and_format_multivalued_annotated_text("mainauthor")
if mainauthor then
add_with_sep(mainauthor)
author_outputted = true
end
add_authorlike("tlr", "translated by ", ", transl.", nil, " translator")
add_sg_and_pl_authorlike("editor", "edited")
add_sg_and_pl_authorlike("compiler", "compiled")
add_sg_and_pl_authorlike("director", "directed")
add_authorlike("lyricist", nil, " (lyrics)", nil, " lyricist")
add_authorlike("lyrics-translator", nil, " (translation)", nil, " lyrics translator")
add_authorlike("composer", nil, " (music)", nil, " composer")
end
local title, title_fullname = a_with_name("title")
local need_comma = false
if title then
local titleobj = parse_annotated_text(title, title_fullname, a("trans-title"))
add(format_annotated_text(titleobj, tag_with_cite, tag_with_cite))
local series = parse_and_format_annotated_text("series")
if series then
add(" (" .. series)
local seriesvolume = parse_and_format_annotated_text("seriesvolume")
if seriesvolume then
add(SEMICOLON_SPACE .. seriesvolume)
end
add(")")
end
need_comma = true
elseif ind == "" then
if not a("notitle") then
add(maintenance_line("Please provide the book title or journal name"))
need_comma = true
end
end
local archiveurl, archiveurl_fullname = aurl_with_name("archiveurl")
local url, url_fullname = aurl_with_name("url")
local urls, urls_fullname = aurl_with_name("urls")
if url and urls then
error(("Supply only one of |%s= and |%s="):format(url_fullname, urls_fullname))
end
local function verify_title_supplied(url_name)
-- There are too many cases of this to throw an error at this time.
-- if not title then
-- error(("If |%s= is given, |%s= must also be supplied"):format(url_name, title_fullname))
-- end
end
if archiveurl or url then
verify_title_supplied(archiveurl and archiveurl_fullname or url_fullname)
sep = nil
add("‎<sup>[" .. (archiveurl or url) .. "]</sup>")
elseif urls then
verify_title_supplied(urls_fullname)
sep = nil
add("‎<sup>" .. urls .. "</sup>")
end
-- display (in Language) if language is provided and is not English and not overriden by termlang or worklang
if format_as_cite and ind == "" and not (args.termlang or args.worklang) and (args[1] or args.lang) then
local lang = get_first_lang(args[1] or args.lang)
if lang then
local langcode = lang:getCode()
if not (langcode == "und" or langcode == "gd") then
local langs = format_langs(args[1] or args.lang)
if langs then
add(" (ann an " .. langs .. ")")
end
end
end
end
if need_comma then
sep = ", "
end
local edition, edition_fullname = parse_and_format_annotated_text_with_name("edition")
local edition_plain, edition_plain_fullname = parse_and_format_annotated_text_with_name("edition_plain")
if edition and edition_plain then
error(("Supply only one of |%s= and |%s="):format(edition_fullname, edition_plain_fullname))
end
if edition then
add_with_sep(edition .. " edition")
end
if edition_plain then
add_with_sep(edition_plain)
end
-- Display a numeric param such as page=, volume=, column=. For each `paramname`, four params are actually
-- recognized, e.g. for paramname == "page", the params page=, pages=, page_plain= and pageurl= are recognized
-- and checked (or the same with an index, e.g. page2=, pages2=, page_plain2= and pageurl2= respectively if
-- ind == "2"). Only one of the first three can be specified; an error results if more than one are given.
-- If none are given, the return value is nil; otherwise it is a string. The numeric spec is taken directly
-- from e.g. page_plain= if given; otherwise if e.g. pages= is given, or if page= is given and looks like a
-- combination of numbers (i.e. it has a hyphen or dash in it, a comma, or the word " and "), it is prefixed
-- by `singular_desc` + "s" (e.g. "pages "), otherwise it is prefixed by just `singular_desc` (e.g. "page ").
-- (As a special case, if either e.g. page=unnumbered or pages=unnumbered is given, the numeric spec is
-- "unnumbered page".) The resulting spec is returned directly unless e.g. pageurl= is given, in which case
-- it is linked to the specified URL. Note that any of the specs can be foreign text, e.g. foreign numbers
-- (including with optional inline modifiers), and such text is handled appropriately.
local function format_numeric_param(paramname, singular_desc)
local sgval = a_with_name(paramname)
local sgobj = parse_annotated_text(sgval, paramname)
local plparamname = paramname .. "s"
local plval = a_with_name(plparamname)
local plobj = parse_annotated_text(plval, plparamname)
local plainval, plain_fullname = parse_and_format_annotated_text_with_name(paramname .. "_plain")
local numspec
if not sgval and not plval and not plainval then
return
elseif plainval and (sgval or plval) then
error(("Can't specify " .. plain_fullname .. " with " .. paramname .. " or " .. plparamname))
elseif sgval and plval then
-- if both singular and plural, display "page 1 of 1-10"
numspec = singular_desc .. " " .. sgval .. " of " .. plval
else
-- Merge page= and pages= and treat alike because people often mix them up in both directions.
if plainval then
numspec = plainval
else
local val = sgobj and sgobj.text or plobj.text
if val == "unnumbered" then
numspec = "unnumbered " .. singular_desc
else
local desc
if val:find("^!") then
val = val:gsub("^!", "")
desc = sgval and singular_desc or pluralize(singular_desc)
else
local check_val = val
if check_val:find("%[") then
check_val = remove_links(check_val)
-- convert URL's of the form [URL DISPLAY] to the displayed value
check_val = check_val:gsub("%[[^ %[%]]* ([^%[%]]*)%]", "%1")
end
-- in case of negative page numbers (do they exist?), don't treat as multiple pages
check_val = check_val:gsub("^%-", "")
-- replace HTML entity en-dashes and em-dashes with their literal codes
check_val = check_val:gsub("–", "–")
check_val = check_val:gsub("–", "–")
check_val = check_val:gsub("—", "—")
check_val = check_val:gsub("—", "—")
-- Check for en-dash or em-dash, or two numbers (possibly with stuff after like 12a-15b)
-- separated by a hyphen or by comma a followed by a space (to avoid firing on thousands separators).
if
umatch(check_val, "[–—]")
or check_val:find(" and ")
or check_val:match("%d+[^ ]* *%- *%d+")
or check_val:match("%d+[^ ]* *, +%d+")
then
desc = pluralize(singular_desc)
else
desc = singular_desc
end
end
local obj = sgobj or plobj
obj.text = val
if obj.link:find("^!") then
obj.link = obj.link:gsub("^!", "")
end
val = format_annotated_text(obj)
numspec = desc .. " " .. val
end
end
end
local url = a(paramname .. "url")
if url then
return "[" .. url .. " " .. numspec .. "]"
else
return numspec
end
end
local volume = format_numeric_param("volume", a("volume_prefix") or "volume")
if volume then
add_with_sep(volume)
end
local issue = format_numeric_param("issue", a("issue_prefix") or "number")
if issue then
add_with_sep(issue)
end
-- number= is an alias for issue= (except in {{quote-av}}, where it is the episode number)
local number = format_numeric_param("number", a("number_prefix") or "number")
if number then
add_with_sep(number)
end
local annotations = {}
local genre = a("genre")
if genre then
insert(annotations, genre)
end
local format = a("format")
if format then
insert(annotations, format)
end
local medium = a("medium")
if medium then
insert(annotations, medium)
end
-- Now handle the display of language annotations like "(in French)" or
-- "(quotation in Nauruan; overall work in German)".
local quotelang = args[1] or args.lang
if not quotelang then
if ind == "" then
-- This can only happen for certain non-mainspace pages, e.g. Talk pages; otherwise an error is thrown
-- above.
insert(annotations, maintenance_line("Please specify the language of the quote using |1="))
else
-- do nothing in newversion= portion
end
elseif ind == "" then
local worklang = a("worklang")
local termlang = a("termlang")
worklang = worklang or quotelang
termlang = termlang or quotelang
if worklang == quotelang then
if worklang == termlang then
-- do nothing
else
insert(annotations, "ann an " .. format_langs(quotelang))
end
else
if quotelang ~= termlang then
insert(annotations, "quotation in " .. format_langs(quotelang))
end
insert(annotations, "overall work in " .. format_langs(worklang))
end
else
local lang2 = a("lang2")
if lang2 then
insert(annotations, "ann an " .. format_langs(lang2))
end
end
if #annotations > 0 then
sep = nil
add_with_sep(" (" .. concat(annotations, SEMICOLON_SPACE) .. ")")
end
local artist = parse_and_format_multivalued_annotated_text("artist", "and")
if artist then
add_with_sep("performed by " .. artist)
end
local feat = parse_and_format_multivalued_annotated_text("feat", "and")
if feat then
sep = " "
add_with_sep("ft. " .. feat)
end
if not format_as_cite then
add_actor_role()
end
local others = parse_and_format_annotated_text("others")
if others then
add_with_sep(others)
end
local quoted_in = parse_and_format_annotated_text("quoted_in", tag_with_cite, tag_with_cite)
if quoted_in then
add_with_sep("quoted in " .. quoted_in)
insert(tracking_categories, "Quotations using quoted-in parameter")
end
local location = parse_and_format_multivalued_annotated_text("location", "; ")
local publisher = parse_and_format_multivalued_annotated_text("publisher", "; ")
if publisher then
if location then
add_with_sep(location) -- colon
sep = ": " -- colon
end
add_with_sep(publisher)
elseif location then
add_with_sep(location)
end
if not date_outputted then
add_date("no_paren")
end
local source = parse_and_format_multivalued_annotated_text("source", "and")
if source then
add_with_sep("sourced from " .. source)
end
local original = parse_and_format_annotated_text("original", tag_with_cite, tag_with_cite)
local by = parse_and_format_multivalued_annotated_text("by", "and")
local origtype = a("deriv") or "translation"
if original or by then
add_with_sep(origtype .. " of " .. (original or "original") .. (by and " by " .. by or ""))
end
-- Handle origlang=, origworklang=. How we handle them depends on whether the original title or author are explicitly
-- given.
local origlang = a("origlang")
local origworklang = a("origworklang")
local origlangtext, origworklangtext
if origlang then
origlangtext = "in " .. format_langs(origlang)
end
if origworklang then
origworklangtext = "overall work in " .. format_langs(origworklang)
end
if origlang or origworklang then
if original or by then
local orig_annotations = {}
if origlangtext then
insert(orig_annotations, origlangtext)
end
if origworklangtext then
insert(orig_annotations, origworklangtext)
end
sep = nil
add_with_sep(" (" .. concat(orig_annotations, SEMICOLON_SPACE) .. ")")
else
add_with_sep(origtype .. " of original" .. (origlangtext and " " .. origlangtext or ""))
if origworklangtext then
sep = nil
add_with_sep(" (" .. origworklangtext .. ")")
end
end
end
if ind ~= "" and has_newversion() then
local formatted_new_date = format_date_args(a, get_full_paramname, alias_map, "", "", nil, "Please provide a date or year", true)
if formatted_new_date then
add_with_sep(formatted_new_date)
end
end
-- Fetch date_published=/year_published=/month_published= and format appropriately.
local formatted_date_published = format_date_args(a, get_full_paramname, alias_map, "", "_published", nil, nil, true)
local platform = parse_and_format_multivalued_annotated_text("platform", "and")
if formatted_date_published then
add_with_sep("published " .. formatted_date_published .. (platform and " via " .. platform or ""))
elseif platform then
add_with_sep("via " .. platform)
end
-- From here on out, there should always be a preceding item, so we
-- can dispense with add_with_sep() and always insert the comma.
sep = nil
local function small(txt)
add(", <small>")
add(txt)
add("</small>")
end
-- Add an identifier to a book or article database such as DOI, ISBN, JSTOR, etc. `param_or_params`
-- is a string identifying the base param, or a list of such strings to check in turn. If found, the value
-- of the parameter is processed using `process` (a function of one argument, defaulting to mw.uri.encode()),
-- and then the actual URL to insert is generated by preceding with `pretext`, following with `posttext`,
-- and running the resulting string through small(), which first adds a comma and then the URL in small font.
local function add_identifier(param_or_params, pretext, posttext, process)
local val = a(param_or_params)
if val then
val = (process or mw.uri.encode)(val)
small(pretext .. val .. posttext)
end
end
add_identifier("bibcode", "[https://adsabs.harvard.edu/abs/", " →Bibcode]")
add_identifier("doi", '<span class="neverexpand">[https://doi.org/', " →DOI]</span>")
add_identifier("isbn", "", "", isbn)
add_identifier("issn", "", "", issn)
add_identifier("jstor", "[https://www.jstor.org/stable/", " →JSTOR]")
add_identifier("lccn", "", "", lccn)
add_identifier("oclc", "[https://search.worldcat.org/title/", " →OCLC]")
add_identifier("ol", "[https://openlibrary.org/works/OL", "/ →OL]")
add_identifier("pmid", "[https://www.ncbi.nlm.nih.gov/pubmed/", " →PMID]")
add_identifier("pmcid", "[https://www.ncbi.nlm.nih.gov/pmc/articles/", "/ →PMCID]")
add_identifier("ssrn", "[https://ssrn.com/abstract=", " →SSRN]")
-- add_identifier("urn", "", "", urn)
local id = a("id")
if id then
small(id)
end
archiveurl, archiveurl_fullname = aurl_with_name("archiveurl")
if archiveurl then
add(", archived from ")
local url, url_fullname = aurl_with_name("url")
if not url then
-- attempt to infer original URL from archive URL; this works at
-- least for Wayback Machine (web.archive.org) URL's
url = archiveurl:match("/(https?:.*)$")
if not url then
error(
("When |%s= is specified, |%s= must also be included"):format(archiveurl_fullname, url_fullname)
)
end
end
add("[" .. url .. " the original] on ")
local archivedate, archivedate_fullname = a_with_name("archivedate")
if archivedate then
add(format_date(archivedate))
elseif string.sub(archiveurl, 1, 28) == "https://web.archive.org/web/" then
-- If the archive is from the Wayback Machine, then it already contains the date
-- Get the date and format it
local wayback_date = string.sub(archiveurl, 29, 29 + 7)
wayback_date = string.sub(wayback_date, 1, 4)
.. "-"
.. string.sub(wayback_date, 5, 6)
.. "-"
.. string.sub(wayback_date, 7, 8)
add(format_date(wayback_date))
else
error(
("When |%s= is specified, |%s= must also be included"):format(
archiveurl_fullname,
archivedate_fullname
)
)
end
end
if a("accessdate") then
--Otherwise do not display here, as already used as a fallback for missing date= or year= earlier.
if (a("date") or a("nodate") or a("year")) and not a("archivedate") then
add(", retrieved " .. format_date(a("accessdate")))
end
end
local formatted_section = format_chapterlike("section", "section ")
if formatted_section then
add(", ")
add(formatted_section)
end
-- video game stuff
local system = parse_and_format_annotated_text("system")
if system then
add(", " .. system)
end
local scene = parse_and_format_annotated_text("scene")
if scene then
add(", scene: " .. scene)
end
local level = parse_and_format_annotated_text("level")
if level then
add(", level/area: " .. level)
end
local note = parse_and_format_annotated_text("note")
if note then
add(", " .. note)
end
local note_plain = parse_and_format_annotated_text("note_plain")
if note_plain then
add(" " .. note_plain)
end
-- Wrapper around format_numeric_param that inserts the formatted text with optional preceding text.
local function handle_numeric_param(paramname, singular_desc, pretext)
local numspec = format_numeric_param(paramname, singular_desc)
if numspec then
add((pretext or "") .. numspec)
end
end
handle_numeric_param("page", a("page_prefix") or "page", ", ")
handle_numeric_param("column", a("column_prefix") or "column", ", ")
handle_numeric_param("line", a("line_prefix") or "line", ", ")
-- FIXME: Does this make sense? What is other=?
local other = parse_and_format_annotated_text("other")
if other then
add(", " .. other)
end
end
local function add_authors(args, last_first)
-- Find maximum indexed author or last name.
local maxind = math.max(args.author.maxindex, args.last.maxindex)
-- Include max index of ancillary params so we get an error message about their use without the primary params.
local ancillary_params =
{"trans-author", "authorlink", "trans-authorlink", "first", "trans-first", "trans-last"}
for _, ancillary in ipairs(ancillary_params) do
maxind = math.max(maxind, args[ancillary].maxindex)
end
local num_authors = 0
for i = 1, maxind do
local ind = i == 1 and "" or i
local author, last = args.author[i], args.last[i]
if author or last then
local this_num_authors = add_author(
author,
"author" .. ind,
args["trans-author"][i],
"trans-author" .. ind,
args.authorlink[i],
"authorlink" .. ind,
args["trans-authorlink"][i],
"trans-authorlink" .. ind,
args.first[i],
"first" .. ind,
args["trans-first"][i],
"trans-first" .. ind,
last,
"last" .. ind,
args["trans-last"][i],
"trans-last" .. ind,
last_first
)
num_authors = num_authors + this_num_authors
sep = last_first and "; " or ", "
else
for _, cant_have in ipairs(ancillary_params) do
if args[cant_have][i] then
error(("Can't have |%s%s= without |author%s= or |last%s="):format(cant_have, ind, ind, ind))
end
end
end
end
return num_authors
end
local function add_newversion()
-- If there's a "newversion" section, add the new-version text.
if has_newversion() then
sep = nil
--Test for new version of work.
add(SEMICOLON_SPACE)
if args.newversion then -- newversion= is intended for English text, e.g. "quoted in" or "republished as".
add(args.newversion)
elseif not args.edition2 then
if has_new_title_or_author() then
add("republished as")
else
add("republished")
end
end
add(" ")
return ""
else
return ", "
end
end
------------------- Now we start outputting text ----------------------
local need_comma = false
-- Set this now so a() works just below.
get_full_paramname = make_get_full_paramname("")
if args.brackets then
add("[")
end
bold_year = not format_as_cite
year_last = format_as_cite
formatted_date, need_date = format_date_args(
a,
get_full_paramname,
alias_map,
nil,
nil,
bold_year,
"Can we [[:Category:Requests for date|date]] this quote?",
year_last
)
-- Fetch origdate=/origyear=/origmonth= and format appropriately.
formatted_origdate = format_date_args(a, get_full_paramname, alias_map, "orig", nil, nil, nil, year_last)
local num_authors, need_date
if format_as_cite then
num_authors = add_authors(args, "last_first")
if author_outputted then
sep = " "
end
-- Display all the text that comes after the author, for the main portion.
postauthor("", num_authors, "format_as_cite")
author_outputted = false
sep = add_newversion()
-- Add the newversion author(s).
if args["2ndauthor"] or args["2ndlast"] then
num_authors = add_author(
args["2ndauthor"],
"2ndauthor",
nil,
nil,
args["2ndauthorlink"],
"2ndauthorlink",
nil,
nil,
args["2ndfirst"],
"2ndfirst",
nil,
nil,
args["2ndlast"],
"2ndlast",
nil,
nil,
"last_first"
)
sep = ", "
else
for _, cant_have in ipairs{"2ndauthorlink", "2ndfirst"} do
if args[cant_have] then
error(("Can't have |%s= without |2ndauthor= or |2ndlast="):format(cant_have))
end
end
end
-- Display all the text that comes after the author, for the "newversion" section.
postauthor(2, num_authors, "format_as_cite")
else
if formatted_date then
need_comma = true
add(formatted_date)
end
if formatted_origdate then
need_comma = true
add(SPACE_LBRAC .. formatted_origdate .. RBRAC)
end
if need_comma then
sep = ", "
end
date_outputted = true
num_authors = add_authors(args)
-- Display all the text that comes after the author, for the main portion.
postauthor("", num_authors)
author_outputted = false
sep = add_newversion()
-- Add the newversion author(s).
if args["2ndauthor"] or args["2ndlast"] then
num_authors = add_author(
args["2ndauthor"],
"2ndauthor",
nil,
nil,
args["2ndauthorlink"],
"2ndauthorlink",
nil,
nil,
args["2ndfirst"],
"2ndfirst",
nil,
nil,
args["2ndlast"],
"2ndlast",
nil,
nil
)
sep = ", "
else
for _, cant_have in ipairs{"2ndauthorlink", "2ndfirst"} do
if args[cant_have] then
error(("Can't have |%s= without |2ndauthor= or |2ndlast="):format(cant_have))
end
end
end
-- Display all the text that comes after the author, for the "newversion" section.
postauthor(2, num_authors)
end
if not args.nocolon then
sep = nil
add(":")
elseif args.usenodot and not args.nodot then
add(".")
end
-- Concatenate output portions to form output text.
local output_text = concat(output)
-- Remainder of code handles adding categories. We add one or more of the following categories:
--
-- 1. [[Category:LANG terms with quotations]], based on the first language code in termlang= or 1=. Added to
-- mainspace, Reconstruction: and Appendix: pages as well as Citations: pages if the corresponding mainspace
-- page exists. Not added if nocat= is given. Note that [[Module:usex]] adds the same category using the same
-- logic, but we do it here too because we may not have a quotation to format. (We add in those circumstances
-- because typically when there's no quotation to format, it's because it's formatted manually underneath the
-- citation, or using {{ja-x}}, {{th-x}} or similar.)
-- 2. [[Category:Requests for date in LANG entries]], based on the first language code in 1=. Added to mainspace,
-- Reconstruction:, Appendix: and Citations: pages unless nocat= is given.
-- 3. [[Category:Quotations using nocat parameter]], if nocat= is given. Added to mainspace, Reconstruction:,
-- Appendix: and Citations: pages.
local categories = {}
local termlang = get_first_lang(args.termlang or argslang)
if args.nocat then
if format_as_cite then
insert(tracking_categories, "Citations using nocat parameter")
else
insert(tracking_categories, "Quotations using nocat parameter")
end
else
-- don't create language tracking categories for citations
if not format_as_cite then
local title
if args.pagename then -- for testing, doc pages, etc.
title = mw.title.new(args.pagename)
if not title then
error(("Bad value for `args.pagename`: '%s'"):format(args.pagename))
end
else
title = mw.title.getCurrentTitle()
end
-- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists.
local ok_to_add_cat
if title.nsText ~= "Citations" then
ok_to_add_cat = true
else
local mainspace_title = mw.title.new(title.text)
if mainspace_title and mainspace_title.exists then
ok_to_add_cat = true
end
end
if ok_to_add_cat then
insert(categories, termlang:getFullName() .. " terms with quotations")
end
end
if need_date then
local argslangobj = get_first_lang(argslang)
insert(categories, "Requests for date in " .. argslangobj:getCanonicalName() .. " entries")
end
end
return output_text .. (
not lang and "" or
(#categories > 0 and format_categories(categories, lang, args.sort) or "") .. (
#tracking_categories > 0
and format_categories(
tracking_categories,
lang,
args.sort,
nil,
not page_should_be_ignored(mw.title.getCurrentTitle().fullText)
)
or ""
)
)
end
-- Alias specs for type= and type2=. Each spec is `{canon, aliases, with_newversion}` where `canon` is the canonical
-- parameter (with "2" added if type2= is being handled), `aliases` is a comma-separated string of aliases (with "2"
-- added if type2= is being handled, except for numeric params), and `with_newversion` indicates whether we should
-- process this spec if type2= is being handled.
local type_alias_specs = {
av = {
{"author", "writer,writers", true},
{"chapter", "episode", true},
{"chapterurl", "episodeurl", true},
{"trans-chapter", "trans-episode", true},
{"chapter_tlr", "episode_tlr,episode_tlrs,episode_translator,episode_translators", true},
{"chapter_series", "episode_series", true},
{"chapter_seriesvolume", "episode_seriesvolume", true},
{"chapter_number", "episode_number", true},
{"chapter_plain", "episode_plain", true},
{"volume", "season", true},
{"volumes", "seasons", true},
{"volume_plain", "season_plain", true},
{"volumeurl", "seasonurl", true},
{"platform", "network", true},
},
book = {
{"author", "3"},
{"title", "4"},
{"url", "5"},
{"year", "2"},
{"page", "6"},
{"text", "7"},
{"t", "8"},
},
journal = {
{"year", "2"},
{"author", "3"},
{"chapter", "title,article,4", true},
{"chapterurl", "titleurl,articleurl", true},
{"trans-chapter", "trans-title,trans-article", true},
{"chapter_tlr", "title_tlr,title_tlrs,title_translator,title_translators,article_tlr,article_tlrs,article_translator,article_translators", true},
{"chapter_series", "title_series,article_series", true},
{"chapter_seriesvolume", "title_seriesvolume,article_seriesvolume", true},
{"chapter_number", "title_number,article_number", true},
{"chapter_plain", "title_plain,article_plain", true},
{"title", "journal,magazine,newspaper,work,5", true},
{"trans-title", "trans-journal,trans-magazine,trans-newspaper,trans-work", true},
{"tlr", "journal_tlr,journal_tlrs,journal_translator,journal_translators,magazine_tlr,magazine_tlrs,magazine_translator,magazine_translators,newspaper_tlr,newspaper_tlrs,newspaper_translator,newspaper_translators,work_tlr,work_tlrs,work_translator,work_translators", true},
{"url", "6,journalurl,magazineurl,newspaperurl,workurl", true},
{"page", "7"},
{"source", "newsagency,news_agency", true},
{"text", "8"},
{"t", "9"},
},
}
-- Process interally-handled aliases related to type= or type2=. `args` is a table of arguments; `typ` is the value of
-- type= or type2=; newversion=true if we're dealing with type2=; alias_map is used to keep track of alias mappings
-- seen.
local function process_type_aliases(args, typ, newversion, alias_map)
local ind = newversion and "2" or ""
local deprecated = ine(args.lang)
if not type_alias_specs[typ] then
local possible_values = {}
for possible, _ in pairs(type_alias_specs) do
insert(possible_values, possible)
end
sort(possible_values)
error(
("Unrecognized value '%s' for type%s=; possible values are %s"):format(
typ,
ind,
concat(possible_values, ",")
)
)
end
for _, alias_spec in ipairs(type_alias_specs[typ]) do
local canon, aliases, with_newversion = unpack(alias_spec)
if with_newversion or not newversion then
canon = canon .. ind
aliases = split(aliases, ",", true)
local saw_alias = nil
for _, alias in ipairs(aliases) do
if alias:match("^%d+$") then
alias = tonumber(alias)
if deprecated then
alias = alias - 1
end
else
alias = alias .. ind
end
if args[alias] then
if saw_alias == nil then
saw_alias = alias
else
error(("|%s= and |%s= are aliases; cannot specify a value for both"):format(saw_alias, alias))
end
end
end
if saw_alias and (not newversion or type(saw_alias) == "string") then
if args[canon] then
error(("|%s= is an alias of |%s=; cannot specify a value for both"):format(saw_alias, canon))
end
args[canon] = args[saw_alias]
-- Wipe out the original after copying. This is important in case of a param that has general significance
-- but has been redefined (e.g. {{quote-av}} redefines number= for the episode number, and
-- {{quote-journal}} redefines title= for the chapter= (article). It's also important due to unhandled
-- parameter checking.
args[saw_alias] = nil
alias_map[canon] = saw_alias
end
end
end
end
-- Clone and combine frame's and parent's args while also assigning nil to empty strings. Handle aliases and ignores.
local function clone_args(direct_args, parent_args)
local args = {}
-- Processing parent args must come first so that direct args override parent args. Note that if a direct arg is
-- specified but is blank, it will still override the parent arg (with nil).
for pname, param in pairs(parent_args) do
-- [[Special:WhatLinksHere/Wiktionary:Tracking/quote/param/PARAM]]
track("param/" .. pname)
args[pname] = ine(param)
end
-- Process ignores. The value of `ignore` is a comma-separated list of parameter names to ignore (erase). We need to
-- do this before aliases due to {{quote-song}}, which sets chapter= to the value of title= in the direct params and
-- sets title= to the value of album= using an alias. If we do the ignores after aliases, we get an error during alias
-- processing, saying that title= and its alias album= are both present.
local ignores = ine(direct_args.ignore)
if ignores then
for ignore in gsplit(ignores, "%s*,%s*") do
args[ignore] = nil
end
end
local alias_map = {}
local other_controls = {}
-- noroman
local noroman = ine(direct_args.noroman)
if noroman then
other_controls.overrides = other_controls.overrides or {}
for param in gsplit(noroman, "%s*,%s*") do
other_controls.overrides[param] = (other_controls.overrides[param] or {})
other_controls.overrides[param].noroman = true
end
end
-- Process internally-specified aliases using type= or type2=.
local typ = args.type or direct_args.type
if typ then
process_type_aliases(args, typ, false, alias_map)
end
local typ2 = args.type2 or direct_args.type2
if typ2 then
process_type_aliases(args, typ2, true, alias_map)
end
-- Process externally-specified aliases. The value of `alias` is a list of semicolon-separated specs, each of which
-- is of the form DEST:SOURCE,SOURCE,... where DEST is the canonical name of a parameter and SOURCE refers to an
-- alias. Whitespace is allowed between all delimiters. The order of aliases may be important. For example, for
-- {{quote-journal}}, title= contains the article name and is an alias of underlying chapter=, while journal= or
-- work= contains the journal name and is an alias of underlying title=. As a result, the title -> chapter alias
-- must be specified before the journal/work -> title alias.
--
-- Whenever we copy a value from argument SOURCE to argument DEST, we record an entry for the pair in alias_map, so
-- that when we would display an error message about DEST, we display SOURCE instead.
--
-- Do alias processing (and ignore and error_if processing) before processing direct_args so that e.g. we can set up
-- an alias of title -> chapter and then set title= to something else in the direct args ({{quote-hansard}} does
-- this).
--
-- FIXME: Delete this once we've converted all alias processing to internal.
local aliases = ine(direct_args.alias)
if aliases then
-- Allow and discard a trailing semicolon, to make managing multiple aliases easier.
aliases = ugsub(aliases, "%s*;$", "")
for alias_spec in gsplit(aliases, "%s*;%s*") do
local alias_spec_parts = split(alias_spec, "%s*:%s*")
if #alias_spec_parts ~= 2 then
error(("Alias spec '%s' should have one colon in it"):format(alias_spec))
end
local dest, sources = unpack(alias_spec_parts)
sources = split(sources, "%s*,%s*")
local saw_source = nil
for _, source in ipairs(sources) do
if source:match("^%d+$") then
source = tonumber(source)
end
if args[source] then
if saw_source == nil then
saw_source = source
else
error(("|%s= and |%s= are aliases; cannot specify a value for both"):format(saw_source, source))
end
end
end
if saw_source then
if args[dest] then
error(("|%s= is an alias of |%s=; cannot specify a value for both"):format(saw_source, dest))
end
args[dest] = args[saw_source]
-- Wipe out the original after copying. This important in case of a param that has general significance
-- but has been redefined (e.g. {{quote-av}} redefines number= for the episode number, and
-- {{quote-journal}} redefines title= for the chapter= (article). It's also important due to unhandled
-- parameter checking.
args[saw_source] = nil
alias_map[dest] = saw_source
end
end
end
-- Process error_if. The value of `error_if` is a comma-separated list of parameter names to throw an error if seen
-- in parent_args (they are params we overwrite in the direct args).
local error_ifs = ine(direct_args.error_if)
if error_ifs then
for error_if in gsplit(error_ifs, "%s*,%s*") do
if ine(parent_args[error_if]) then
error(
("Cannot specify a value |%s=%s as it would be overwritten or ignored"):format(
error_if,
ine(parent_args[error_if])
)
)
end
end
end
for pname, param in pairs(direct_args) do
-- ignore control params
if pname ~= "ignore" and pname ~= "alias" and pname ~= "error_if" and pname ~= "noroman" then
args[pname] = ine(param)
end
end
return args, alias_map, other_controls
end
local function get_args(frame_args, parent_args, require_lang)
-- FIXME: We are processing arguments twice, once in clone_args() and then again in [[Module:parameters]]. This is
-- wasteful of memory.
local cloned_args, alias_map, other_controls = clone_args(frame_args, parent_args)
local deprecated = ine(parent_args.lang)
local alias_of_t = {alias_of = "t"}
local boolean = {type = "boolean"}
local language_sublist = {type = "language", sublist = true}
local list_allow_holes = {list = true, allow_holes = true}
local script = {type = "script"}
-- First, the "single" params that don't have FOO2 or FOOn versions.
local params = {
[deprecated and "lang" or 1] = {required = require_lang, type = "language", sublist = true, default = "und"},
["lang2"] = language_sublist,
["newversion"] = true,
["author"] = list_allow_holes,
["2ndauthor"] = true,
["trans-author"] = list_allow_holes,
["authorlink"] = list_allow_holes,
["2ndauthorlink"] = true,
["trans-authorlink"] = list_allow_holes,
["first"] = list_allow_holes,
["2ndfirst"] = true,
["trans-first"] = list_allow_holes,
["last"] = list_allow_holes,
["2ndlast"] = true,
["trans-last"] = list_allow_holes,
["nocat"] = boolean,
["nocolon"] = boolean,
-- quote params
text = true,
passage = {alias_of = "text"},
tr = true,
transliteration = {alias_of = "tr"},
ts = true,
transcription = {alias_of = "ts"},
norm = true,
normalization = {alias_of = "norm"},
sc = script,
normsc = script,
sort = true,
subst = true,
footer = true,
lit = true,
t = true,
translation = alias_of_t,
gloss = alias_of_t,
lb = true,
brackets = boolean,
-- original quote params
origtext = true,
origtr = true,
origts = true,
orignorm = true,
origsc = script,
orignormsc = script,
origsubst = true,
origlb = true,
usenodot = boolean,
nodot = boolean,
inline = boolean,
asquote = boolean,
ascite = boolean,
}
-- Most params have PARAM2 variants for use with `newversion`. FIXME: We should generalize this to
-- allow PARAMN variants for any N.
local function add_with_2(param, value)
params[param] = value
params[param .. "2"] = value
end
local function alias_with_2(alias, canon)
params[alias] = {alias_of = canon}
params[alias .. "2"] = {alias_of = canon .. "2"}
end
for _, param12 in ipairs{
"worklang",
"termlang",
"origlang",
"origworklang"
} do
add_with_2(param12, language_sublist)
end
-- Then the newversion params (which have FOO2 versions).
for _, param12 in ipairs{
-- author-like params; author params themselves are either list params (author=, last=, etc.) or single params
-- (2ndauthor=, 2ndlast=, etc.)
"coauthors",
"quotee",
"tlr",
"editor",
"editors",
"mainauthor",
"compiler",
"compilers",
"director",
"directors",
"lyricist",
"lyrics-translator",
"composer",
"role",
"actor",
"artist",
"feat",
-- author control params
"default-authorlabel",
"authorlabel",
-- title
"title",
"trans-title",
"series",
"seriesvolume",
"notitle",
-- entry
"entry",
"entryurl",
"trans-entry",
-- chapter
"chapter",
"chapterurl",
"chapter_number",
"chapter_plain",
"chapter_series",
"chapter_seriesvolume",
"trans-chapter",
"chapter_tlr",
-- section
"section",
"sectionurl",
"section_number",
"section_plain",
"section_series",
"section_seriesvolume",
"trans-section",
-- other video-game params
"system",
"scene",
"level",
-- URL
"url",
"urls",
"archiveurl",
-- edition
"edition",
"edition_plain",
-- ID params
"bibcode",
"doi",
"isbn",
"issn",
"jstor",
"lccn",
"oclc",
"ol",
"pmid",
"pmcid",
"ssrn",
"urn",
"id",
-- misc date params; most date params handled below
"archivedate",
"accessdate",
"nodate",
-- numeric params handled below
-- other params
"type",
"genre",
"format",
"medium",
"others",
"quoted_in",
"location",
"publisher",
"original",
"by",
"deriv",
"note",
"note_plain",
"other",
"source",
"platform",
} do
add_with_2(param12, true)
end
-- Then the aliases of newversion params (which have FOO2 versions).
for _, param12_aliased in ipairs{
{"role", "roles"},
{"role", "speaker"},
{"tlr", "tlrs"},
{"tlr", "translator"},
{"tlr", "translators"},
{"chapter_tlr", "chapter_tlrs"},
{"chapter_tlr", "chapter_translator"},
{"chapter_tlr", "chapter_translators"},
{"doi", "DOI"},
{"isbn", "ISBN"},
{"issn", "ISSN"},
{"jstor", "JSTOR"},
{"lccn", "LCCN"},
{"oclc", "OCLC"},
{"ol", "OL"},
{"pmid", "PMID"},
{"pmcid", "PMCID"},
{"ssrn", "SSRN"},
{"urn", "URN"},
} do
local canon, alias = unpack(param12_aliased)
alias_with_2(alias, canon)
end
-- Then the date params.
for _, datelike in ipairs{{"", ""}, {"orig", ""}, {"", "_published"}} do
local pref, suf = unpack(datelike)
for _, arg in ipairs{"date", "year", "month", "start_date", "start_year", "start_month"} do
add_with_2(pref .. arg .. suf, true)
end
end
local numeric_param_suffixes = {"", "s", "_plain", "url", "_prefix"}
-- Then the numeric params.
for _, numeric in ipairs{"volume", "issue", "number", "line", "page", "column"} do
for _, suf in ipairs(numeric_param_suffixes) do
add_with_2(numeric .. suf, true)
end
end
-- And the aliases of numeric params.
for _, numeric_aliased in ipairs{{"volume", "vol"}} do
local canon, alias = unpack(numeric_aliased)
for _, suf in ipairs(numeric_param_suffixes) do
alias_with_2(alias .. suf, canon .. suf)
end
end
return process_params(cloned_args, params), alias_map, other_controls
end
local function get_origtext_params(args)
local origtext, origtextlang, origsc, orignormsc
if args.origtext then
-- Wiktionary language codes have at least two lowercase letters followed possibly by lowercase letters and/or
-- hyphens (there are more restrictions but this is close enough). Also check for nonstandard Latin etymology
-- language codes (e.g. VL. or LL.). (There used to be more nonstandard codes but they have all been
-- eliminated.)
origtextlang, origtext = args.origtext:match("^(%l%l[%l-]*):([^ ].*)$")
if not origtextlang then
-- Special hack for Latin variants, which can have nonstandard etym codes, e.g. VL., LL.
origtextlang, origtext = args.origtext:match("^(%uL%.):([^ ].*)$")
end
if not origtextlang then
error("origtext= should begin with a language code prefix")
end
origtextlang = get_lang(origtextlang, nil, "allow etym") or
error("origtext= should begin with a language code prefix")
origsc = args.origsc
orignormsc = args.orignormsc
else
for _, noparam in ipairs{"origtr", "origts", "origsc", "orignorm", "orignormsc", "origsubst", "origlb"} do
if args[noparam] then
error(("Cannot specify %s= without origtext="):format(noparam))
end
end
end
return origtext, origtextlang, origsc, orignormsc
end
local function get_quote(args, is_cite)
local text = args.text
local gloss = args.t
local tr = args.tr
local ts = args.ts
local norm = args.norm
local sc = args.sc
local normsc = args.normsc
-- Fetch original-text parameters.
local origtext, origtextlang, origsc, orignormsc = get_origtext_params(args)
-- If any quote-related args are present, display the actual quote; otherwise, display nothing.
if text or gloss or tr or ts or norm or args.origtext then
-- Pass "und" here rather than cause an error; there will be an error on mainspace, Citations, etc. pages
-- in any case in source() if the language is omitted.
local lang = get_first_lang(args[1] or args.lang)
local termlang = args.termlang and get_first_lang(args.termlang) or lang
local usex_data = {
lang = lang,
termlang = termlang,
usex = text,
sc = sc,
translation = gloss,
normalization = norm,
normsc = normsc,
transliteration = tr,
transcription = ts,
brackets = args.brackets,
subst = args.subst,
lit = args.lit,
footer = args.footer,
qq = parse_and_format_labels(args.lb, lang),
quote = "quote-meta",
orig = origtext,
origlang = origtextlang,
origsc = origsc,
orignorm = args.orignorm,
orignormsc = orignormsc,
origtr = args.origtr,
origts = args.origts,
origsubst = args.origsubst,
origqq = parse_and_format_labels(args.origlb, lang),
noreq = args.noreq,
nocat = is_cite or args.nocat,
}
if args.inline then
-- don't let usex format the footer, otherwise it gets inlined with the rest of the quoted text
usex_data.footer = nil
usex_data.inline = 1
text = format_usex(usex_data)
if text then
text = " “" .. text .. "”"
else
text = ""
end
if args.footer then
text = text .. "<dl><dd>" .. args.footer .. "</dd></dl>"
end
else
text = "<dl><dd>" .. format_usex(usex_data) .. "</dd></dl>"
end
elseif args.footer then
text = "<dl><dd>" .. args.footer .. "</dd></dl>"
end
return text
end
-- External interface, meant to be called from a template. Replaces {{quote-meta}} and meant to be the primary
-- interface for {{quote-*}} templates.
function export.quote_t(frame)
local args, alias_map, other_controls = get_args(frame.args, frame:getParent().args, "require_lang")
if args.ascite then
return export.cite_t(frame)
end
return show_quote(args, alias_map, other_controls)
end
function show_quote(args, alias_map, other_controls)
local parts = {}
insert(parts, '<div class="citation-whole"><span class="cited-source">')
insert(parts, export.source(args, alias_map, nil, other_controls))
insert(parts, "</span>")
insert(parts, get_quote(args))
insert(parts, "</div>")
local retval = concat(parts)
return deprecated and frame:expandTemplate({
title = "check deprecated lang param usage",
args = {retval, lang = args.lang},
}) or retval
end
-- External interface, meant to be called from a template. Replaces {{cite-meta}} and meant to be the primary
-- interface for {{cite-*}} templates.
function export.cite_t(frame)
local parent_args = {}
for k, v in pairs(frame:getParent().args) do
parent_args[k] = v
end
-- use "und" as lang if none provided
if parent_args[1] == nil then
parent_args[1] = "und"
end
local args, alias_map, other_controls = get_args(frame.args, parent_args)
if args.asquote then
return show_quote(args, alias_map, other_controls)
end
return show_cite(args, alias_map, other_controls)
end
function show_cite(args, alias_map, other_controls)
local parts = {}
-- don't nag for translations
if args.text and not args.t then
args.noreq = 1
end
local len_visible = args.text and ulen((args.text:gsub("<[^<>]+>", ""))) or 0
if len_visible == 0 then
if not args.t or args.t == "-" then
args.nocolon = true
end
elseif args.inline == nil then
args.inline = not (
args.block_text
or len_visible > 300
or (args.t and not args.t == "-" and len_visible < 80)
or string.match(args.text, "<br>")
)
end
insert(parts, '<span class="citation-whole"><span class="cited-source">')
insert(parts, export.source(args, alias_map, "format_as_cite", other_controls))
insert(parts, "</span>")
insert(parts, get_quote(args, "is_cite"))
insert(parts, "</span>")
local retval = concat(parts)
return deprecated
and frame:expandTemplate({
title = "check deprecated lang param usage",
args = {retval, lang = args.lang},
})
or retval
end
-- External interface, meant to be called from a template.
function export.call_quote_template(frame)
return export.call_template(frame)
end
-- External interface, for calling a template with overloaded parameters.
function export.overload(frame)
local iargs, overloaded_args = process_params(frame.args, {
[1] = {required = true}
}, true)
local parent_args = frame:getParent().args
for k, v in pairs(parent_args) do
overloaded_args[k] = v
end
return frame:expandTemplate{title = iargs[1], args = overloaded_args}
end
-- External interface, meant to be called from a template.
function export.call_template(frame)
local parameter_sublist = {type = "parameter", sublist = true}
local iargs, other_direct_args = process_params(frame.args, {
["template"] = true,
["textparam"] = parameter_sublist,
["pageparam"] = parameter_sublist,
["propagateparams"] = parameter_sublist,
["allowparams"] = {sublist = true}, -- Doesn't use type = "parameter", because any that end in :list get processed differently.
}, true)
local function fetch_param(source, params)
for _, param in ipairs(params) do
if source[param] then
return source[param]
end
end
return nil
end
local params = {
["text"] = true,
["passage"] = true,
["footer"] = true, -- always propagates, but gets special formatting
}
-- paramaters that can be used during template declaration or included in template calls
-- if the template is called with param=- it will set any declared value to nil
local always_propagate = {
["brackets"] = true,
["usenodot"] = true,
["nodot"] = true,
["ascite"] = true,
["asquote"] = true,
}
for k,v in pairs(always_propagate) do
params[k] = v
end
local textparam = iargs.textparam or {}
for _, param in ipairs(textparam) do
params[param] = true
end
local pageparam = iargs.pageparam or {}
local pageparam1 = pageparam[1]
if pageparam1 ~= nil then
params["page"], params["pages"] = true, true
for _, param in ipairs(pageparam) do
params[param] = true
end
end
local allowparams, allow_all, list = iargs.allowparams, false
if allowparams ~= nil then
for _, allow in ipairs(allowparams) do
local param = allow:match("^(.*):list$")
if param then
if list == nil then
list = {list = true}
end
params[scribunto_parameter_key(param)] = list
elseif allow == "*" then
track("no parameter checking")
allow_all = true
else
params[scribunto_parameter_key(allow)] = true
end
end
else
local parent = frame:getParent()
local template_name = parent:getTitle()
local template_title = new_title(template_name)
local content = template_title:getContent()
local allowed_params, seen = {}, {}
-- Detect all params used by the parent template. param:get_name() takes the
-- parent frame arg table as an argument so that preprocessing will take
-- them into account, since it will matter if the name contains another
-- parameter (e.g. the outer param in "{{{foo{{{bar}}}baz}}}" will change
-- depending on the value for bar=). `seen` memoizes results based on the
-- raw parameter text (which is stored as a string in the parameter object),
-- which avoids unnecessary param:get_name() calls, which are non-trivial.
for param in find_parameters(content) do
local raw = param.raw
if not seen[raw] then
allow = param:get_name(template_args)
params[scribunto_parameter_key(allow)] = true
seen[raw] = true
end
end
end
local propagateparams = iargs.propagateparams or {}
for _, param in ipairs(propagateparams) do
params[param] = true
end
local parent_args = frame:getParent().args
local args = process_params(parent_args, params, allow_all)
parent_args = shallow_copy(parent_args)
if textparam[1] ~= "-" then
other_direct_args.passage = args.text or args.passage or fetch_param(args, textparam)
end
if not (pageparam1 == nil or pageparam1 == "-") then
other_direct_args.page = fetch_param(args, pageparam) or args.page or other_direct_args.page or nil
other_direct_args.pages = args.pages or other_direct_args.pages or nil
if other_direct_args.page == "-" then
other_direct_args.page = nil
end
if other_direct_args.pages == "-" then
other_direct_args.pages = nil
end
end
if args.footer then
other_direct_args.footer = frame:expandTemplate{title = "small", args = {args.footer}}
end
for param, _ in pairs(always_propagate) do
if args[param] == "-" then
other_direct_args[param] = nil
else
other_direct_args[param] = args[param] or other_direct_args[param] or nil
end
end
-- authorlink=- can be used to prevent copying of author= to authorlink= but we don't want to propagate this to
-- the actual {{quote-*}} code.
if other_direct_args.authorlink == "-" then
other_direct_args.authorlink = nil
end
for _, param in ipairs(propagateparams) do
if args[param] then
other_direct_args[param] = args[param]
end
end
return frame:expandTemplate{title = iargs.template or "quote-book", args = other_direct_args}
end
local paramdoc_param_replacements = {
passage = {
param_with_synonym = "<<synonym>>, {{para|text}}, or {{para|passage}}",
param_no_synonym = "{{para|text}} or {{para|passage}}",
text = [=[
* <<params>> – the passage to be quoted.]=],
},
page = {
param_with_synonym = "<<synonym>> or {{para|page}}, or {{para|pages}}",
param_no_synonym = "{{para|page}} or {{para|pages}}",
text = [=[
* <<params>> – '''mandatory in some cases''': the page number(s) quoted from. When quoting a range of pages, note the following:
** Separate the first and last pages of the range with an [[en dash]], like this: {{para|pages|10–11}}.
** You must also use {{para|pageref}} to indicate the page to be linked to (usually the page on which the Wiktionary entry appears).
: This parameter must be specified to have the template link to the online version of the work.]=],
},
page_with_roman_preface = {
param_with_synonym = {"inherit", "page"},
param_no_synonym = {"inherit", "page"},
text = [=[
* <<params>> – '''mandatory in some cases''': the page number(s) quoted from. If quoting from the preface, specify the page number(s) in lowercase Roman numerals. When quoting a range of pages, note the following:
** Separate the first and last page number of the range with an [[en dash]], like this: {{para|pages|10–11}} or {{para|pages|iii–iv}}.
** You must also use {{para|pageref}} to indicate the page to be linked to (usually the page on which the Wiktionary entry appears).
: This parameter must be specified to have the template link to the online version of the work.]=],
},
chapter = {
param_with_synonym = "<<synonym>> or {{para|chapter}}",
param_no_synonym = "{{para|chapter}}",
text = [=[
* <<params>> – the name of the chapter quoted from.]=],
},
roman_chapter = {
param_with_synonym = {"inherit", "chapter"},
param_no_synonym = {"inherit", "chapter"},
text = [=[
* <<params>> – the chapter number quoted from in uppercase Roman numerals.]=],
},
arabic_chapter = {
param_with_synonym = {"inherit", "chapter"},
param_no_synonym = {"inherit", "chapter"},
text = [=[
* <<params>> – the chapter number quoted from in Arabic numerals.]=],
},
trailing_params = {
text = [=[
* {{para|footer}} – a comment on the passage quoted.
* {{para|brackets}} – use {{para|brackets|on}} to surround a quotation with [[bracket#Noun|brackets]]. This indicates that the quotation either contains a mere mention of a term (for example, “some people find the word '''''manoeuvre''''' hard to spell”) rather than an actual use of it (for example, “we need to '''manoeuvre''' carefully to avoid causing upset”), or does not provide an actual instance of a term but provides information about related terms.]=],
},
}
function export.paramdoc(frame)
local parargs = frame:getParent().args
local args = process_params(parargs, {
[1] = true,
})
local text = args[1]
local function do_param_with_optional_synonym(param, text_to_sub, paramtext_synonym, paramtext_no_synonym)
local function sub_param(synonym)
local subbed_paramtext
if synonym then
subbed_paramtext = paramtext_synonym:gsub("<<synonym>>", "{{para|" .. replacement_escape(synonym) .. "}}")
else
subbed_paramtext = paramtext_no_synonym
end
return frame:preprocess((text_to_sub:gsub("<<params>>", replacement_escape(subbed_paramtext))))
end
text = text:gsub("<<" .. pattern_escape(param) .. ">>", function()
return sub_param()
end)
text = text:gsub("<<" .. pattern_escape(param) .. ":(.-)>>", sub_param)
end
local function fetch_text(param_to_replace, key)
local spec = paramdoc_param_replacements[param_to_replace]
local val = spec[key]
if type(val) == "string" then
return val
end
if type(val) == "table" and val[1] == "inherit" then
return fetch_text(val[2], key)
end
error(
"Internal error: Unrecognized value for param '"
.. param_to_replace
.. "', key '"
.. key
.. "': "
.. mw.dumpObject(val)
)
end
for param_to_replace, spec in pairs(paramdoc_param_replacements) do
if not spec.param_no_synonym then
-- Text to substitute directly.
text = text:gsub("<<" .. pattern_escape(param_to_replace) .. ">>", function()
return frame:preprocess(fetch_text(param_to_replace, "text"))
end)
else
do_param_with_optional_synonym(
param_to_replace,
fetch_text(param_to_replace, "text"),
fetch_text(param_to_replace, "param_with_synonym"),
fetch_text(param_to_replace, "param_no_synonym")
)
end
end
-- Remove final newline so template code can add a newline after invocation
text = text:gsub("\n$", "")
return text
end
return export
k5559jxnbddst687ypwonq9gixjxzt5
Mòideal:etymology/templates
828
17526
88842
87953
2026-08-06T21:41:00Z
Altronic
4137
add argument
88842
Scribunto
text/plain
local export = {}
local require_when_needed = require("Module:require when needed")
local concat = table.concat
local format_categories = require_when_needed("Module:utilities", "format_categories")
local insert = table.insert
local process_params = require_when_needed("Module:parameters", "process")
local trim = mw.text.trim
local lower = mw.ustring.lower
local dump = mw.dumpObject
local etymology_module = "Module:etymology"
local etymology_specialized_module = "Module:etymology/specialized"
local parameter_utilities_module = "Module:parameter utilities"
-- For testing
local force_cat = false
local allowed_conjs = {"and", "or", ",", "/", "~", ";"}
local function parse_etym_args(parent_args, base_params, has_dest_lang)
local m_param_utils = require(parameter_utilities_module)
local param_mods = m_param_utils.construct_param_mods {
{group = {"link", "q", "l", "ref"}},
}
local sourcearg, termarg
if has_dest_lang then
sourcearg, termarg = 2, 3
else
sourcearg, termarg = 1, 2
end
local terms, args = m_param_utils.parse_term_with_inline_modifiers_and_separate_params {
params = base_params,
param_mods = param_mods,
raw_args = parent_args,
termarg = termarg,
track_module = "etymology",
lang = function(args)
return args[sourcearg][#args[sourcearg]]
end,
sc = "sc",
-- Don't do this, doesn't seem to make sense.
-- parse_lang_prefix = true,
make_separate_g_into_list = true,
splitchar = ",",
subitem_param_handling = "last",
}
-- If term param 3= is empty, there will be no terms in terms.terms. To facilitate further code and for
-- compatibility,, insert one. It will display as <small>[Term?]</small>.
if not terms.terms[1] then
terms.terms[1] = {
lang = args[sourcearg][#args[sourcearg]],
sc = args.sc,
}
end
return terms.terms, args
end
function export.parse_2_lang_args(parent_args, has_text, no_family)
local boolean = {type = "boolean"}
local params = {
[1] = {
required = true,
type = "language",
default = "und"
},
[2] = {
required = true,
sublist = true,
type = "language",
family = not no_family,
default = "und"
},
[3] = true,
[4] = {alias_of = "alt"},
[5] = {alias_of = "t"},
["senseid"] = true,
["nocat"] = boolean,
["sort"] = true,
["sourceconj"] = true,
["conj"] = {set = allowed_conjs, default = ","},
["len"] = { type = "boolean" }
}
if has_text then
params["notext"] = boolean
params["nocap"] = boolean
end
return parse_etym_args(parent_args, params, "has dest lang")
end
-- Implementation of deprecated {{etyl}}. Provided to make histories more legible.
function export.etyl(frame)
local params = {
[1] = {required = true, type = "language", default = "und"},
[2] = {type = "language", default = "en"},
["sort"] = {},
}
-- Empty language means English, but "-" means no language. Yes, confusing...
local args = frame:getParent().args
if args[2] and trim(args[2]) == "-" then
params[2] = nil
args = process_params({
[1] = args[1],
["sort"] = args.sort
}, params)
else
args = process_params(args, params)
end
return require(etymology_module).format_source {
lang = args[2],
source = args[1],
sort_key = args.sort,
force_cat = force_cat,
}
end
-- Implementation of {{derived}}/{{der}}.
function export.derived(frame)
local parent_args = frame:getParent().args
local terms, args = export.parse_2_lang_args(parent_args)
return require(etymology_module).format_derived {
lang = args[1],
sources = args[2],
terms = terms,
sort_key = args.sort,
nocat = args.nocat,
sourceconj = args.sourceconj,
conj = args.conj,
template_name = "derived",
force_cat = force_cat,
}
end
-- Implementation of {{borrowed}}/{{bor}}.
function export.borrowed(frame)
local parent_args = frame:getParent().args
local terms, args = export.parse_2_lang_args(parent_args)
return require(etymology_module).format_borrowed {
lang = args[1],
sources = args[2],
terms = terms,
sort_key = args.sort,
nocat = args.nocat,
sourceconj = args.sourceconj,
conj = args.conj,
force_cat = force_cat,
}
end
function export.inherited(frame)
local parent_args = frame:getParent().args
local terms, args = export.parse_2_lang_args(parent_args)
local sources = args[2]
if sources[2] then
-- Because this doesn't really make sense.
error("[[Template:inherited]] doesn't support multiple comma-separated sources")
end
return require(etymology_module).format_inherited {
lang = args[1],
terms = terms,
sort_key = args.sort,
nocat = args.nocat,
conj = args.conj,
force_cat = force_cat,
}
end
function export.cognate(frame)
local params = {
[1] = {
required = true,
sublist = true,
type = "language",
family = true,
default = "und"
},
[2] = true,
[3] = {alias_of = "alt"},
[4] = {alias_of = "t"},
sourceconj = true,
["conj"] = {set = allowed_conjs, default = ","},
sort = true,
}
local parent_args = frame:getParent().args
local terms, args = parse_etym_args(parent_args, params, false)
return require(etymology_module).format_cognate {
sources = args[1],
terms = terms,
sort_key = args.sort,
sourceconj = args.sourceconj,
conj = args.conj,
force_cat = force_cat,
}
end
function export.noncognate(frame)
return export.cognate(frame)
end
-- Supports various specialized types of borrowings, according to `frame.args.bortype`:
-- "learned" = {{lbor}}/{{learned borrowing}}
-- "semi-learned" = {{slbor}}/{{semi-learned borrowing}}
-- "orthographic" = {{obor}}/{{orthographic borrowing}}
-- "unadapted" = {{ubor}}/{{unadapted borrowing}}
-- "calque" = {{cal}}/{{calque}}
-- "partial-calque" = {{pcal}}/{{partial calque}}
-- "semantic-loan" = {{sl}}/{{semantic loan}}
-- "transliteration" = {{translit}}/{{transliteration}}
-- "phono-semantic-matching" = {{psm}}/{{phono-semantic matching}}
function export.specialized_borrowing(frame)
local parent_args = frame:getParent().args
local terms, args = export.parse_2_lang_args(parent_args, "has text")
local m_etymology_specialized = require(etymology_specialized_module)
return m_etymology_specialized.specialized_borrowing {
bortype = frame.args.bortype,
lang = args[1],
sources = args[2],
terms = terms,
sort_key = args.sort,
nocap = args.nocap,
notext = args.notext,
nocat = args.nocat,
sourceconj = args.sourceconj,
conj = args.conj,
senseid = args.senseid,
force_cat = force_cat,
}
end
-- Implementation of miscellaneous templates such as {{abbrev}}, {{back-formation}}, {{clipping}}, {{ellipsis}},
-- {{rebracketing}} and {{reduplication}} that have a single associated term.
function export.misc_variant(frame)
local iparams = {
["ignore-params"] = true,
text = {required = true},
oftext = true,
cat = {list = true}, -- allow and compress holes
conj = true,
}
local iargs = process_params(frame.args, iparams)
local boolean = {type = "boolean"}
local params = {
[1] = {required = true, type = "language", default = "und"},
[2] = true,
[3] = {alias_of = "alt"},
[4] = {alias_of = "t"},
nocap = boolean, -- should be processed in the template itself
notext = boolean,
nocat = boolean,
conj = {set = allowed_conjs},
sort = true,
}
-- |ignore-params= parameter to module invocation specifies
-- additional parameter names to allow in template invocation, separated by
-- commas. They must consist of ASCII letters or numbers or hyphens.
local ignore_params = iargs["ignore-params"]
if ignore_params then
ignore_params = trim(ignore_params)
if not ignore_params:match("^[%w%-,]+$") then
error("Invalid characters in |ignore-params=: " .. ignore_params:gsub("[%w%-,]+", ""))
end
for param in ignore_params:gmatch("[%w%-]+") do
if params[param] then
error("Duplicate param |" .. param
.. " in |ignore-params=: already specified in params")
end
params[param] = true
end
end
local m_param_utils = require(parameter_utilities_module)
local param_mods = m_param_utils.construct_param_mods {
{group = {"link", "q", "l", "ref"}},
}
local parent_args = frame:getParent().args
local terms, args = m_param_utils.parse_term_with_inline_modifiers_and_separate_params {
params = params,
param_mods = param_mods,
raw_args = parent_args,
termarg = 2,
track_module = "etymology",
-- Don't set lang here as we want to know whether there was a lang prefix or not.
sc = "sc",
parse_lang_prefix = true,
allow_multiple_lang_prefixes = true,
make_separate_g_into_list = true,
splitchar = ",",
subitem_param_handling = "last",
}
return require(etymology_module).format_misc_variant {
lang = args[1],
notext = args.notext,
text = iargs.text,
oftext = iargs.oftext,
terms = terms.terms,
sort_key = args.sort,
conj = args.conj or iargs.conj or "and",
nocat = args.nocat,
cats = iargs.cat,
force_cat = force_cat,
}
end
-- Implementation of miscellaneous templates such as {{doublet}} that can take multiple terms. Doesn't handle {{blend}}
-- or {{univerbation}}, which display + signs between elements and use compound_like in [[Module:affix/templates]].
function export.misc_variant_multiple_terms(frame)
local iparams = {
text = {required = true},
oftext = true,
cat = {list = true}, -- allow and compress holes
conj = true,
}
local iargs = process_params(frame.args, iparams)
local boolean = {type = "boolean"}
local params = {
[1] = {required = true, type = "language", template_default = "und"},
[2] = {list = true, allow_holes = true},
nocap = boolean, -- should be processed in the template itself
notext = boolean,
nocat = boolean,
conj = {set = allowed_conjs},
sort = true,
}
local m_param_utils = require(parameter_utilities_module)
local param_mods = m_param_utils.construct_param_mods {
-- We want to require an index for all params.
{default = true, require_index = true},
{group = {"link", "q", "l", "ref"}},
}
local parent_args = frame:getParent().args
local terms, args = m_param_utils.parse_list_with_inline_modifiers_and_separate_params {
params = params,
param_mods = param_mods,
raw_args = parent_args,
termarg = 2,
parse_lang_prefix = true,
allow_multiple_lang_prefixes = true,
track_module = "etymology-templates-doublet",
disallow_custom_separators = true,
-- For compatibility, we need to not skip completely unspecified items. It is common, for example, to do
-- {{suffix|lang||foo}} to generate "+ -foo".
dont_skip_items = true,
-- Don't set lang here as we want to know whether there was a lang prefix or not.
sc = "sc.default",
}
return require(etymology_module).format_misc_variant {
lang = args[1],
notext = args.notext,
text = iargs.text,
oftext = iargs.oftext,
terms = terms,
sort_key = args.sort,
conj = args.conj or iargs.conj or "and",
nocat = args.nocat,
cats = iargs.cat,
force_cat = force_cat,
}
end
-- Implementation of miscellaneous templates such as {{unknown}} that have no associated terms.
do
local function get_args(frame)
local boolean = {type = "boolean"}
local params = {
[1] = {required = true, type = "language", default = "und"},
["title"] = true,
["nocap"] = boolean, -- should be processed in the template itself
["notext"] = boolean,
["nocat"] = boolean,
["sort"] = true,
}
if frame.args.title2_alias then
params[2] = {alias_of = "title"}
end
return process_params(frame:getParent().args, params)
end
function export.misc_variant_no_term(frame)
local args = get_args(frame)
return require(etymology_module).format_misc_variant_no_term {
lang = args[1],
notext = args.notext,
title = args.title or frame.args.text,
nocat = args.nocat,
cat = frame.args.cat,
sort_key = args.sort,
force_cat = force_cat,
}
end
-- This function works similarly to misc_variant_no_term(), but with some automatic linking to the glossary in
-- `title`.
function export.onomatopoeia(frame)
local args = get_args(frame)
local title = args.title
if title and (lower(title) == "imitative" or lower(title) == "imitation") then
title = "[[Appendix:Glossary#imitative|" .. title .. "]]"
end
return require(etymology_module).format_misc_variant_no_term {
lang = args[1],
notext = args.notext,
title = title or frame.args.text,
nocat = args.nocat,
cat = frame.args.cat,
sort_key = args.sort,
force_cat = force_cat,
}
end
end
return export
bbpkn5ag0kw0xf4gi06xeh7t1i3mqd5
Teamplaid:RQ:sga:Glosses
10
17886
88768
2026-03-08T20:33:56Z
en>Mahagaja
0
88768
wikitext
text/x-wiki
{{#invoke:checkparams|error}}<!-- Validate template parameters
-->{{#switch:{{{1|}}}
|Acr={{circa|'''850'''}}, Carlsruhe Glosses on St Augustine’s ''Soliloquia'', published in ''Thesaurus Palaeohibernicus'' (reprinted 1987, Dublin Institute for Advanced Studies), edited and with translations by Whitley Stokes and John Strachan, vol. 2, pp. 1–9, [[oldwikisource:Thesaurus Palaeohibernicus/Glosses on S. Augustine's Soliloquia (Carlsruhe)#{{{2}}}{{{3}}}|Acr. {{{2}}}{{sup|{{{3|}}}}}{{{4|}}}]]
|Ardm={{circa|'''810'''}}, Biblical Glosses in the Book of Armagh, published in ''Thesaurus Palaeohibernicus'' (reprinted 1987, Dublin Institute for Advanced Studies), edited and with translations by Whitley Stokes and John Strachan, vol. I, pp. 494–98, {{{1}}}. {{{2}}}{{sup|{{{3|}}}}}{{{4|}}}
|Bcr={{circa|'''850'''}} Glosses on the Carlsruhe Beda, published in ''Thesaurus Palaeohibernicus'' (reprinted 1987, Dublin Institute for Advanced Studies), edited and with translations by Whitley Stokes and John Strachan, vol. II, pp. 10–30, [[oldwikisource:Thesaurus Palaeohibernicus/Glosses on Beda (Carlsruhe)#{{{2}}}{{{3}}}|Bcr. {{{2}}}{{sup|{{{3|}}}}}{{{4|}}}]]
|Ml={{circa|'''800–825'''}}, Diarmait, Milan Glosses on the Psalms, published in ''Thesaurus Palaeohibernicus'' (reprinted 1987, Dublin Institute for Advanced Studies), edited and with translations by Whitley Stokes and John Strachan, vol. I, pp. 7–483, {{{1}}}. {{{2}}}{{sup|{{{3|}}}}}{{{4|}}}
|PCr={{circa|'''825–850'''}} Karlsruhe Glosses on Priscian, published in ''Thesaurus Palaeohibernicus'' (reprinted 1975, Dublin Institute for Advanced Studies), edited and with translations by Whitley Stokes and John Strachan, vol. II, pp. 225–230, {{{1}}}. {{{2}}}{{sup|{{{3|}}}}}{{{4|}}}
|Sg={{circa|'''845'''}}, St Gall Glosses on Priscian, published in ''Thesaurus Palaeohibernicus'' (reprinted 1975, Dublin Institute for Advanced Studies), edited and with translations by Whitley Stokes and John Strachan, vol. II, pp. 49–224, {{{1}}}. {{{2}}}{{sup|{{{3|}}}}}{{{4|}}}
|Tur={{circa|'''850–875'''}}, Turin Glosses and Scholia on St Mark, published in ''Thesaurus Palaeohibernicus'' (reprinted 1987, Dublin Institute for Advanced Studies), edited and with translations by Whitley Stokes and John Strachan, vol. I, pp. 484–94, {{{1}}}. {{{2}}}{{sup|{{{3|}}}}}{{{4|}}}
|Wb={{circa|'''800'''}}, Würzburg Glosses on the Pauline Epistles, published in ''Thesaurus Palaeohibernicus'' (reprinted 1987, Dublin Institute for Advanced Studies), edited and with translations by Whitley Stokes and John Strachan, vol. I, pp. 499–712, {{{1}}}. [https://wuerzburg.ie/index_Eng.html#folf.{{{2}}}{{{3}}}.1 {{{2}}}{{sup|{{{3|}}}}}{{{4|}}}]
|<includeonly>'''Error: please include a valid parameter'''[[Category:Requests for attention concerning Old Irish]]</includeonly>
}}<noinclude>
{{documentation}}</noinclude>
9r5aida8uu4iktbr6etl00yb96963k1
88769
88768
2026-08-06T17:45:44Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Template:RQ:sga:Glosses]]
88768
wikitext
text/x-wiki
{{#invoke:checkparams|error}}<!-- Validate template parameters
-->{{#switch:{{{1|}}}
|Acr={{circa|'''850'''}}, Carlsruhe Glosses on St Augustine’s ''Soliloquia'', published in ''Thesaurus Palaeohibernicus'' (reprinted 1987, Dublin Institute for Advanced Studies), edited and with translations by Whitley Stokes and John Strachan, vol. 2, pp. 1–9, [[oldwikisource:Thesaurus Palaeohibernicus/Glosses on S. Augustine's Soliloquia (Carlsruhe)#{{{2}}}{{{3}}}|Acr. {{{2}}}{{sup|{{{3|}}}}}{{{4|}}}]]
|Ardm={{circa|'''810'''}}, Biblical Glosses in the Book of Armagh, published in ''Thesaurus Palaeohibernicus'' (reprinted 1987, Dublin Institute for Advanced Studies), edited and with translations by Whitley Stokes and John Strachan, vol. I, pp. 494–98, {{{1}}}. {{{2}}}{{sup|{{{3|}}}}}{{{4|}}}
|Bcr={{circa|'''850'''}} Glosses on the Carlsruhe Beda, published in ''Thesaurus Palaeohibernicus'' (reprinted 1987, Dublin Institute for Advanced Studies), edited and with translations by Whitley Stokes and John Strachan, vol. II, pp. 10–30, [[oldwikisource:Thesaurus Palaeohibernicus/Glosses on Beda (Carlsruhe)#{{{2}}}{{{3}}}|Bcr. {{{2}}}{{sup|{{{3|}}}}}{{{4|}}}]]
|Ml={{circa|'''800–825'''}}, Diarmait, Milan Glosses on the Psalms, published in ''Thesaurus Palaeohibernicus'' (reprinted 1987, Dublin Institute for Advanced Studies), edited and with translations by Whitley Stokes and John Strachan, vol. I, pp. 7–483, {{{1}}}. {{{2}}}{{sup|{{{3|}}}}}{{{4|}}}
|PCr={{circa|'''825–850'''}} Karlsruhe Glosses on Priscian, published in ''Thesaurus Palaeohibernicus'' (reprinted 1975, Dublin Institute for Advanced Studies), edited and with translations by Whitley Stokes and John Strachan, vol. II, pp. 225–230, {{{1}}}. {{{2}}}{{sup|{{{3|}}}}}{{{4|}}}
|Sg={{circa|'''845'''}}, St Gall Glosses on Priscian, published in ''Thesaurus Palaeohibernicus'' (reprinted 1975, Dublin Institute for Advanced Studies), edited and with translations by Whitley Stokes and John Strachan, vol. II, pp. 49–224, {{{1}}}. {{{2}}}{{sup|{{{3|}}}}}{{{4|}}}
|Tur={{circa|'''850–875'''}}, Turin Glosses and Scholia on St Mark, published in ''Thesaurus Palaeohibernicus'' (reprinted 1987, Dublin Institute for Advanced Studies), edited and with translations by Whitley Stokes and John Strachan, vol. I, pp. 484–94, {{{1}}}. {{{2}}}{{sup|{{{3|}}}}}{{{4|}}}
|Wb={{circa|'''800'''}}, Würzburg Glosses on the Pauline Epistles, published in ''Thesaurus Palaeohibernicus'' (reprinted 1987, Dublin Institute for Advanced Studies), edited and with translations by Whitley Stokes and John Strachan, vol. I, pp. 499–712, {{{1}}}. [https://wuerzburg.ie/index_Eng.html#folf.{{{2}}}{{{3}}}.1 {{{2}}}{{sup|{{{3|}}}}}{{{4|}}}]
|<includeonly>'''Error: please include a valid parameter'''[[Category:Requests for attention concerning Old Irish]]</includeonly>
}}<noinclude>
{{documentation}}</noinclude>
9r5aida8uu4iktbr6etl00yb96963k1
Teamplaid:circa
10
17887
88770
2026-08-04T19:13:00Z
en>AutoDooz
0
no existing calls with bad parameters, throw error instead of warning to avoid future misuse
88770
wikitext
text/x-wiki
{{#invoke:checkparams|error|short}}<!-- Validate template parameters
--><abbr title="circa">c.</abbr>{{#if:{{{1|}}}
| <span style="white-space: nowrap;"> {{trim|{{{1|}}}}}</span>
}}{{#if: {{{2|}}}
| – c.<span style="white-space: nowrap;"> {{{2}}}</span>
}}<!--
--><noinclude>{{documentation}}</noinclude>
3d10wbgim45bivvspu1zqlj2slxrv5m
88771
88770
2026-08-06T17:46:54Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Template:circa]]
88770
wikitext
text/x-wiki
{{#invoke:checkparams|error|short}}<!-- Validate template parameters
--><abbr title="circa">c.</abbr>{{#if:{{{1|}}}
| <span style="white-space: nowrap;"> {{trim|{{{1|}}}}}</span>
}}{{#if: {{{2|}}}
| – c.<span style="white-space: nowrap;"> {{{2}}}</span>
}}<!--
--><noinclude>{{documentation}}</noinclude>
3d10wbgim45bivvspu1zqlj2slxrv5m
Teamplaid:quote
10
17888
88772
2026-04-15T09:13:27Z
en>Surjection
0
Changed protection settings for "[[Template:quote]]": Highly visible template/module ([Edit=Allow only autopatrollers] (indefinite) [Move=Allow only autopatrollers] (indefinite))
88772
wikitext
text/x-wiki
<includeonly>{{#invoke:usex/templates|usex_t|quote=1}}</includeonly><noinclude>{{documentation}}</noinclude>
j4a69ylf2bgejx9blqbs44xcom1x6si
88773
88772
2026-08-06T17:46:59Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Template:quote]]
88772
wikitext
text/x-wiki
<includeonly>{{#invoke:usex/templates|usex_t|quote=1}}</includeonly><noinclude>{{documentation}}</noinclude>
j4a69ylf2bgejx9blqbs44xcom1x6si
Teamplaid:hyph
10
17889
88774
2024-11-09T20:24:20Z
en>Surjection
0
Changed protection settings for "[[Template:hyph]]" ([Edit=Allow only template editors and administrators] (indefinite) [Move=Allow only template editors and administrators] (indefinite))
88774
wikitext
text/x-wiki
#REDIRECT [[Template:hyphenation]]
74ud0rq50ttrtn2x4qfwvh9jxuk3ezv
88775
88774
2026-08-06T17:59:19Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Template:hyph]]
88774
wikitext
text/x-wiki
#REDIRECT [[Template:hyphenation]]
74ud0rq50ttrtn2x4qfwvh9jxuk3ezv
Teamplaid:hyphenation
10
17890
88776
2023-01-12T03:36:34Z
en>Theknightwho
0
88776
wikitext
text/x-wiki
{{ {{#if:{{{lang|}}}|check deprecated lang param usage|no deprecated lang param usage}}|lang={{{lang|}}}|<!--
-->{{#invoke:hyphenation|hyphenation}}<!--
-->}}<!--
--><noinclude>{{documentation}}</noinclude>
364pj5u4hseu4haqzv1fa5f8npchy0s
88777
88776
2026-08-06T17:59:25Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Template:hyphenation]]
88776
wikitext
text/x-wiki
{{ {{#if:{{{lang|}}}|check deprecated lang param usage|no deprecated lang param usage}}|lang={{{lang|}}}|<!--
-->{{#invoke:hyphenation|hyphenation}}<!--
-->}}<!--
--><noinclude>{{documentation}}</noinclude>
364pj5u4hseu4haqzv1fa5f8npchy0s
88778
88777
2026-08-06T17:59:42Z
Altronic
4137
88778
wikitext
text/x-wiki
{{#invoke:hyphenation|hyphenation}}<!--
--><noinclude>{{documentation}}</noinclude>
i50htcn4kctu70dozrb1ocm8dks5cza
Mòideal:hyphenation
828
17891
88779
2025-11-04T04:08:30Z
en>Benwing2
0
tracking for '.qualifiers' field
88779
Scribunto
text/plain
local export = {}
local links_module = "Module:links"
local parameters_module = "Module:parameters"
local pron_qualifier_module = "Module:pron qualifier"
local function track(page)
require("Module:debug/track")("hyphenation/" .. page)
return true
end
--[==[
Meant to be called from a module. `data` is a table containing the following fields:
* `lang`: language object for the hyphenations or syllabifications;
* `hyphs`: a list of hyphenations/syllabifications, each described by an object which can contain the following fields:
** `hyph`: list of syllables comprising the hyphenation or syllabification, each a string;
** `q`: {nil} or a list of left regular qualifier strings, formatted using {format_qualifier()} in
[[Module:qualifier]];
** `qq`: {nil} or a list of right regular qualifier strings;
** `qualifiers`: {nil} or a list of left regular qualifier strings; for compatibility purposes only, do not use in new
code;
** `a`: {nil} or a list of left accent qualifier strings, formatted using {format_qualifiers()} in
[[Module:accent qualifier]];
** `aa`: {nil} or a list of right accent qualifier strings;
** `refs`: {nil} or a list of references or reference specs to add after the pronunciation and any posttext and
qualifiers; the value of a list item is either a string containing the reference text (typically a call to a
citation template such as {{tl|cite-book}}, or a template wrapping such a call), or an object with fields `text`
(the reference text), `name` (the name of the reference, as in {{cd|<nowiki><ref name="foo">...</ref></nowiki>}}
or {{cd|<nowiki><ref name="foo" /></nowiki>}}) and/or `group` (the group of the reference, as in
{{cd|<nowiki><ref name="foo" group="bar">...</ref></nowiki>}} or
{{cd|<nowiki><ref name="foo" group="bar"/></nowiki>}}); this uses a parser function to format the reference
appropriately and insert a footnote number that hyperlinks to the actual reference, located in the
{{cd|<nowiki><references /></nowiki>}} section;
** `sc`: {nil} or script object for this particular hyphenation/syllabification;
* `sc`: {nil} or script object for the hyphenations/syllabifications;
* `q`: {nil} or a list of overall left regular qualifier strings, formatted using {format_qualifier()} in
[[Module:qualifier]];
* `qq`: {nil} or a list of overall right regular qualifier strings;
* `a`: {nil} or a list of overall left accent qualifier strings, formatted using {format_qualifiers()} in
[[Module:accent qualifier]];
* `aa`: {nil} or a list of overall right accent qualifier strings;
* `caption`, {nil} or a string specifying the caption to use, in place of {"Hyphenation"}; e.g. use {"Syllabification"}
if what is passed in is actually a syllabification, as is common; a colon and space is automatically added after the
caption;
* `nocaption`: if true, suppress the caption display.
]==]
function export.format_hyphenations(data)
local hyphtexts = {}
for _, hyph in ipairs(data.hyphs) do
if #hyph.hyph == 0 then
error("Saw empty hyphenation; use || to separate hyphenations")
end
if hyph.qualifiers then
track("old-qualifiers")
end
local text = require(links_module).full_link {
lang = data.lang, sc = hyph.sc or data.sc, alt = table.concat(hyph.hyph, "‧"), tr = "-" }
if hyph.q and hyph.q[1] or hyph.qq and hyph.qq[1] or hyph.qualifiers and hyph.qualifiers[1]
or hyph.a and hyph.a[1] or hyph.aa and hyph.aa[1] or hyph.refs and hyph.refs[1] then
text = require(pron_qualifier_module).format_qualifiers {
lang = data.lang,
text = text,
q = hyph.q,
qq = hyph.qq,
qualifiers = hyph.qualifiers,
a = hyph.a,
aa = hyph.aa,
refs = hyph.refs,
}
end
table.insert(hyphtexts, text)
end
local text = ((data.nocaption and "") or ((data.caption or "Hyphenation") .. ": ")) .. table.concat(hyphtexts, ", ")
if data.q and data.q[1] or data.qq and data.qq[1] or data.a and data.a[1] or data.aa and data.aa[1] then
text = require(pron_qualifier_module).format_qualifiers {
lang = data.lang,
text = text,
q = data.q,
qq = data.qq,
a = data.a,
aa = data.aa,
}
end
return text
end
--[==[
Entry point for {{tl|hyphenation}} template (also written {{tl|hyph}}).
]==]
function export.hyphenation(frame)
local parent_args = frame:getParent().args
local compat = parent_args.lang
local lang_param = compat and "lang" or 1
local offset = compat and 0 or 1
local params = {
[lang_param] = {required = true, type = "language", default = "und"},
[1 + offset] = {list = true, required = true, allow_holes = true, default = "{{{2}}}"},
-- FIXME: For compatibility, q= and qq= refer to the first hyphenation rather than overall. Consider tracking and
-- changing this.
["q"] = {list = true, allow_holes = true, type = "qualifier"},
["qq"] = {list = true, allow_holes = true, type = "qualifier"},
["a"] = {list = true, allow_holes = true, separate_no_index = true, type = "labels"},
["aa"] = {list = true, allow_holes = true, separate_no_index = true, type = "labels"},
["ref"] = {list = true, allow_holes = true, type = "references"},
["caption"] = {},
["nocaption"] = {type = "boolean"},
["sc"] = {list = true, allow_holes = true, separate_no_index = true, type = "script"},
}
local args = require(parameters_module).process(parent_args, params)
local lang = args[lang_param]
local sc = args.sc.default
local data = {
lang = lang,
sc = sc,
hyphs = {},
caption = args.caption,
nocaption = args.nocaption,
a = args.a.default,
aa = args.aa.default,
}
local this_hyph = {hyph = {}}
local maxindex = args[1 + offset].maxindex
local function insert_hyph()
local hyphnum = #data.hyphs + 1
this_hyph.q = args.q[hyphnum]
this_hyph.qq = args.qq[hyphnum]
this_hyph.a = args.a[hyphnum]
this_hyph.aa = args.aa[hyphnum]
this_hyph.refs = args.ref[hyphnum]
table.insert(data.hyphs, this_hyph)
end
for i=1, maxindex do
local syl = args[1 + offset][i]
if not syl then
insert_hyph()
this_hyph = {hyph = {}}
else
table.insert(this_hyph.hyph, syl)
end
end
insert_hyph()
return export.format_hyphenations(data)
end
return export
5ifdc56czy0071cr4rwjvauyq6wq80t
88780
88779
2026-08-06T17:59:49Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:hyphenation]]
88779
Scribunto
text/plain
local export = {}
local links_module = "Module:links"
local parameters_module = "Module:parameters"
local pron_qualifier_module = "Module:pron qualifier"
local function track(page)
require("Module:debug/track")("hyphenation/" .. page)
return true
end
--[==[
Meant to be called from a module. `data` is a table containing the following fields:
* `lang`: language object for the hyphenations or syllabifications;
* `hyphs`: a list of hyphenations/syllabifications, each described by an object which can contain the following fields:
** `hyph`: list of syllables comprising the hyphenation or syllabification, each a string;
** `q`: {nil} or a list of left regular qualifier strings, formatted using {format_qualifier()} in
[[Module:qualifier]];
** `qq`: {nil} or a list of right regular qualifier strings;
** `qualifiers`: {nil} or a list of left regular qualifier strings; for compatibility purposes only, do not use in new
code;
** `a`: {nil} or a list of left accent qualifier strings, formatted using {format_qualifiers()} in
[[Module:accent qualifier]];
** `aa`: {nil} or a list of right accent qualifier strings;
** `refs`: {nil} or a list of references or reference specs to add after the pronunciation and any posttext and
qualifiers; the value of a list item is either a string containing the reference text (typically a call to a
citation template such as {{tl|cite-book}}, or a template wrapping such a call), or an object with fields `text`
(the reference text), `name` (the name of the reference, as in {{cd|<nowiki><ref name="foo">...</ref></nowiki>}}
or {{cd|<nowiki><ref name="foo" /></nowiki>}}) and/or `group` (the group of the reference, as in
{{cd|<nowiki><ref name="foo" group="bar">...</ref></nowiki>}} or
{{cd|<nowiki><ref name="foo" group="bar"/></nowiki>}}); this uses a parser function to format the reference
appropriately and insert a footnote number that hyperlinks to the actual reference, located in the
{{cd|<nowiki><references /></nowiki>}} section;
** `sc`: {nil} or script object for this particular hyphenation/syllabification;
* `sc`: {nil} or script object for the hyphenations/syllabifications;
* `q`: {nil} or a list of overall left regular qualifier strings, formatted using {format_qualifier()} in
[[Module:qualifier]];
* `qq`: {nil} or a list of overall right regular qualifier strings;
* `a`: {nil} or a list of overall left accent qualifier strings, formatted using {format_qualifiers()} in
[[Module:accent qualifier]];
* `aa`: {nil} or a list of overall right accent qualifier strings;
* `caption`, {nil} or a string specifying the caption to use, in place of {"Hyphenation"}; e.g. use {"Syllabification"}
if what is passed in is actually a syllabification, as is common; a colon and space is automatically added after the
caption;
* `nocaption`: if true, suppress the caption display.
]==]
function export.format_hyphenations(data)
local hyphtexts = {}
for _, hyph in ipairs(data.hyphs) do
if #hyph.hyph == 0 then
error("Saw empty hyphenation; use || to separate hyphenations")
end
if hyph.qualifiers then
track("old-qualifiers")
end
local text = require(links_module).full_link {
lang = data.lang, sc = hyph.sc or data.sc, alt = table.concat(hyph.hyph, "‧"), tr = "-" }
if hyph.q and hyph.q[1] or hyph.qq and hyph.qq[1] or hyph.qualifiers and hyph.qualifiers[1]
or hyph.a and hyph.a[1] or hyph.aa and hyph.aa[1] or hyph.refs and hyph.refs[1] then
text = require(pron_qualifier_module).format_qualifiers {
lang = data.lang,
text = text,
q = hyph.q,
qq = hyph.qq,
qualifiers = hyph.qualifiers,
a = hyph.a,
aa = hyph.aa,
refs = hyph.refs,
}
end
table.insert(hyphtexts, text)
end
local text = ((data.nocaption and "") or ((data.caption or "Hyphenation") .. ": ")) .. table.concat(hyphtexts, ", ")
if data.q and data.q[1] or data.qq and data.qq[1] or data.a and data.a[1] or data.aa and data.aa[1] then
text = require(pron_qualifier_module).format_qualifiers {
lang = data.lang,
text = text,
q = data.q,
qq = data.qq,
a = data.a,
aa = data.aa,
}
end
return text
end
--[==[
Entry point for {{tl|hyphenation}} template (also written {{tl|hyph}}).
]==]
function export.hyphenation(frame)
local parent_args = frame:getParent().args
local compat = parent_args.lang
local lang_param = compat and "lang" or 1
local offset = compat and 0 or 1
local params = {
[lang_param] = {required = true, type = "language", default = "und"},
[1 + offset] = {list = true, required = true, allow_holes = true, default = "{{{2}}}"},
-- FIXME: For compatibility, q= and qq= refer to the first hyphenation rather than overall. Consider tracking and
-- changing this.
["q"] = {list = true, allow_holes = true, type = "qualifier"},
["qq"] = {list = true, allow_holes = true, type = "qualifier"},
["a"] = {list = true, allow_holes = true, separate_no_index = true, type = "labels"},
["aa"] = {list = true, allow_holes = true, separate_no_index = true, type = "labels"},
["ref"] = {list = true, allow_holes = true, type = "references"},
["caption"] = {},
["nocaption"] = {type = "boolean"},
["sc"] = {list = true, allow_holes = true, separate_no_index = true, type = "script"},
}
local args = require(parameters_module).process(parent_args, params)
local lang = args[lang_param]
local sc = args.sc.default
local data = {
lang = lang,
sc = sc,
hyphs = {},
caption = args.caption,
nocaption = args.nocaption,
a = args.a.default,
aa = args.aa.default,
}
local this_hyph = {hyph = {}}
local maxindex = args[1 + offset].maxindex
local function insert_hyph()
local hyphnum = #data.hyphs + 1
this_hyph.q = args.q[hyphnum]
this_hyph.qq = args.qq[hyphnum]
this_hyph.a = args.a[hyphnum]
this_hyph.aa = args.aa[hyphnum]
this_hyph.refs = args.ref[hyphnum]
table.insert(data.hyphs, this_hyph)
end
for i=1, maxindex do
local syl = args[1 + offset][i]
if not syl then
insert_hyph()
this_hyph = {hyph = {}}
else
table.insert(this_hyph.hyph, syl)
end
end
insert_hyph()
return export.format_hyphenations(data)
end
return export
5ifdc56czy0071cr4rwjvauyq6wq80t
88781
88780
2026-08-06T18:01:30Z
Altronic
4137
change text
88781
Scribunto
text/plain
local export = {}
local links_module = "Module:links"
local parameters_module = "Module:parameters"
local pron_qualifier_module = "Module:pron qualifier"
local function track(page)
require("Module:debug/track")("hyphenation/" .. page)
return true
end
--[==[
Meant to be called from a module. `data` is a table containing the following fields:
* `lang`: language object for the hyphenations or syllabifications;
* `hyphs`: a list of hyphenations/syllabifications, each described by an object which can contain the following fields:
** `hyph`: list of syllables comprising the hyphenation or syllabification, each a string;
** `q`: {nil} or a list of left regular qualifier strings, formatted using {format_qualifier()} in
[[Module:qualifier]];
** `qq`: {nil} or a list of right regular qualifier strings;
** `qualifiers`: {nil} or a list of left regular qualifier strings; for compatibility purposes only, do not use in new
code;
** `a`: {nil} or a list of left accent qualifier strings, formatted using {format_qualifiers()} in
[[Module:accent qualifier]];
** `aa`: {nil} or a list of right accent qualifier strings;
** `refs`: {nil} or a list of references or reference specs to add after the pronunciation and any posttext and
qualifiers; the value of a list item is either a string containing the reference text (typically a call to a
citation template such as {{tl|cite-book}}, or a template wrapping such a call), or an object with fields `text`
(the reference text), `name` (the name of the reference, as in {{cd|<nowiki><ref name="foo">...</ref></nowiki>}}
or {{cd|<nowiki><ref name="foo" /></nowiki>}}) and/or `group` (the group of the reference, as in
{{cd|<nowiki><ref name="foo" group="bar">...</ref></nowiki>}} or
{{cd|<nowiki><ref name="foo" group="bar"/></nowiki>}}); this uses a parser function to format the reference
appropriately and insert a footnote number that hyperlinks to the actual reference, located in the
{{cd|<nowiki><references /></nowiki>}} section;
** `sc`: {nil} or script object for this particular hyphenation/syllabification;
* `sc`: {nil} or script object for the hyphenations/syllabifications;
* `q`: {nil} or a list of overall left regular qualifier strings, formatted using {format_qualifier()} in
[[Module:qualifier]];
* `qq`: {nil} or a list of overall right regular qualifier strings;
* `a`: {nil} or a list of overall left accent qualifier strings, formatted using {format_qualifiers()} in
[[Module:accent qualifier]];
* `aa`: {nil} or a list of overall right accent qualifier strings;
* `caption`, {nil} or a string specifying the caption to use, in place of {"Hyphenation"}; e.g. use {"Syllabification"}
if what is passed in is actually a syllabification, as is common; a colon and space is automatically added after the
caption;
* `nocaption`: if true, suppress the caption display.
]==]
function export.format_hyphenations(data)
local hyphtexts = {}
for _, hyph in ipairs(data.hyphs) do
if #hyph.hyph == 0 then
error("Saw empty hyphenation; use || to separate hyphenations")
end
if hyph.qualifiers then
track("old-qualifiers")
end
local text = require(links_module).full_link {
lang = data.lang, sc = hyph.sc or data.sc, alt = table.concat(hyph.hyph, "‧"), tr = "-" }
if hyph.q and hyph.q[1] or hyph.qq and hyph.qq[1] or hyph.qualifiers and hyph.qualifiers[1]
or hyph.a and hyph.a[1] or hyph.aa and hyph.aa[1] or hyph.refs and hyph.refs[1] then
text = require(pron_qualifier_module).format_qualifiers {
lang = data.lang,
text = text,
q = hyph.q,
qq = hyph.qq,
qualifiers = hyph.qualifiers,
a = hyph.a,
aa = hyph.aa,
refs = hyph.refs,
}
end
table.insert(hyphtexts, text)
end
local text = ((data.nocaption and "") or ((data.caption or "Tàthanachadh") .. ": ")) .. table.concat(hyphtexts, ", ")
if data.q and data.q[1] or data.qq and data.qq[1] or data.a and data.a[1] or data.aa and data.aa[1] then
text = require(pron_qualifier_module).format_qualifiers {
lang = data.lang,
text = text,
q = data.q,
qq = data.qq,
a = data.a,
aa = data.aa,
}
end
return text
end
--[==[
Entry point for {{tl|hyphenation}} template (also written {{tl|hyph}}).
]==]
function export.hyphenation(frame)
local parent_args = frame:getParent().args
local compat = parent_args.lang
local lang_param = compat and "lang" or 1
local offset = compat and 0 or 1
local params = {
[lang_param] = {required = true, type = "language", default = "und"},
[1 + offset] = {list = true, required = true, allow_holes = true, default = "{{{2}}}"},
-- FIXME: For compatibility, q= and qq= refer to the first hyphenation rather than overall. Consider tracking and
-- changing this.
["q"] = {list = true, allow_holes = true, type = "qualifier"},
["qq"] = {list = true, allow_holes = true, type = "qualifier"},
["a"] = {list = true, allow_holes = true, separate_no_index = true, type = "labels"},
["aa"] = {list = true, allow_holes = true, separate_no_index = true, type = "labels"},
["ref"] = {list = true, allow_holes = true, type = "references"},
["caption"] = {},
["nocaption"] = {type = "boolean"},
["sc"] = {list = true, allow_holes = true, separate_no_index = true, type = "script"},
}
local args = require(parameters_module).process(parent_args, params)
local lang = args[lang_param]
local sc = args.sc.default
local data = {
lang = lang,
sc = sc,
hyphs = {},
caption = args.caption,
nocaption = args.nocaption,
a = args.a.default,
aa = args.aa.default,
}
local this_hyph = {hyph = {}}
local maxindex = args[1 + offset].maxindex
local function insert_hyph()
local hyphnum = #data.hyphs + 1
this_hyph.q = args.q[hyphnum]
this_hyph.qq = args.qq[hyphnum]
this_hyph.a = args.a[hyphnum]
this_hyph.aa = args.aa[hyphnum]
this_hyph.refs = args.ref[hyphnum]
table.insert(data.hyphs, this_hyph)
end
for i=1, maxindex do
local syl = args[1 + offset][i]
if not syl then
insert_hyph()
this_hyph = {hyph = {}}
else
table.insert(this_hyph.hyph, syl)
end
end
insert_hyph()
return export.format_hyphenations(data)
end
return export
t2bfdyvl8kp8jlw170va4rup8ulzziu
Teamplaid:category tree data submodule documentation
10
17892
88782
2025-06-04T21:16:26Z
en>Benwing2
0
Benwing2 moved page [[Template:poscatboiler data submodule documentation]] to [[Template:category tree data submodule documentation]] without leaving a redirect: 'poscatboiler' as term is going away, leaving just 'category tree'
88782
wikitext
text/x-wiki
This data submodule defines part of Wiktionary's category structure.
For an introduction to the category tree system and a description of how to add or modify categories, see [[Module:category tree/documentation]].<noinclude>{{documentation}}</noinclude>
mcxfpt90i6xr7t9uglu422x131ffdpn
88783
88782
2026-08-06T18:16:29Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Template:category_tree_data_submodule_documentation]]
88782
wikitext
text/x-wiki
This data submodule defines part of Wiktionary's category structure.
For an introduction to the category tree system and a description of how to add or modify categories, see [[Module:category tree/documentation]].<noinclude>{{documentation}}</noinclude>
mcxfpt90i6xr7t9uglu422x131ffdpn
Mòideal:category tree/topic/Music
828
17893
88784
2026-07-23T19:36:34Z
en>WingerBot
0
explicit 'description = "default"' unnecessary; remove it (manually assisted)
88784
Scribunto
text/plain
local labels = {}
------------------------------------ music base labels ------------------------------------
labels["music"] = {
type = "related-to",
parents = {"art", "sound"},
}
labels["singing"] = {
type = "related-to",
parents = {"music", "talking"},
}
------------------------------------ music theory ------------------------------------
labels["musical voices and registers"] = {
type = "type",
parents = {"music", "singing"},
}
labels["musical meters"] = {
type = "type",
parents = {"music"},
}
labels["musical notes"] = {
type = "type",
parents = {"music"},
}
------------------------------------ musicians ------------------------------------
labels["musicians"] = {
type = "type",
parents = {"occupations", "music"},
}
labels["Justin Bieber"] = {
type = "related-to",
description = "=Canadian singer {{w|Justin Bieber}}",
parents = {"individuals", "musicians"},
}
labels["Taylor Swift"] = {
type = "related-to",
description = "=American singer-songwriter {{w|Taylor Swift}}",
parents = {"individuals", "musicians"},
}
------------------------------------ musical genres ------------------------------------
labels["musical genres"] = {
type = "type",
parents = {"music", "genres"},
}
labels["blues music"] = {
type = "related-to",
wikidata = 9759,
parents = {"musical genres"},
}
labels["jazz"] = {
type = "related-to",
parents = {"musical genres"},
}
labels["opera"] = {
type = "related-to",
parents = {"theater", "musical genres"},
}
------------------------------------ musical instruments ------------------------------------
labels["musical instruments"] = {
type = "set",
parents = {"music"},
}
labels["brass instruments"] = {
type = "type",
parents = {"wind instruments"},
}
labels["gongs"] = {
type = "related-to",
parents = {"percussion instruments"},
}
labels["organ instruments"] = {
type = "type",
parents = {"wind instruments"},
}
labels["percussion instruments"] = {
type = "set",
parents = {"musical instruments"},
}
labels["singing voice synthesis"] = {
type = "related-to",
parents = {"musical instruments", "singing"},
}
labels["string instruments"] = {
type = "set",
description = "{{{langname}}} names of [[string instrument]]s.",
parents = {"musical instruments"},
}
labels["wind instruments"] = {
type = "type",
parents = {"musical instruments"},
}
labels["woodwind instruments"] = {
type = "type",
parents = {"wind instruments"},
}
------------------------------------ music misc ------------------------------------
labels["lutherie"] = {
type = "related-to",
parents = {"music", "crafts"},
}
labels["music industry"] = {
type = "related-to",
description = "default with the",
parents = {"industries", "music"},
}
labels["national anthems"] = {
type = "name",
parents = {"artistic works", "music"},
}
return labels
8yabl5xj151u3vejavycr34z2n77gdx
88785
88784
2026-08-06T18:20:12Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/topic/Music]]
88784
Scribunto
text/plain
local labels = {}
------------------------------------ music base labels ------------------------------------
labels["music"] = {
type = "related-to",
parents = {"art", "sound"},
}
labels["singing"] = {
type = "related-to",
parents = {"music", "talking"},
}
------------------------------------ music theory ------------------------------------
labels["musical voices and registers"] = {
type = "type",
parents = {"music", "singing"},
}
labels["musical meters"] = {
type = "type",
parents = {"music"},
}
labels["musical notes"] = {
type = "type",
parents = {"music"},
}
------------------------------------ musicians ------------------------------------
labels["musicians"] = {
type = "type",
parents = {"occupations", "music"},
}
labels["Justin Bieber"] = {
type = "related-to",
description = "=Canadian singer {{w|Justin Bieber}}",
parents = {"individuals", "musicians"},
}
labels["Taylor Swift"] = {
type = "related-to",
description = "=American singer-songwriter {{w|Taylor Swift}}",
parents = {"individuals", "musicians"},
}
------------------------------------ musical genres ------------------------------------
labels["musical genres"] = {
type = "type",
parents = {"music", "genres"},
}
labels["blues music"] = {
type = "related-to",
wikidata = 9759,
parents = {"musical genres"},
}
labels["jazz"] = {
type = "related-to",
parents = {"musical genres"},
}
labels["opera"] = {
type = "related-to",
parents = {"theater", "musical genres"},
}
------------------------------------ musical instruments ------------------------------------
labels["musical instruments"] = {
type = "set",
parents = {"music"},
}
labels["brass instruments"] = {
type = "type",
parents = {"wind instruments"},
}
labels["gongs"] = {
type = "related-to",
parents = {"percussion instruments"},
}
labels["organ instruments"] = {
type = "type",
parents = {"wind instruments"},
}
labels["percussion instruments"] = {
type = "set",
parents = {"musical instruments"},
}
labels["singing voice synthesis"] = {
type = "related-to",
parents = {"musical instruments", "singing"},
}
labels["string instruments"] = {
type = "set",
description = "{{{langname}}} names of [[string instrument]]s.",
parents = {"musical instruments"},
}
labels["wind instruments"] = {
type = "type",
parents = {"musical instruments"},
}
labels["woodwind instruments"] = {
type = "type",
parents = {"wind instruments"},
}
------------------------------------ music misc ------------------------------------
labels["lutherie"] = {
type = "related-to",
parents = {"music", "crafts"},
}
labels["music industry"] = {
type = "related-to",
description = "default with the",
parents = {"industries", "music"},
}
labels["national anthems"] = {
type = "name",
parents = {"artistic works", "music"},
}
return labels
8yabl5xj151u3vejavycr34z2n77gdx
Mòideal:category tree/topic/Names
828
17894
88786
2026-07-23T19:36:35Z
en>WingerBot
0
explicit 'description = "default"' unnecessary; remove it (manually assisted)
88786
Scribunto
text/plain
local labels = {}
local handlers = {}
labels["names"] = {
type = "related-to",
description = "{{{langname}}} names for a variety of things.",
additional = "Given names and surnames can be found in [[:Category:Names by language]].",
parents = {"all topics"},
}
labels["letter names"] = {
type = "name",
description = "{{{langname}}} terms that are the names of letters.",
additional = "Ideally entries should be in a subcategory for the names of letters in a particular script.",
parents = {"names", "letters, symbols, and punctuation"},
}
-- Handler for "SCRIPT letter names".
table.insert(handlers, function(label)
local script = label:match("^(.*) letter names$")
if script then
local sc = require("Module:scripts").getByCanonicalName(script)
if sc then
local script_page
local appendix = ("Appendix: %s script"):format(script)
local appendix_title = mw.title.new(appendix)
if appendix_title and appendix_title.exists then
script_page = appendix
else
script_page = "w:" .. sc:getWikipediaArticle()
end
local link = ("[[%s|%s script]]"):format(script_page, script)
return {
type = "name",
description = ("{{{langname}}} terms that serve as names for letters and symbols directly based on letters, " ..
"such as [[ligature]]s and letters with [[diacritic]]s, of the %s."):format(link),
parents = {"letter names"},
}
end
end
end)
labels["couple nicknames"] = {
type = "name",
description = "{{{langname}}} informal names for pairs of people, especially [[celebrity]] [[couple]]s.",
additional = "For fictional relationships, see [[:Category:Ships (fandom)]].",
parents = {"nicknames", "people"},
}
labels["named prayers"] = {
type = "name",
description = "=[[specific]] [[prayer]]s",
parents = {"names", "prayer"},
}
labels["nicknames"] = {
type = "name",
description = "{{{langname}}} terms that are informal names or diminutives of specific entities (people, places, etc.)",
parents = {"names"},
}
labels["nicknames for individuals"] = {
type = "name",
description = "{{{langname}}} terms that are informal names for specific individuals (e.g. [[J-Lo]] for singer {{w|Jennifer Lopez}}).",
parents = {"individuals", "nicknames"},
}
labels["sports nicknames"] = {
type = "name",
description = "{{{langname}}} informal names for people or teams involved in sports.",
parents = {"nicknames", "sports"},
}
labels["taxonomic names"] = {
type = "name",
parents = {"names", "taxonomy"},
}
return {LABELS = labels, HANDLERS = handlers}
5knyjw34nmc268rm5tk8ub80kdq7mo9
88787
88786
2026-08-06T18:21:09Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/topic/Names]]
88786
Scribunto
text/plain
local labels = {}
local handlers = {}
labels["names"] = {
type = "related-to",
description = "{{{langname}}} names for a variety of things.",
additional = "Given names and surnames can be found in [[:Category:Names by language]].",
parents = {"all topics"},
}
labels["letter names"] = {
type = "name",
description = "{{{langname}}} terms that are the names of letters.",
additional = "Ideally entries should be in a subcategory for the names of letters in a particular script.",
parents = {"names", "letters, symbols, and punctuation"},
}
-- Handler for "SCRIPT letter names".
table.insert(handlers, function(label)
local script = label:match("^(.*) letter names$")
if script then
local sc = require("Module:scripts").getByCanonicalName(script)
if sc then
local script_page
local appendix = ("Appendix: %s script"):format(script)
local appendix_title = mw.title.new(appendix)
if appendix_title and appendix_title.exists then
script_page = appendix
else
script_page = "w:" .. sc:getWikipediaArticle()
end
local link = ("[[%s|%s script]]"):format(script_page, script)
return {
type = "name",
description = ("{{{langname}}} terms that serve as names for letters and symbols directly based on letters, " ..
"such as [[ligature]]s and letters with [[diacritic]]s, of the %s."):format(link),
parents = {"letter names"},
}
end
end
end)
labels["couple nicknames"] = {
type = "name",
description = "{{{langname}}} informal names for pairs of people, especially [[celebrity]] [[couple]]s.",
additional = "For fictional relationships, see [[:Category:Ships (fandom)]].",
parents = {"nicknames", "people"},
}
labels["named prayers"] = {
type = "name",
description = "=[[specific]] [[prayer]]s",
parents = {"names", "prayer"},
}
labels["nicknames"] = {
type = "name",
description = "{{{langname}}} terms that are informal names or diminutives of specific entities (people, places, etc.)",
parents = {"names"},
}
labels["nicknames for individuals"] = {
type = "name",
description = "{{{langname}}} terms that are informal names for specific individuals (e.g. [[J-Lo]] for singer {{w|Jennifer Lopez}}).",
parents = {"individuals", "nicknames"},
}
labels["sports nicknames"] = {
type = "name",
description = "{{{langname}}} informal names for people or teams involved in sports.",
parents = {"nicknames", "sports"},
}
labels["taxonomic names"] = {
type = "name",
parents = {"names", "taxonomy"},
}
return {LABELS = labels, HANDLERS = handlers}
5knyjw34nmc268rm5tk8ub80kdq7mo9
Mòideal:category tree/topic/Places
828
17895
88788
2026-07-23T19:36:43Z
en>WingerBot
0
explicit 'description = "default"' unnecessary; remove it (manually assisted)
88788
Scribunto
text/plain
local labels = {}
local handlers = {}
local m_table = require("Module:table")
local en_utilities_module = "Module:en-utilities"
local string_utilities_module = "Module:string utilities"
local m_locations = require("Module:place/locations")
local m_placetypes = require("Module:place/placetypes")
local placetype_data = m_placetypes.placetype_data
local internal_error = m_locations.internal_error
local dump = mw.dumpObject
local insert = table.insert
local concat = table.concat
local is_callable = require("Module:fun").is_callable
--[==[ intro:
This module is part of the category tree code and contains code to generate the descriptions of place-related categories
such as [[Category:de:Hokkaido Prefecture, Japan]], [[Category:es:Cities in France]],
[[Category:pt:Municipalities of Tocantins, Brazil]], etc.). Note that this module doesn't actually create the
categories; that must be done separately, with the text "{{tl|auto cat}}" as the definition of the category. (This
process should automatically happen periodically for non-empty categories, because they will appear in
[[Special:WantedCategories]] and a bot will periodically examine that list and create any needed category.)
There are two ways that category descriptions are specified: (1) by manually adding an entry to the `labels` table,
keyed by the label (the category minus the language code) with a value consisting of a Lua table specifying the
description text and the category's parents; (2) through handlers (pieces of Lua code) added to the `handlers` list,
which recognize labels of a specific type (e.g. `Cities in France`) and generate the appropriate specification for that
label on-the-fly.
See [[Module:place]] for an introduction to the terminology associated with places along with a list of all the relevant
modules, along with for more specific information on types of toponyms and placetypes and how their categorization
works.
]==]
local function lcfirst(label)
return mw.getContentLanguage():lcfirst(label)
end
local function gsub_literally(str, from, to)
local m_strutils = require(string_utilities_module)
return (str:gsub(m_strutils.pattern_escape(from), m_strutils.replacement_escape(to)))
end
local class_to_bare_category_parent = {
["polity"] = "polities",
["subpolity"] = "political divisions",
["settlement"] = "settlements",
["non-admin settlement"] = "settlements",
["capital"] = "capital cities",
["natural feature"] = "natural features",
["man-made structure"] = "man-made structures",
["geographic region"] = "geographic and cultural areas",
}
local class_is_political_division = {
["polity"] = true, -- strictly false but there are placetypes ambiguous between polity and subpolity
["subpolity"] = true,
["settlement"] = true,
["non-admin settlement"] = false,
["capital"] = true,
["natural feature"] = false,
["man-made structure"] = false,
["geographic region"] = false,
["generic place"] = false,
}
local capital_cat_to_placetype = {}
for placetype, capital_cat in pairs(m_placetypes.placetype_to_capital_cat) do
capital_cat_to_placetype[capital_cat] = placetype
end
-- Handler for bare categories for all types of capitals. This needs to precede the handler for bare placetype
-- categories as some of the types of capitals exist as placetypes as well.
insert(handlers, function(label)
label = lcfirst(label)
local capital_placetype = capital_cat_to_placetype[label]
if capital_placetype then
local pl_placetype = m_placetypes.pluralize_placetype(capital_placetype)
local linkdesc = m_placetypes.get_placetype_display_form(pl_placetype, "top-level")
if linkdesc == nil then
internal_error("Unrecognized placetype %s when processing label %s", capital_placetype, label)
end
if linkdesc == false then
mw.log(("Display form for pl_placetype %s is false, can't categorize"):format(dump(pl_placetype)))
return nil
end
return {
type = "name",
topic = label,
description = "{{{langname}}} names of [[capital]]s of " .. linkdesc .. ".",
parents = {"capital cities"},
}
end
end)
-- Handler for bare placetype categories. FIXME: Add wpcat= and commonscat= info. Previously we had it for various
-- so-called "generic" placetypes, but sometimes the categories were wrong.
insert(handlers, function(label)
for _, canon_label in ipairs { lcfirst(label), label } do
local ptdesc, ptdata = m_placetypes.get_placetype_display_form(canon_label, "top-level", "return full")
if ptdesc then
local from_category_props = {
from_category = true,
no_split_qualifiers = true,
}
local bare_category_parent = m_placetypes.get_equiv_placetype_prop(canon_label, function(pt)
local bare_category_parent = m_placetypes.get_placetype_prop(pt, "bare_category_parent")
if bare_category_parent then
return bare_category_parent
end
local class = m_placetypes.get_placetype_prop(pt, "class")
if class then
if class_to_bare_category_parent[class] == nil then
internal_error("Saw unknown category class %s derived from placetype %s",
class, canon_label)
end
return class_to_bare_category_parent[class]
end
end, from_category_props)
if not bare_category_parent then
internal_error("Saw placetype %s without a `class` or `bare_category_parent` setting, either " ..
"directly or through a fallback", canon_label)
end
local addl_bare_category_parents = m_placetypes.get_equiv_placetype_prop(canon_label, function(pt)
return m_placetypes.get_placetype_prop(pt, "addl_bare_category_parents")
end, from_category_props)
local bare_category_breadcrumb = m_placetypes.get_equiv_placetype_prop(canon_label, function(pt)
return m_placetypes.get_placetype_prop(pt, "bare_category_breadcrumb")
end, from_category_props)
if type(bare_category_parent) == "string" and bare_category_breadcrumb then
bare_category_parent = {name = bare_category_parent, sort = bare_category_breadcrumb}
end
local parents = {bare_category_parent}
if addl_bare_category_parents then
m_table.extend(parents, addl_bare_category_parents)
end
return {
type = "name",
topic = canon_label,
description = "{{{langname}}} " .. ptdesc .. ".",
breadcrumb = bare_category_breadcrumb,
parents = parents,
}
elseif ptdesc == false then
mw.log(("Display form for canon_label %s is false, can't categorize"):format(dump(canon_label)))
end
end
end)
local function fetch_primary_placetype(key, spec)
local placetype = spec.placetype
if type(placetype) == "table" then
placetype = placetype[1]
end
if not placetype then
internal_error("No placetype specified or defaulted for key %s, spec %s", key, spec)
end
return placetype
end
--[==[
Construct an appropriately linked location based on the full or elliptical placename, preceded by `"the "`` if
appropriate. Specifically:
Fetch the full and elliptical_placenames. If they are the same, just link to the placename directly. Otherwise, check if
the full placename exists; if so link to it. Otherwise, if the elliptical placename exists, link to it but display it as
the full placename. Finally, if neither full placename nor elliptical placename exists, fall back to linking to the full
placename. That way, we prefer full placenames to elliptical placenames if both or neither exist as Wiktionary entries,
but if only one exists, we link to that one rather than have a red link.
]==]
local function construct_linked_location(group, key, spec)
local full_placename, elliptical_placename = m_locations.key_to_placename(group, key)
local linked_placename
if elliptical_placename ~= full_placename then
local full_placename_title = mw.title.new(full_placename)
if full_placename_title and full_placename_title.exists then
linked_placename = m_locations.construct_linked_placename(spec, full_placename)
else
local elliptical_placename_title = mw.title.new(elliptical_placename)
if elliptical_placename_title and elliptical_placename_title.exists then
linked_placename = m_locations.construct_linked_placename(spec, elliptical_placename, full_placename)
end
end
end
return linked_placename or m_locations.construct_linked_placename(spec, full_placename)
end
--[==[
Construct the description of a location, including its container trail either to the end or until we encounter a
`no_include_container_in_desc` setting. For example, for the city of [[Birmingham]], the description will read
`"[[Birmingham]], a [[city]] in the [[West Midlands]] (which is a [[county]] of [[England]], which is a
[[constituent country]] of the [[United Kingdom]], which is a [[country]] in [[Europe]])"`. FIXME: Possibly we should
adopt the way city descriptions used to read, which was similar to `"the city of [[Birmingham]], in the county of the
[[West Midlands]], in the [[constituent country]] of [[England]], in the [[country]] of the [[United Kingdom]], in
[[Europe]]"`.
]==]
local function construct_location_desc(group, key, spec)
local parts = {}
local function ins(txt)
insert(parts, txt)
end
ins(construct_linked_location(group, key, spec))
local iteration = 0
local need_closing_paren = false
local containers = {{group = group, key = key, spec = spec}}
local container_iterator = m_locations.iterate_containers(group, key, spec)
while true do
iteration = iteration + 1
local include_container_in_desc = false
for _, container in ipairs(containers) do
if not container.spec.no_include_container_in_desc then
include_container_in_desc = true
break
end
end
if not include_container_in_desc then
break
end
local next_containers = container_iterator()
if not next_containers then
break
end
local is_former = nil
for _, container in ipairs(containers) do
local this_is_former = container.spec.is_former_place
if is_former == nil then
is_former = this_is_former
elseif is_former ~= this_is_former then
internal_error("When processing container trail of key %s, found a mixture of former and non-former " ..
"containers: %s", key, containers)
end
end
if #containers > 1 then
local placetypes = {}
local prepositions = {}
for _, container in ipairs(containers) do
local container_type = fetch_primary_placetype(container.key, container.spec)
m_table.insertIfNot(placetypes, m_placetypes.pluralize_placetype(container_type))
m_table.insertIfNot(prepositions, m_placetypes.get_placetype_entry_preposition(container_type))
end
if iteration == 1 then
ins(", ")
elseif iteration == 2 then
ins(" (which are ")
need_closing_paren = true
else
ins(", which are ")
end
if is_former then
ins("former ")
end
ins(m_table.serialCommaJoin(placetypes))
ins(" ")
ins(concat(prepositions, "/"))
else
if iteration == 1 then
ins(", ")
elseif iteration == 2 then
ins(" (which is ")
need_closing_paren = true
else
ins(", which is ")
end
local container_type = fetch_primary_placetype(containers[1].key, containers[1].spec)
if is_former then
ins("a former ")
else
ins(m_placetypes.get_placetype_article(container_type))
ins(" ")
end
ins(container_type)
ins(" ")
ins(m_placetypes.get_placetype_entry_preposition(container_type))
end
ins(" ")
first_container = false
containers = next_containers
local container_locations = {}
for _, container in ipairs(containers) do
insert(container_locations, construct_linked_location(container.group, container.key,
container.spec))
end
ins(m_table.serialCommaJoin(container_locations))
end
if need_closing_paren then
ins(")")
end
return concat(parts)
end
-- Fetch or construct the description of the location specified by `key`. If the `keydesc` property is specified,
-- use it directly but substitute any occurrence of `+++` with the auto-constructed location description, which
-- mentions the placename corresponding to the key, its placetype and container, and repeats the description up
-- the container trail until either there are no more containers or (more usually) the `no_include_container_in_desc`
-- setting is found (which is set on all continents and continent-level regions).
local function fetch_or_construct_location_desc(group, key, spec)
local val = spec.keydesc
if is_callable(val) then
val = val(group, key, spec)
spec.keydesc = val
end
val = val or "+++"
if val:find("%+%+%+") then
val = gsub_literally(val, "+++", construct_location_desc(group, key, spec))
end
return val
end
local function normalize_cat_as(cat_as, div)
if type(cat_as) ~= "table" or cat_as.type then
cat_as = {cat_as}
end
local ret_cat_as = {}
for _, pt_cat_as in ipairs(cat_as) do
if type(pt_cat_as) == "string" then
pt_cat_as = {type = pt_cat_as}
end
insert(ret_cat_as, {type = pt_cat_as.type, prep = pt_cat_as.prep or div.prep or "of"})
end
return ret_cat_as
end
-- Find the specified plural placetype among the divs for a given known location. Return a list of cat_as specs, where
-- each spec is of the form {type = "PLURAL_PLACETYPE", prep = "PREP"} indicating the plural placetype to use when
-- categorizing and the preposition to follow.
local function find_placetype_cat_as(divs, pl_placetype)
if divs then
if type(divs) ~= "table" then
divs = {divs}
end
for _, div in ipairs(divs) do
if type(div) == "string" then
div = {type = div}
end
if div.type == pl_placetype then
local cat_as = div.cat_as or div.type
return normalize_cat_as(cat_as, div)
end
end
end
return nil
end
-- Handler for bare placename categories for known locations in `locations` in [[Module:place/locations]].
insert(handlers, function(label)
for _, canon_label in ipairs { label, lcfirst(label) } do
local group, spec = m_locations.find_canonical_key(canon_label)
if group then
-- wp= defaults to true (Wikipedia article matches location's full placename)
local wp = spec.wp
if wp == nil then
wp = true
end
-- wpcat= defaults to wp= (if Wikipedia article has its own name, Wikipedia category and Commons category
-- generally follow)
local wpcat = spec.wpcat
if wpcat == nil then
wpcat = wp
end
-- commonscat= defaults to wpcat= (if Wikipedia category has its own name, Commons category generally
-- follows)
local commonscat = spec.commonscat
if commonscat == nil then
commonscat = wpcat
end
local parents = {}
local bare_label_parents = spec.overriding_bare_label_parents
local container_iterator = m_locations.iterate_containers(group, canon_label, spec)
local containers = container_iterator()
if not bare_label_parents then
bare_label_parents = {"+++"}
end
local full_location_placename, elliptical_location_placename = m_locations.key_to_placename(group, canon_label)
local full_container_placename
if containers then
full_container_placename, _ = m_locations.key_to_placename(containers[1].group, containers[1].key)
end
local inserted_containers = false
for _, parent in ipairs(bare_label_parents) do
if parent == "+++" then
parent = "PL_PLACETYPE PREP CONTAINER"
end
if parent:find("CONTAINER") then
if not containers then
internal_error("Parent category %s needs the container of %s but no containers specified: %s",
parent, canon_label, spec)
end
local location_type = fetch_primary_placetype(canon_label, spec)
local pl_location_type = m_placetypes.pluralize_placetype(location_type)
for _, container in ipairs(containers) do
local per_container_parent = parent
local cat_as_list
if per_container_parent:find("PL_PLACETYPE") then
if spec.bare_category_parent_type then
cat_as_list = normalize_cat_as(spec.bare_category_parent_type, spec)
else
cat_as_list = find_placetype_cat_as(container.spec.divs, pl_location_type) or
find_placetype_cat_as(container.spec.addl_divs, pl_location_type)
end
end
if not cat_as_list then
local canon_placetype, ptdata, ptmatch = m_placetypes.get_placetype_data(location_type, "from category")
if not canon_placetype or not (ptdata.generic_before_non_cities or ptdata.generic_before_cities) then
internal_error("Unable to locate plural location type %s among the divs or addl_divs " ..
"for container key %s spec %s, and the location type is either not in placetype_data or " ..
"not identified as a generic placetype", pl_location_type, container.key, container.spec)
end
cat_as_list = {{type = pl_location_type, prep =
m_placetypes.get_placetype_entry_preposition(location_type)}}
end
local prefixed_key = m_placetypes.get_prefixed_key(container.key, container.spec)
per_container_parent = gsub_literally(per_container_parent, "CONTAINER", prefixed_key)
for _, cat_as in ipairs(cat_as_list) do
local per_container_per_placetype_parent = per_container_parent
per_container_per_placetype_parent = gsub_literally(per_container_per_placetype_parent, "PL_PLACETYPE",
cat_as.type)
per_container_per_placetype_parent = gsub_literally(per_container_per_placetype_parent, "PREP",
cat_as.prep)
m_table.insertIfNot(parents, per_container_per_placetype_parent)
end
end
inserted_containers = true
else
m_table.insertIfNot(parents, parent)
end
end
if not inserted_containers and containers then
-- If we didn't insert the containers above in some form, insert them now as bare categories. Note that
-- this may be different categories from the container categories inserted above.
for _, container in ipairs(containers) do
m_table.insertIfNot(parents, container.key)
end
end
if spec.addl_parents then
for _, parent in ipairs(spec.addl_parents) do
m_table.insertIfNot(parents, parent)
end
end
local function format_boxval(val, specname)
if val == true then
val = "%l"
end
if type(val) == "string" then
val = gsub_literally(val, "%l", full_location_placename)
val = gsub_literally(val, "%e", elliptical_location_placename)
if val:find("%%c") then
if not full_container_placename then
internal_error("Wikipedia/Commons spec %s = %s has %%c in it but key %s has no " ..
"containers: %s", specname, val, canon_label, spec)
end
val = gsub_literally(val, "%c", full_container_placename)
end
end
return val
end
local description = spec.fulldesc or (
"{{{langname}}} terms related to the people, culture, or territory of " ..
fetch_or_construct_location_desc(group, canon_label, spec) .. ".")
local full_placename, _ = m_locations.key_to_placename(group, canon_label)
return {
type = "topic",
description = description,
breadcrumb = full_placename,
parents = parents,
wp = format_boxval(wp, "wp"),
wpcat = format_boxval(wpcat, "wpcat"),
commonscat = format_boxval(commonscat, "commonscat"),
}
end
end
end)
local function find_canonical_key_from_place(place, canon_label)
local has_the = false
local key
if place:find("^the ") then
key = place:gsub("^the ", "")
has_the = true
else
key = place
end
local group, spec = m_locations.find_canonical_key(key)
if group then
local requires_the = spec.the or false
if has_the ~= requires_the then
if has_the then
mw.log(("Mismatch in category name '%s', has 'the' in the category when it should not"):format(
canon_label))
else
mw.log(("Mismatch in category name '%s', should have 'the' in the category but does not"):
format(canon_label))
end
return nil
end
return group, key, spec
end
return nil
end
-- Handler for generic placetypes (those whose categories are added through category generation handlers or through
-- explicit category specs in the placetype data) for known locations in [[Module:place/locations]]. All such
-- placetypes have either a `generic_before_non_cities` setting (meaning they can occur before non-city locations) or
-- `generic_before_cities` setting (meaning they can occur before cities), or both. Examples of such categories are
-- "cities in the Bahamas" or "rivers in Western Australia, Australia", or (for city locations)
-- "neighbourhoods of Hong Kong" or "places in Melbourne".
insert(handlers, function(label)
for _, canon_label in ipairs { lcfirst(label), label } do
local placetype, in_of, place = canon_label:match("^([A-Za-z%- ]-) (in) (.*)$")
if not placetype then
placetype, in_of, place = canon_label:match("^([A-Za-z%- ]-) (of) (.*)$")
end
if placetype then
local normalized_placetype = placetype == "neighbourhoods" and "neighborhoods" or placetype
local canon_placetype, ptdata, ptmatch = m_placetypes.get_placetype_data(normalized_placetype, "from category")
if canon_placetype and (ptdata.generic_before_non_cities or ptdata.generic_before_cities) then
local group, key, spec = find_canonical_key_from_place(place, canon_label)
if group then
-- Check whether the location uses British spelling, but also check all containers, because
-- it's too hard to keep in sync the `british_spelling` setting for locations at all different
-- levels (e.g. cities of various countries, first and second level administrative division, etc.),
-- so we just set it at top level on the country.
local uses_british_spelling = spec.british_spelling
if uses_british_spelling == nil then
for containers in m_locations.iterate_containers(group, key, spec) do
local must_outer_break = false
for _, container in ipairs(containers) do
if container.spec.british_spelling ~= nil then
uses_british_spelling = container.spec.british_spelling
must_outer_break = true
break
end
end
if must_outer_break then
break
end
end
end
local allow_cat = true
if placetype == "neighborhoods" and uses_british_spelling or
placetype == "neighbourhoods" and not uses_british_spelling then
mw.log(("Mismatch in spelling of placetype '%s' in category '%s', should be '%s'"):format(
placetype, canon_label, uses_british_spelling and "neighbourhoods" or "neighborhoods"))
allow_cat = false
end
if spec.is_former_place and placetype ~= "places" then
allow_cat = false
end
local expected_prep
if spec.is_city then
expected_prep = ptdata.generic_before_cities
else
expected_prep = ptdata.generic_before_non_cities
end
if not expected_prep then
allow_cat = false
end
if allow_cat then
if expected_prep ~= in_of then
mw.log(("Mismatch in category name '%s', has '%s' when it should have '%s'"):format(
canon_label, in_of, expected_prep))
return nil
end
local linkdesc = m_placetypes.get_placetype_display_form(placetype,
spec.is_city and "city" or "noncity", "return full")
if linkdesc == false then
mw.log(("Display form for placetype %s is false, can't categorize"):format(dump(placetype)))
return nil
end
if not linkdesc then
internal_error("Unrecognized placetype %s when processing key %s, data %s, label %s",
placetype, key, spec, canon_label)
end
desc = linkdesc .. " " .. in_of .. " " .. fetch_or_construct_location_desc(group, key, spec)
desc = "{{{langname}}} " .. desc .. "."
local parents = {}
insert(parents, key)
if spec.no_container_parent then
-- top-level country, constituent country, continent or the like
insert(parents, {name = normalized_placetype, sort = key})
if spec.placetype == "country" or m_table.contains(spec.placetype, "country") then
local category_class = m_placetypes.get_equiv_placetype_prop(normalized_placetype,
function(pt) return m_placetypes.get_placetype_prop(pt, "class") end, {
from_category = true,
no_split_qualifiers = true,
})
if not category_class then
internal_error("Saw placetype %s that is either unknown or has no `class` " ..
"setting in `placetype_data`", normalized_placetype)
end
if class_is_political_division[category_class] == nil then
internal_error("Saw unknown category class %s derived from placetype %s",
category_class, normalized_placetype)
end
if class_is_political_division[category_class] then
insert(parents, "political divisions of specific countries")
end
end
else
local container_iterator = m_locations.iterate_containers(group, key, spec)
local next_containers = container_iterator()
if next_containers then
for _, container in ipairs(next_containers) do
local container_prep
if container.spec.is_city then
container_prep = ptdata.generic_before_cities
else
container_prep = ptdata.generic_before_non_cities
end
if not container_prep then
internal_error("For container key %s spec %s defines is_city = %s but " ..
"there is no corresponding `generic_before_*` setting in the " ..
"placedata for placetype %s", container.key, container.spec,
container.spec.is_city, placetype)
end
insert(parents, {
name = placetype .. " " .. container_prep .. " " .. m_placetypes.get_prefixed_key(
container.key, container.spec),
sort = key
})
end
else
-- unrecognized countries or the like
insert(parents, {name = normalized_placetype, sort = key})
end
end
return {
type = "name",
topic = canon_label,
description = desc,
breadcrumb = placetype,
parents = parents,
}
end
end
end
end
end
end)
-- Handler for "state capitals of the United States", "provincial capitals of Canada", etc. This must precede the next
-- handler for specific political and misc (non-political) divisions of polities and subpolities, such as
-- "provinces of the Philippines", because "departmental capitals" is listed in cat_as for French prefectures and so
-- will trigger an error if that handler runs before this one.
insert(handlers, function(label)
label = lcfirst(label)
local capital_cat, place = label:match("^([a-z%- ]- capitals) of (.*)$")
-- Make sure we recognize the type of capital.
if place and capital_cat_to_placetype[capital_cat] then
local placetype = capital_cat_to_placetype[capital_cat]
local pl_placetype = m_placetypes.pluralize_placetype(placetype)
-- Locate the container, fetch its known political divisions, and make sure the placetype corresponding to the
-- type of capital is among the list.
local group, key, spec = find_canonical_key_from_place(place, canon_label)
if group and (spec.divs or spec.addl_divs) then
local saw_match = false
local variant_matches = {}
local divlists = {}
if spec.divs then
insert(divlists, spec.divs)
end
if spec.addl_divs then
insert(divlists, spec.addl_divs)
end
for _, divlist in ipairs(divlists) do
if type(divlist) ~= "table" then
divlist = {divlist}
end
for _, div in ipairs(divlist) do
if type(div) == "string" then
div = {type = div}
end
-- HACK. Currently if we don't find a match for the placetype, we map e.g. 'autonomous region'
-- -> 'regional capitals' and 'union territory' -> 'territorial capitals'. When encountering a
-- political division like 'autonomous region' or 'union territory', chop off everything up
-- through a space to make things match. To make this clearer, we record all such
-- "variant match" cases, and down below we insert a note into the category text indicating that
-- such "variant matches" are included among the category.
if pl_placetype == div.type or pl_placetype == div.type:gsub("^.* ", "") then
saw_match = true
if pl_placetype ~= div.type then
insert(variant_matches, div.type)
end
end
end
end
if saw_match then
-- Everything checks out, construct the category description.
local placetype_desc = m_placetypes.get_placetype_display_form(pl_placetype,
placetype.is_city and "city" or "noncity")
if placetype_desc == false then
mw.log(("Display form for pl_placetype %s is false, can't categorize"):format(dump(pl_placetype)))
return nil
end
if not placetype_desc then
internal_error("Unrecognized plural placetype %s, generated as the plural of %s, which " ..
"was found as the placetype of capital placetype %s in label %s", pl_placetype,
placetype, capital_cat, label)
end
local variant_match_text = ""
if variant_matches[1] then
local real_variant_match_descs = {}
for i, variant_match in ipairs(variant_matches) do
local variant_match_desc = m_placetypes.get_placetype_display_form(variant_match,
placetype.is_city and "city" or "noncity")
if variant_match_desc == nil then
internal_error("Unrecognized variant match plural placetype %s, coming from " ..
"place key %s, data %s in label %s", variant_match, key, spec, label)
end
if variant_match_desc then
-- skip those for which the description is `false`, like `ABBREVIATION_OF states`
-- in the United States divs.
insert(real_variant_match_descs, variant_match_desc)
end
end
if real_variant_match_descs[1] then
variant_match_text = " (including " .. m_table.serialCommaJoin(real_variant_match_descs)
.. ")"
end
end
local desc = "{{{langname}}} names of [[capital]]s of " .. placetype_desc .. variant_match_text ..
" of " .. fetch_or_construct_location_desc(group, key, spec) .. "."
local full_placename, _ = m_locations.key_to_placename(group, key)
local parents = {}
if spec.no_container_parent then
-- top-level country, constituent country, continent or the like
insert(parents, {name = capital_cat, sort = key})
else
local container_iterator = m_locations.iterate_containers(group, key, spec)
local next_containers = container_iterator()
if next_containers then
for _, container in ipairs(next_containers) do
insert(parents, {
name = capital_cat .. " of " .. m_placetypes.get_prefixed_key(
container.key, container.spec),
sort = key
})
end
else
-- unrecognized countries or the like
insert(parents, {name = capital_cat, sort = key})
end
end
insert(parents, key)
return {
type = "name",
topic = label,
description = desc,
breadcrumb = full_placename,
parents = parents,
}
end
end
end
end)
local overriding_category_descriptions = {
["autonomous cities of Spain"] = "the [[w:Autonomous communities of Spain#Autonomous_cities|autonomous cities of Spain]]",
["regions of Greece"] = "the regions ([[periphery|peripheries]]) of [[Greece]]",
["regions of North Macedonia"] = "the regions ([[periphery|peripheries]]) of [[North Macedonia]]",
["subprefectures of Japan"] = "[[subprefecture]]s of [[Japan]]ese [[prefecture]]s",
}
-- Handler for specific political and misc (non-political) divisions of locations (polities, subpolities, cities, etc.),
-- such as "provinces of the Philippines", "counties of Wales", "municipalities of Tocantins, Brazil",
-- "boroughs of New York City", etc. This does not handle categories for generic placetypes (cities, rivers, etc.) of
-- locations, which are handled by different handlers above.
insert(handlers, function(label)
-- The label comes with an initial capitalization but we have to check both lowercase-initial and capital-initial
-- versions of the placetype to handle e.g. [[:Category:en:Indian reserves of Canada]].
for _, canon_label in ipairs { label, lcfirst(label) } do
for _, minimal_placetype in ipairs { true, false } do
local match_quantifier = minimal_placetype and "-" or "+"
-- Some categories have two "of"s in them, and depending on the category, it's correct to do either a greedy
-- ([[:Category:en:Abbreviations of states of the United States]], with placetype `abbreviations of states`)
-- or non-greedy ([[:Category:en:Provinces of the Democratic Republic of the Congo]], with placetype
-- `provinces`) match. We can't know in advance which is correct so we try both possibilities, doing the
-- non-greedy one first as it seems more common (there are many locations with "of" in them, but currently
-- only `abbreviations of states` occurs with a following location).
local placetype, in_of, place = canon_label:match("^([A-Za-z%- ]" .. match_quantifier .. ") (of) (.*)$")
if not placetype then
placetype, in_of, place = canon_label:match("^([A-Za-z%- ]" .. match_quantifier .. ") (in) (.*)$")
end
if placetype then
local group, key, spec = find_canonical_key_from_place(place, canon_label)
if group then
local function find_placetype(divs)
if divs then
if type(divs) ~= "table" then
divs = {divs}
end
for _, div in ipairs(divs) do
if type(div) == "string" then
div = {type = div}
end
local cat_as = div.cat_as or div.type
if type(cat_as) ~= "table" then
cat_as = {cat_as}
end
for _, pt_cat_as in ipairs(cat_as) do
if type(pt_cat_as) == "string" then
pt_cat_as = {type = pt_cat_as}
end
if placetype == pt_cat_as.type then
local div_parent = pt_cat_as.container_parent_type
if div_parent == nil then -- allow false
div_parent = div.container_parent_type
end
if div_parent == nil then
div_parent = placetype
end
return div_parent, pt_cat_as.prep or div.prep or "of"
end
end
end
end
return nil
end
local div_parent, div_prep = find_placetype(spec.divs)
if div_parent == nil then -- allow false
div_parent, div_prep = find_placetype(spec.addl_divs)
end
if div_parent == nil then -- allow false
div_parent, div_prep = find_placetype(spec.addl_divs_for_categorization)
end
if div_parent ~= nil then
if div_prep ~= in_of then
mw.log(("Mismatch in category name '%s', has '%s' when it should have '%s'"):format(
canon_label, in_of, div_prep))
return nil
end
local linkdesc = m_placetypes.get_placetype_display_form(placetype, spec.is_city and "city" or "noncity",
"return full")
if linkdesc == false then
mw.log(("Display form for placetype %s is false, can't categorize"):format(dump(placetype)))
return nil
end
if not linkdesc then
internal_error("Unrecognized placetype %s when processing key %s, data %s, label %s",
placetype, key, spec, canon_label)
end
local desc = overriding_category_descriptions[canon_label]
if not desc then
desc = linkdesc .. " " .. in_of .. " " .. fetch_or_construct_location_desc(group, key, spec)
end
desc = "{{{langname}}} " .. desc .. "."
local parents = {}
insert(parents, key)
if div_parent then -- div_parent may be `false`
if spec.no_container_parent then
-- top-level country, constituent country, continent or the like
insert(parents, {name = placetype, sort = " " .. key})
if spec.placetype == "country" or m_table.contains(spec.placetype, "country") then
insert(parents, "political divisions of specific countries")
end
else
local container_iterator = m_locations.iterate_containers(group, key, spec)
local next_containers = container_iterator()
if next_containers then
for _, container in ipairs(next_containers) do
insert(parents, {
name = div_parent .. " " .. in_of .. " " .. m_placetypes.get_prefixed_key(
container.key, container.spec),
sort = key
})
end
else
-- unrecognized countries or the like
insert(parents, {name = placetype, sort = " " .. key})
end
end
end
return {
type = "name",
topic = canon_label,
description = desc,
breadcrumb = placetype,
parents = parents,
}
end
end
end
end
end
end)
labels["exonyms"] = {
type = "name",
-- special-cased description
description = "{{{langname}}} [[exonym]]s.",
parents = {"places"},
}
labels["political divisions of specific countries"] = {
type = "grouping",
description = "{{{langname}}} categories for political divisions of specific countries.",
parents = {"places"},
}
-- Misc. FIXME: Remove the need for this.
labels["nomes of Ancient Egypt"] = {
type = "name",
-- special-cased description
description = "{{{langname}}} names of the [[nome]]s of [[Ancient Egypt]].",
breadcrumb = "nomes",
parents = {"Ancient Egypt"},
}
-- Sui generis place categories (mostly cross-jurisdictional subcontinental/transcontinental regions)
-- FIXME: Everything here has been moved from [[Module:category tree/topic/Earth]]. Most should be removed.
labels["Atlantic Ocean"] = {
type = "related-to",
description = "default with the",
parents = {"Earth"},
}
labels["British Isles"] = {
type = "related-to",
description = "=the people, culture, or territory of [[Great Britain]], [[Ireland]], and other nearby islands",
parents = {"Europe", "islands"},
}
labels["European Union"] = {
type = "related-to",
description = "default with the",
parents = {"Europe"},
}
labels["Gascony"] = {
type = "related-to",
parents = {"Occitania, France"},
}
labels["Indian subcontinent"] = {
type = "related-to",
description = "default with the",
parents = {"South Asia"},
}
labels["Bengal"] = {
type = "related-to",
description = "{{{langname}}} terms related to the people, culture, or territory of [[Bengal]].",
parents = {"Indian subcontinent"},
}
labels["Kashmir"] = {
type = "related-to",
description = "{{{langname}}} terms related to the people, culture, or territory of [[Kashmir]].",
parents = {"Indian subcontinent"},
}
labels["Kashmir, India"] = {
type = "related-to",
description = "{{{langname}}} names of places in {{w|Kashmir, India}}.",
parents = {"India", "Kashmir"},
}
labels["Korea"] = {
type = "related-to",
description = "=the people, culture, or territory of [[Korea]]",
parents = {"Asia"},
}
labels["Languedoc"] = {
type = "related-to",
parents = {"Occitania, France"},
}
labels["Lapland"] = {
type = "related-to",
description = "=[[Lapland]], a region in northernmost Europe",
parents = {"Europe", "Finland", "Norway", "Russia", "Sweden"},
}
labels["Middle East"] = {
type = "related-to",
description = "default with the",
parents = {"Africa", "Asia"},
}
labels["Netherlands Antilles"] = {
type = "related-to",
description = "=the people, culture, or territory of the [[Netherlands Antilles]]",
parents = {"Netherlands", "North America"},
}
labels["Overseas France"] = {
type = "related-to",
description = "={{w|Overseas France}} ({{m|fr||France d'outre-mer}})",
parents = {"France"},
}
labels["Provence"] = {
type = "related-to",
parents = {"Provence-Alpes-Côte d'Azur, France"},
}
labels["Polish People's Republic"] = {
type = "related-to",
parents = {"Poland"},
}
labels["South Asia"] = {
type = "related-to",
parents = {"Eurasia", "Asia"},
}
return {LABELS = labels, HANDLERS = handlers}
d3765m11xpzqpaf3g1rpd7tkzgydkgp
88789
88788
2026-08-06T18:21:36Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/topic/Places]]
88788
Scribunto
text/plain
local labels = {}
local handlers = {}
local m_table = require("Module:table")
local en_utilities_module = "Module:en-utilities"
local string_utilities_module = "Module:string utilities"
local m_locations = require("Module:place/locations")
local m_placetypes = require("Module:place/placetypes")
local placetype_data = m_placetypes.placetype_data
local internal_error = m_locations.internal_error
local dump = mw.dumpObject
local insert = table.insert
local concat = table.concat
local is_callable = require("Module:fun").is_callable
--[==[ intro:
This module is part of the category tree code and contains code to generate the descriptions of place-related categories
such as [[Category:de:Hokkaido Prefecture, Japan]], [[Category:es:Cities in France]],
[[Category:pt:Municipalities of Tocantins, Brazil]], etc.). Note that this module doesn't actually create the
categories; that must be done separately, with the text "{{tl|auto cat}}" as the definition of the category. (This
process should automatically happen periodically for non-empty categories, because they will appear in
[[Special:WantedCategories]] and a bot will periodically examine that list and create any needed category.)
There are two ways that category descriptions are specified: (1) by manually adding an entry to the `labels` table,
keyed by the label (the category minus the language code) with a value consisting of a Lua table specifying the
description text and the category's parents; (2) through handlers (pieces of Lua code) added to the `handlers` list,
which recognize labels of a specific type (e.g. `Cities in France`) and generate the appropriate specification for that
label on-the-fly.
See [[Module:place]] for an introduction to the terminology associated with places along with a list of all the relevant
modules, along with for more specific information on types of toponyms and placetypes and how their categorization
works.
]==]
local function lcfirst(label)
return mw.getContentLanguage():lcfirst(label)
end
local function gsub_literally(str, from, to)
local m_strutils = require(string_utilities_module)
return (str:gsub(m_strutils.pattern_escape(from), m_strutils.replacement_escape(to)))
end
local class_to_bare_category_parent = {
["polity"] = "polities",
["subpolity"] = "political divisions",
["settlement"] = "settlements",
["non-admin settlement"] = "settlements",
["capital"] = "capital cities",
["natural feature"] = "natural features",
["man-made structure"] = "man-made structures",
["geographic region"] = "geographic and cultural areas",
}
local class_is_political_division = {
["polity"] = true, -- strictly false but there are placetypes ambiguous between polity and subpolity
["subpolity"] = true,
["settlement"] = true,
["non-admin settlement"] = false,
["capital"] = true,
["natural feature"] = false,
["man-made structure"] = false,
["geographic region"] = false,
["generic place"] = false,
}
local capital_cat_to_placetype = {}
for placetype, capital_cat in pairs(m_placetypes.placetype_to_capital_cat) do
capital_cat_to_placetype[capital_cat] = placetype
end
-- Handler for bare categories for all types of capitals. This needs to precede the handler for bare placetype
-- categories as some of the types of capitals exist as placetypes as well.
insert(handlers, function(label)
label = lcfirst(label)
local capital_placetype = capital_cat_to_placetype[label]
if capital_placetype then
local pl_placetype = m_placetypes.pluralize_placetype(capital_placetype)
local linkdesc = m_placetypes.get_placetype_display_form(pl_placetype, "top-level")
if linkdesc == nil then
internal_error("Unrecognized placetype %s when processing label %s", capital_placetype, label)
end
if linkdesc == false then
mw.log(("Display form for pl_placetype %s is false, can't categorize"):format(dump(pl_placetype)))
return nil
end
return {
type = "name",
topic = label,
description = "{{{langname}}} names of [[capital]]s of " .. linkdesc .. ".",
parents = {"capital cities"},
}
end
end)
-- Handler for bare placetype categories. FIXME: Add wpcat= and commonscat= info. Previously we had it for various
-- so-called "generic" placetypes, but sometimes the categories were wrong.
insert(handlers, function(label)
for _, canon_label in ipairs { lcfirst(label), label } do
local ptdesc, ptdata = m_placetypes.get_placetype_display_form(canon_label, "top-level", "return full")
if ptdesc then
local from_category_props = {
from_category = true,
no_split_qualifiers = true,
}
local bare_category_parent = m_placetypes.get_equiv_placetype_prop(canon_label, function(pt)
local bare_category_parent = m_placetypes.get_placetype_prop(pt, "bare_category_parent")
if bare_category_parent then
return bare_category_parent
end
local class = m_placetypes.get_placetype_prop(pt, "class")
if class then
if class_to_bare_category_parent[class] == nil then
internal_error("Saw unknown category class %s derived from placetype %s",
class, canon_label)
end
return class_to_bare_category_parent[class]
end
end, from_category_props)
if not bare_category_parent then
internal_error("Saw placetype %s without a `class` or `bare_category_parent` setting, either " ..
"directly or through a fallback", canon_label)
end
local addl_bare_category_parents = m_placetypes.get_equiv_placetype_prop(canon_label, function(pt)
return m_placetypes.get_placetype_prop(pt, "addl_bare_category_parents")
end, from_category_props)
local bare_category_breadcrumb = m_placetypes.get_equiv_placetype_prop(canon_label, function(pt)
return m_placetypes.get_placetype_prop(pt, "bare_category_breadcrumb")
end, from_category_props)
if type(bare_category_parent) == "string" and bare_category_breadcrumb then
bare_category_parent = {name = bare_category_parent, sort = bare_category_breadcrumb}
end
local parents = {bare_category_parent}
if addl_bare_category_parents then
m_table.extend(parents, addl_bare_category_parents)
end
return {
type = "name",
topic = canon_label,
description = "{{{langname}}} " .. ptdesc .. ".",
breadcrumb = bare_category_breadcrumb,
parents = parents,
}
elseif ptdesc == false then
mw.log(("Display form for canon_label %s is false, can't categorize"):format(dump(canon_label)))
end
end
end)
local function fetch_primary_placetype(key, spec)
local placetype = spec.placetype
if type(placetype) == "table" then
placetype = placetype[1]
end
if not placetype then
internal_error("No placetype specified or defaulted for key %s, spec %s", key, spec)
end
return placetype
end
--[==[
Construct an appropriately linked location based on the full or elliptical placename, preceded by `"the "`` if
appropriate. Specifically:
Fetch the full and elliptical_placenames. If they are the same, just link to the placename directly. Otherwise, check if
the full placename exists; if so link to it. Otherwise, if the elliptical placename exists, link to it but display it as
the full placename. Finally, if neither full placename nor elliptical placename exists, fall back to linking to the full
placename. That way, we prefer full placenames to elliptical placenames if both or neither exist as Wiktionary entries,
but if only one exists, we link to that one rather than have a red link.
]==]
local function construct_linked_location(group, key, spec)
local full_placename, elliptical_placename = m_locations.key_to_placename(group, key)
local linked_placename
if elliptical_placename ~= full_placename then
local full_placename_title = mw.title.new(full_placename)
if full_placename_title and full_placename_title.exists then
linked_placename = m_locations.construct_linked_placename(spec, full_placename)
else
local elliptical_placename_title = mw.title.new(elliptical_placename)
if elliptical_placename_title and elliptical_placename_title.exists then
linked_placename = m_locations.construct_linked_placename(spec, elliptical_placename, full_placename)
end
end
end
return linked_placename or m_locations.construct_linked_placename(spec, full_placename)
end
--[==[
Construct the description of a location, including its container trail either to the end or until we encounter a
`no_include_container_in_desc` setting. For example, for the city of [[Birmingham]], the description will read
`"[[Birmingham]], a [[city]] in the [[West Midlands]] (which is a [[county]] of [[England]], which is a
[[constituent country]] of the [[United Kingdom]], which is a [[country]] in [[Europe]])"`. FIXME: Possibly we should
adopt the way city descriptions used to read, which was similar to `"the city of [[Birmingham]], in the county of the
[[West Midlands]], in the [[constituent country]] of [[England]], in the [[country]] of the [[United Kingdom]], in
[[Europe]]"`.
]==]
local function construct_location_desc(group, key, spec)
local parts = {}
local function ins(txt)
insert(parts, txt)
end
ins(construct_linked_location(group, key, spec))
local iteration = 0
local need_closing_paren = false
local containers = {{group = group, key = key, spec = spec}}
local container_iterator = m_locations.iterate_containers(group, key, spec)
while true do
iteration = iteration + 1
local include_container_in_desc = false
for _, container in ipairs(containers) do
if not container.spec.no_include_container_in_desc then
include_container_in_desc = true
break
end
end
if not include_container_in_desc then
break
end
local next_containers = container_iterator()
if not next_containers then
break
end
local is_former = nil
for _, container in ipairs(containers) do
local this_is_former = container.spec.is_former_place
if is_former == nil then
is_former = this_is_former
elseif is_former ~= this_is_former then
internal_error("When processing container trail of key %s, found a mixture of former and non-former " ..
"containers: %s", key, containers)
end
end
if #containers > 1 then
local placetypes = {}
local prepositions = {}
for _, container in ipairs(containers) do
local container_type = fetch_primary_placetype(container.key, container.spec)
m_table.insertIfNot(placetypes, m_placetypes.pluralize_placetype(container_type))
m_table.insertIfNot(prepositions, m_placetypes.get_placetype_entry_preposition(container_type))
end
if iteration == 1 then
ins(", ")
elseif iteration == 2 then
ins(" (which are ")
need_closing_paren = true
else
ins(", which are ")
end
if is_former then
ins("former ")
end
ins(m_table.serialCommaJoin(placetypes))
ins(" ")
ins(concat(prepositions, "/"))
else
if iteration == 1 then
ins(", ")
elseif iteration == 2 then
ins(" (which is ")
need_closing_paren = true
else
ins(", which is ")
end
local container_type = fetch_primary_placetype(containers[1].key, containers[1].spec)
if is_former then
ins("a former ")
else
ins(m_placetypes.get_placetype_article(container_type))
ins(" ")
end
ins(container_type)
ins(" ")
ins(m_placetypes.get_placetype_entry_preposition(container_type))
end
ins(" ")
first_container = false
containers = next_containers
local container_locations = {}
for _, container in ipairs(containers) do
insert(container_locations, construct_linked_location(container.group, container.key,
container.spec))
end
ins(m_table.serialCommaJoin(container_locations))
end
if need_closing_paren then
ins(")")
end
return concat(parts)
end
-- Fetch or construct the description of the location specified by `key`. If the `keydesc` property is specified,
-- use it directly but substitute any occurrence of `+++` with the auto-constructed location description, which
-- mentions the placename corresponding to the key, its placetype and container, and repeats the description up
-- the container trail until either there are no more containers or (more usually) the `no_include_container_in_desc`
-- setting is found (which is set on all continents and continent-level regions).
local function fetch_or_construct_location_desc(group, key, spec)
local val = spec.keydesc
if is_callable(val) then
val = val(group, key, spec)
spec.keydesc = val
end
val = val or "+++"
if val:find("%+%+%+") then
val = gsub_literally(val, "+++", construct_location_desc(group, key, spec))
end
return val
end
local function normalize_cat_as(cat_as, div)
if type(cat_as) ~= "table" or cat_as.type then
cat_as = {cat_as}
end
local ret_cat_as = {}
for _, pt_cat_as in ipairs(cat_as) do
if type(pt_cat_as) == "string" then
pt_cat_as = {type = pt_cat_as}
end
insert(ret_cat_as, {type = pt_cat_as.type, prep = pt_cat_as.prep or div.prep or "of"})
end
return ret_cat_as
end
-- Find the specified plural placetype among the divs for a given known location. Return a list of cat_as specs, where
-- each spec is of the form {type = "PLURAL_PLACETYPE", prep = "PREP"} indicating the plural placetype to use when
-- categorizing and the preposition to follow.
local function find_placetype_cat_as(divs, pl_placetype)
if divs then
if type(divs) ~= "table" then
divs = {divs}
end
for _, div in ipairs(divs) do
if type(div) == "string" then
div = {type = div}
end
if div.type == pl_placetype then
local cat_as = div.cat_as or div.type
return normalize_cat_as(cat_as, div)
end
end
end
return nil
end
-- Handler for bare placename categories for known locations in `locations` in [[Module:place/locations]].
insert(handlers, function(label)
for _, canon_label in ipairs { label, lcfirst(label) } do
local group, spec = m_locations.find_canonical_key(canon_label)
if group then
-- wp= defaults to true (Wikipedia article matches location's full placename)
local wp = spec.wp
if wp == nil then
wp = true
end
-- wpcat= defaults to wp= (if Wikipedia article has its own name, Wikipedia category and Commons category
-- generally follow)
local wpcat = spec.wpcat
if wpcat == nil then
wpcat = wp
end
-- commonscat= defaults to wpcat= (if Wikipedia category has its own name, Commons category generally
-- follows)
local commonscat = spec.commonscat
if commonscat == nil then
commonscat = wpcat
end
local parents = {}
local bare_label_parents = spec.overriding_bare_label_parents
local container_iterator = m_locations.iterate_containers(group, canon_label, spec)
local containers = container_iterator()
if not bare_label_parents then
bare_label_parents = {"+++"}
end
local full_location_placename, elliptical_location_placename = m_locations.key_to_placename(group, canon_label)
local full_container_placename
if containers then
full_container_placename, _ = m_locations.key_to_placename(containers[1].group, containers[1].key)
end
local inserted_containers = false
for _, parent in ipairs(bare_label_parents) do
if parent == "+++" then
parent = "PL_PLACETYPE PREP CONTAINER"
end
if parent:find("CONTAINER") then
if not containers then
internal_error("Parent category %s needs the container of %s but no containers specified: %s",
parent, canon_label, spec)
end
local location_type = fetch_primary_placetype(canon_label, spec)
local pl_location_type = m_placetypes.pluralize_placetype(location_type)
for _, container in ipairs(containers) do
local per_container_parent = parent
local cat_as_list
if per_container_parent:find("PL_PLACETYPE") then
if spec.bare_category_parent_type then
cat_as_list = normalize_cat_as(spec.bare_category_parent_type, spec)
else
cat_as_list = find_placetype_cat_as(container.spec.divs, pl_location_type) or
find_placetype_cat_as(container.spec.addl_divs, pl_location_type)
end
end
if not cat_as_list then
local canon_placetype, ptdata, ptmatch = m_placetypes.get_placetype_data(location_type, "from category")
if not canon_placetype or not (ptdata.generic_before_non_cities or ptdata.generic_before_cities) then
internal_error("Unable to locate plural location type %s among the divs or addl_divs " ..
"for container key %s spec %s, and the location type is either not in placetype_data or " ..
"not identified as a generic placetype", pl_location_type, container.key, container.spec)
end
cat_as_list = {{type = pl_location_type, prep =
m_placetypes.get_placetype_entry_preposition(location_type)}}
end
local prefixed_key = m_placetypes.get_prefixed_key(container.key, container.spec)
per_container_parent = gsub_literally(per_container_parent, "CONTAINER", prefixed_key)
for _, cat_as in ipairs(cat_as_list) do
local per_container_per_placetype_parent = per_container_parent
per_container_per_placetype_parent = gsub_literally(per_container_per_placetype_parent, "PL_PLACETYPE",
cat_as.type)
per_container_per_placetype_parent = gsub_literally(per_container_per_placetype_parent, "PREP",
cat_as.prep)
m_table.insertIfNot(parents, per_container_per_placetype_parent)
end
end
inserted_containers = true
else
m_table.insertIfNot(parents, parent)
end
end
if not inserted_containers and containers then
-- If we didn't insert the containers above in some form, insert them now as bare categories. Note that
-- this may be different categories from the container categories inserted above.
for _, container in ipairs(containers) do
m_table.insertIfNot(parents, container.key)
end
end
if spec.addl_parents then
for _, parent in ipairs(spec.addl_parents) do
m_table.insertIfNot(parents, parent)
end
end
local function format_boxval(val, specname)
if val == true then
val = "%l"
end
if type(val) == "string" then
val = gsub_literally(val, "%l", full_location_placename)
val = gsub_literally(val, "%e", elliptical_location_placename)
if val:find("%%c") then
if not full_container_placename then
internal_error("Wikipedia/Commons spec %s = %s has %%c in it but key %s has no " ..
"containers: %s", specname, val, canon_label, spec)
end
val = gsub_literally(val, "%c", full_container_placename)
end
end
return val
end
local description = spec.fulldesc or (
"{{{langname}}} terms related to the people, culture, or territory of " ..
fetch_or_construct_location_desc(group, canon_label, spec) .. ".")
local full_placename, _ = m_locations.key_to_placename(group, canon_label)
return {
type = "topic",
description = description,
breadcrumb = full_placename,
parents = parents,
wp = format_boxval(wp, "wp"),
wpcat = format_boxval(wpcat, "wpcat"),
commonscat = format_boxval(commonscat, "commonscat"),
}
end
end
end)
local function find_canonical_key_from_place(place, canon_label)
local has_the = false
local key
if place:find("^the ") then
key = place:gsub("^the ", "")
has_the = true
else
key = place
end
local group, spec = m_locations.find_canonical_key(key)
if group then
local requires_the = spec.the or false
if has_the ~= requires_the then
if has_the then
mw.log(("Mismatch in category name '%s', has 'the' in the category when it should not"):format(
canon_label))
else
mw.log(("Mismatch in category name '%s', should have 'the' in the category but does not"):
format(canon_label))
end
return nil
end
return group, key, spec
end
return nil
end
-- Handler for generic placetypes (those whose categories are added through category generation handlers or through
-- explicit category specs in the placetype data) for known locations in [[Module:place/locations]]. All such
-- placetypes have either a `generic_before_non_cities` setting (meaning they can occur before non-city locations) or
-- `generic_before_cities` setting (meaning they can occur before cities), or both. Examples of such categories are
-- "cities in the Bahamas" or "rivers in Western Australia, Australia", or (for city locations)
-- "neighbourhoods of Hong Kong" or "places in Melbourne".
insert(handlers, function(label)
for _, canon_label in ipairs { lcfirst(label), label } do
local placetype, in_of, place = canon_label:match("^([A-Za-z%- ]-) (in) (.*)$")
if not placetype then
placetype, in_of, place = canon_label:match("^([A-Za-z%- ]-) (of) (.*)$")
end
if placetype then
local normalized_placetype = placetype == "neighbourhoods" and "neighborhoods" or placetype
local canon_placetype, ptdata, ptmatch = m_placetypes.get_placetype_data(normalized_placetype, "from category")
if canon_placetype and (ptdata.generic_before_non_cities or ptdata.generic_before_cities) then
local group, key, spec = find_canonical_key_from_place(place, canon_label)
if group then
-- Check whether the location uses British spelling, but also check all containers, because
-- it's too hard to keep in sync the `british_spelling` setting for locations at all different
-- levels (e.g. cities of various countries, first and second level administrative division, etc.),
-- so we just set it at top level on the country.
local uses_british_spelling = spec.british_spelling
if uses_british_spelling == nil then
for containers in m_locations.iterate_containers(group, key, spec) do
local must_outer_break = false
for _, container in ipairs(containers) do
if container.spec.british_spelling ~= nil then
uses_british_spelling = container.spec.british_spelling
must_outer_break = true
break
end
end
if must_outer_break then
break
end
end
end
local allow_cat = true
if placetype == "neighborhoods" and uses_british_spelling or
placetype == "neighbourhoods" and not uses_british_spelling then
mw.log(("Mismatch in spelling of placetype '%s' in category '%s', should be '%s'"):format(
placetype, canon_label, uses_british_spelling and "neighbourhoods" or "neighborhoods"))
allow_cat = false
end
if spec.is_former_place and placetype ~= "places" then
allow_cat = false
end
local expected_prep
if spec.is_city then
expected_prep = ptdata.generic_before_cities
else
expected_prep = ptdata.generic_before_non_cities
end
if not expected_prep then
allow_cat = false
end
if allow_cat then
if expected_prep ~= in_of then
mw.log(("Mismatch in category name '%s', has '%s' when it should have '%s'"):format(
canon_label, in_of, expected_prep))
return nil
end
local linkdesc = m_placetypes.get_placetype_display_form(placetype,
spec.is_city and "city" or "noncity", "return full")
if linkdesc == false then
mw.log(("Display form for placetype %s is false, can't categorize"):format(dump(placetype)))
return nil
end
if not linkdesc then
internal_error("Unrecognized placetype %s when processing key %s, data %s, label %s",
placetype, key, spec, canon_label)
end
desc = linkdesc .. " " .. in_of .. " " .. fetch_or_construct_location_desc(group, key, spec)
desc = "{{{langname}}} " .. desc .. "."
local parents = {}
insert(parents, key)
if spec.no_container_parent then
-- top-level country, constituent country, continent or the like
insert(parents, {name = normalized_placetype, sort = key})
if spec.placetype == "country" or m_table.contains(spec.placetype, "country") then
local category_class = m_placetypes.get_equiv_placetype_prop(normalized_placetype,
function(pt) return m_placetypes.get_placetype_prop(pt, "class") end, {
from_category = true,
no_split_qualifiers = true,
})
if not category_class then
internal_error("Saw placetype %s that is either unknown or has no `class` " ..
"setting in `placetype_data`", normalized_placetype)
end
if class_is_political_division[category_class] == nil then
internal_error("Saw unknown category class %s derived from placetype %s",
category_class, normalized_placetype)
end
if class_is_political_division[category_class] then
insert(parents, "political divisions of specific countries")
end
end
else
local container_iterator = m_locations.iterate_containers(group, key, spec)
local next_containers = container_iterator()
if next_containers then
for _, container in ipairs(next_containers) do
local container_prep
if container.spec.is_city then
container_prep = ptdata.generic_before_cities
else
container_prep = ptdata.generic_before_non_cities
end
if not container_prep then
internal_error("For container key %s spec %s defines is_city = %s but " ..
"there is no corresponding `generic_before_*` setting in the " ..
"placedata for placetype %s", container.key, container.spec,
container.spec.is_city, placetype)
end
insert(parents, {
name = placetype .. " " .. container_prep .. " " .. m_placetypes.get_prefixed_key(
container.key, container.spec),
sort = key
})
end
else
-- unrecognized countries or the like
insert(parents, {name = normalized_placetype, sort = key})
end
end
return {
type = "name",
topic = canon_label,
description = desc,
breadcrumb = placetype,
parents = parents,
}
end
end
end
end
end
end)
-- Handler for "state capitals of the United States", "provincial capitals of Canada", etc. This must precede the next
-- handler for specific political and misc (non-political) divisions of polities and subpolities, such as
-- "provinces of the Philippines", because "departmental capitals" is listed in cat_as for French prefectures and so
-- will trigger an error if that handler runs before this one.
insert(handlers, function(label)
label = lcfirst(label)
local capital_cat, place = label:match("^([a-z%- ]- capitals) of (.*)$")
-- Make sure we recognize the type of capital.
if place and capital_cat_to_placetype[capital_cat] then
local placetype = capital_cat_to_placetype[capital_cat]
local pl_placetype = m_placetypes.pluralize_placetype(placetype)
-- Locate the container, fetch its known political divisions, and make sure the placetype corresponding to the
-- type of capital is among the list.
local group, key, spec = find_canonical_key_from_place(place, canon_label)
if group and (spec.divs or spec.addl_divs) then
local saw_match = false
local variant_matches = {}
local divlists = {}
if spec.divs then
insert(divlists, spec.divs)
end
if spec.addl_divs then
insert(divlists, spec.addl_divs)
end
for _, divlist in ipairs(divlists) do
if type(divlist) ~= "table" then
divlist = {divlist}
end
for _, div in ipairs(divlist) do
if type(div) == "string" then
div = {type = div}
end
-- HACK. Currently if we don't find a match for the placetype, we map e.g. 'autonomous region'
-- -> 'regional capitals' and 'union territory' -> 'territorial capitals'. When encountering a
-- political division like 'autonomous region' or 'union territory', chop off everything up
-- through a space to make things match. To make this clearer, we record all such
-- "variant match" cases, and down below we insert a note into the category text indicating that
-- such "variant matches" are included among the category.
if pl_placetype == div.type or pl_placetype == div.type:gsub("^.* ", "") then
saw_match = true
if pl_placetype ~= div.type then
insert(variant_matches, div.type)
end
end
end
end
if saw_match then
-- Everything checks out, construct the category description.
local placetype_desc = m_placetypes.get_placetype_display_form(pl_placetype,
placetype.is_city and "city" or "noncity")
if placetype_desc == false then
mw.log(("Display form for pl_placetype %s is false, can't categorize"):format(dump(pl_placetype)))
return nil
end
if not placetype_desc then
internal_error("Unrecognized plural placetype %s, generated as the plural of %s, which " ..
"was found as the placetype of capital placetype %s in label %s", pl_placetype,
placetype, capital_cat, label)
end
local variant_match_text = ""
if variant_matches[1] then
local real_variant_match_descs = {}
for i, variant_match in ipairs(variant_matches) do
local variant_match_desc = m_placetypes.get_placetype_display_form(variant_match,
placetype.is_city and "city" or "noncity")
if variant_match_desc == nil then
internal_error("Unrecognized variant match plural placetype %s, coming from " ..
"place key %s, data %s in label %s", variant_match, key, spec, label)
end
if variant_match_desc then
-- skip those for which the description is `false`, like `ABBREVIATION_OF states`
-- in the United States divs.
insert(real_variant_match_descs, variant_match_desc)
end
end
if real_variant_match_descs[1] then
variant_match_text = " (including " .. m_table.serialCommaJoin(real_variant_match_descs)
.. ")"
end
end
local desc = "{{{langname}}} names of [[capital]]s of " .. placetype_desc .. variant_match_text ..
" of " .. fetch_or_construct_location_desc(group, key, spec) .. "."
local full_placename, _ = m_locations.key_to_placename(group, key)
local parents = {}
if spec.no_container_parent then
-- top-level country, constituent country, continent or the like
insert(parents, {name = capital_cat, sort = key})
else
local container_iterator = m_locations.iterate_containers(group, key, spec)
local next_containers = container_iterator()
if next_containers then
for _, container in ipairs(next_containers) do
insert(parents, {
name = capital_cat .. " of " .. m_placetypes.get_prefixed_key(
container.key, container.spec),
sort = key
})
end
else
-- unrecognized countries or the like
insert(parents, {name = capital_cat, sort = key})
end
end
insert(parents, key)
return {
type = "name",
topic = label,
description = desc,
breadcrumb = full_placename,
parents = parents,
}
end
end
end
end)
local overriding_category_descriptions = {
["autonomous cities of Spain"] = "the [[w:Autonomous communities of Spain#Autonomous_cities|autonomous cities of Spain]]",
["regions of Greece"] = "the regions ([[periphery|peripheries]]) of [[Greece]]",
["regions of North Macedonia"] = "the regions ([[periphery|peripheries]]) of [[North Macedonia]]",
["subprefectures of Japan"] = "[[subprefecture]]s of [[Japan]]ese [[prefecture]]s",
}
-- Handler for specific political and misc (non-political) divisions of locations (polities, subpolities, cities, etc.),
-- such as "provinces of the Philippines", "counties of Wales", "municipalities of Tocantins, Brazil",
-- "boroughs of New York City", etc. This does not handle categories for generic placetypes (cities, rivers, etc.) of
-- locations, which are handled by different handlers above.
insert(handlers, function(label)
-- The label comes with an initial capitalization but we have to check both lowercase-initial and capital-initial
-- versions of the placetype to handle e.g. [[:Category:en:Indian reserves of Canada]].
for _, canon_label in ipairs { label, lcfirst(label) } do
for _, minimal_placetype in ipairs { true, false } do
local match_quantifier = minimal_placetype and "-" or "+"
-- Some categories have two "of"s in them, and depending on the category, it's correct to do either a greedy
-- ([[:Category:en:Abbreviations of states of the United States]], with placetype `abbreviations of states`)
-- or non-greedy ([[:Category:en:Provinces of the Democratic Republic of the Congo]], with placetype
-- `provinces`) match. We can't know in advance which is correct so we try both possibilities, doing the
-- non-greedy one first as it seems more common (there are many locations with "of" in them, but currently
-- only `abbreviations of states` occurs with a following location).
local placetype, in_of, place = canon_label:match("^([A-Za-z%- ]" .. match_quantifier .. ") (of) (.*)$")
if not placetype then
placetype, in_of, place = canon_label:match("^([A-Za-z%- ]" .. match_quantifier .. ") (in) (.*)$")
end
if placetype then
local group, key, spec = find_canonical_key_from_place(place, canon_label)
if group then
local function find_placetype(divs)
if divs then
if type(divs) ~= "table" then
divs = {divs}
end
for _, div in ipairs(divs) do
if type(div) == "string" then
div = {type = div}
end
local cat_as = div.cat_as or div.type
if type(cat_as) ~= "table" then
cat_as = {cat_as}
end
for _, pt_cat_as in ipairs(cat_as) do
if type(pt_cat_as) == "string" then
pt_cat_as = {type = pt_cat_as}
end
if placetype == pt_cat_as.type then
local div_parent = pt_cat_as.container_parent_type
if div_parent == nil then -- allow false
div_parent = div.container_parent_type
end
if div_parent == nil then
div_parent = placetype
end
return div_parent, pt_cat_as.prep or div.prep or "of"
end
end
end
end
return nil
end
local div_parent, div_prep = find_placetype(spec.divs)
if div_parent == nil then -- allow false
div_parent, div_prep = find_placetype(spec.addl_divs)
end
if div_parent == nil then -- allow false
div_parent, div_prep = find_placetype(spec.addl_divs_for_categorization)
end
if div_parent ~= nil then
if div_prep ~= in_of then
mw.log(("Mismatch in category name '%s', has '%s' when it should have '%s'"):format(
canon_label, in_of, div_prep))
return nil
end
local linkdesc = m_placetypes.get_placetype_display_form(placetype, spec.is_city and "city" or "noncity",
"return full")
if linkdesc == false then
mw.log(("Display form for placetype %s is false, can't categorize"):format(dump(placetype)))
return nil
end
if not linkdesc then
internal_error("Unrecognized placetype %s when processing key %s, data %s, label %s",
placetype, key, spec, canon_label)
end
local desc = overriding_category_descriptions[canon_label]
if not desc then
desc = linkdesc .. " " .. in_of .. " " .. fetch_or_construct_location_desc(group, key, spec)
end
desc = "{{{langname}}} " .. desc .. "."
local parents = {}
insert(parents, key)
if div_parent then -- div_parent may be `false`
if spec.no_container_parent then
-- top-level country, constituent country, continent or the like
insert(parents, {name = placetype, sort = " " .. key})
if spec.placetype == "country" or m_table.contains(spec.placetype, "country") then
insert(parents, "political divisions of specific countries")
end
else
local container_iterator = m_locations.iterate_containers(group, key, spec)
local next_containers = container_iterator()
if next_containers then
for _, container in ipairs(next_containers) do
insert(parents, {
name = div_parent .. " " .. in_of .. " " .. m_placetypes.get_prefixed_key(
container.key, container.spec),
sort = key
})
end
else
-- unrecognized countries or the like
insert(parents, {name = placetype, sort = " " .. key})
end
end
end
return {
type = "name",
topic = canon_label,
description = desc,
breadcrumb = placetype,
parents = parents,
}
end
end
end
end
end
end)
labels["exonyms"] = {
type = "name",
-- special-cased description
description = "{{{langname}}} [[exonym]]s.",
parents = {"places"},
}
labels["political divisions of specific countries"] = {
type = "grouping",
description = "{{{langname}}} categories for political divisions of specific countries.",
parents = {"places"},
}
-- Misc. FIXME: Remove the need for this.
labels["nomes of Ancient Egypt"] = {
type = "name",
-- special-cased description
description = "{{{langname}}} names of the [[nome]]s of [[Ancient Egypt]].",
breadcrumb = "nomes",
parents = {"Ancient Egypt"},
}
-- Sui generis place categories (mostly cross-jurisdictional subcontinental/transcontinental regions)
-- FIXME: Everything here has been moved from [[Module:category tree/topic/Earth]]. Most should be removed.
labels["Atlantic Ocean"] = {
type = "related-to",
description = "default with the",
parents = {"Earth"},
}
labels["British Isles"] = {
type = "related-to",
description = "=the people, culture, or territory of [[Great Britain]], [[Ireland]], and other nearby islands",
parents = {"Europe", "islands"},
}
labels["European Union"] = {
type = "related-to",
description = "default with the",
parents = {"Europe"},
}
labels["Gascony"] = {
type = "related-to",
parents = {"Occitania, France"},
}
labels["Indian subcontinent"] = {
type = "related-to",
description = "default with the",
parents = {"South Asia"},
}
labels["Bengal"] = {
type = "related-to",
description = "{{{langname}}} terms related to the people, culture, or territory of [[Bengal]].",
parents = {"Indian subcontinent"},
}
labels["Kashmir"] = {
type = "related-to",
description = "{{{langname}}} terms related to the people, culture, or territory of [[Kashmir]].",
parents = {"Indian subcontinent"},
}
labels["Kashmir, India"] = {
type = "related-to",
description = "{{{langname}}} names of places in {{w|Kashmir, India}}.",
parents = {"India", "Kashmir"},
}
labels["Korea"] = {
type = "related-to",
description = "=the people, culture, or territory of [[Korea]]",
parents = {"Asia"},
}
labels["Languedoc"] = {
type = "related-to",
parents = {"Occitania, France"},
}
labels["Lapland"] = {
type = "related-to",
description = "=[[Lapland]], a region in northernmost Europe",
parents = {"Europe", "Finland", "Norway", "Russia", "Sweden"},
}
labels["Middle East"] = {
type = "related-to",
description = "default with the",
parents = {"Africa", "Asia"},
}
labels["Netherlands Antilles"] = {
type = "related-to",
description = "=the people, culture, or territory of the [[Netherlands Antilles]]",
parents = {"Netherlands", "North America"},
}
labels["Overseas France"] = {
type = "related-to",
description = "={{w|Overseas France}} ({{m|fr||France d'outre-mer}})",
parents = {"France"},
}
labels["Provence"] = {
type = "related-to",
parents = {"Provence-Alpes-Côte d'Azur, France"},
}
labels["Polish People's Republic"] = {
type = "related-to",
parents = {"Poland"},
}
labels["South Asia"] = {
type = "related-to",
parents = {"Eurasia", "Asia"},
}
return {LABELS = labels, HANDLERS = handlers}
d3765m11xpzqpaf3g1rpd7tkzgydkgp
Mòideal:place/locations
828
17896
88790
2026-07-14T10:46:44Z
en>Juwan
0
[[Template talk:Bhutan - chiwogs]]
88790
Scribunto
text/plain
local export = {}
export.force_cat = false -- set to true to force category generation even on non-mainspace pages
local m_table = require("Module:table")
local string_utilities_module = "Module:string utilities"
local en_utilities_module = "Module:en-utilities"
local insert = table.insert
local concat = table.concat
local dump = mw.dumpObject
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
--[==[ intro:
This module contains data on all known locations, along with some lower-level code to process them (higher-level
known-location code is in [[Module:place/placetypes]]). You must load this module using require(), not using
mw.loadData().
===Location data===
'''NOTE: In order to understand the following better, first read the introductory documentation in [[Module:place]],
especially the section `More about known locations`.'''
The bulk of the code in this module (after some helper functions and placetype tables) describes the known locations
and their relationships. Locations are grouped into ''location groups'' that share some common properties (examples are
states of the United States and cities in Brazil). Each location group is associated with two tables, a ''data table''
that lists the locations and their individual properties, and a ''metadata table'' that lists group-level properties and
defaults for the location properties. Each metadata table points to the associated data table (i.e. contains the data
table as its `data` field), and the global `locations` variable holds a list of all group metadata tables. A given
location is generally described by three values: (a) the group metadata table for the group the location is part of; (b)
the location's canonical ''key'', which is the actual key in the group's data table and is globally unique across all
locations; and (c) the location's ''spec'', which is the initialized object describing the properties of the location
and comes from the value in the data table corresponding to the canonical key, transformed by the `initialize_spec()`
function. These are typically named `group`, `key` and `spec`, respectively and in that order, and are found in the
arguments to many functions.
In a per-group data table, the keys are either ''canonical keys'' describing locations (which, as mentioned above, must
be globally unique) or ''alias keys'' specifying an allowed alias for a given location. There may be multiple aliases
for a given location and the alias keys only need to be unique within a particular group data table, not across all
groups. It is also possible for the same string to serve as an alias key in one group and a canonical key in another
group. (For example, `Newcastle` appears as an alias key in two different groups, referring to two different locations,
canonically known as `Newcastle upon Tyne`, for the city in England, and `Newcastle, New South Wales`, for the city in
New South Wales, Australia; and `Birmingham` appears both as a canonical key in the group of English cities and an alias
key for canonical `Birmingham, Alabama` in the group of US cities.) The corresponding value objects are different for
canonical and alias keys. Corresponding to canonical keys are ''location specs'', describing the properies of the
location that cannot be derived from default properties of the group or global defaults. Corresponding to alias keys
are ''alias specs'', which are highly restricted in the properties they can contain, and whose properties do not have
per-group defaults, but only global defaults.
The canonical key is always the same as the bare category corresponding to the location, which is one of the reasons it
must be globally unique. For example, the country of Georgia uses the canonical key `Georgia` and corresponding bare
category [[:Category:Georgia]], while the US state of Georgia uses the canonical key `Georgia, USA` and corresponding
bare category [[:Category:Georgia, USA]]. The following conventions are followed in naming keys:
* Countries, ''country-like entities'' (which are a mixture of unrecognized de-facto states and dependent territories)
and ''former countries'' (which also includes other types of polities, such as the Roman Empire) use their unqualified
placename as the canonical key. (See the documentation for [[Module:place]] for the distinction between keys and
placenames, which is critical to understand when working with location data.) This also applies to constituent
countries (such as England, Aruba and the Faroe Islands) and constituent parts of grouped dependent territories (such
as the island of Saint Helena, which is administratively part of the British overseas territory of Saint Helena,
Ascension and Tristan da Cunha).
* Cities (including prefecture-level cities in China, which behave in most respects more like non-city administrative
divisions) also normally use their unqualified placename as the canonical key, but if this causes name conflicts or
ambiguities, they use a ''qualified key'' containing either the country name or immediate containing division (if
different) following a comma, such as the case of `Newcastle, New South Wales` and `Birmingham, Alabama` above.
Examples of name conflicts are the two cities just given; examples of ambiguities are the major cities of León and
Mérida in Mexico and city of Cartagena, Colombia, which are given the respective canonical keys of `León, Guanajuato`,
`Mérida, Yucatán` and `Cartagena, Colombia` to avoid ambiguity with the well-known respective cities of the same name
in Spain, even though none of those cities are large enough to be included as known locations in this module. (The
cutoff is generally having a metro area of at least 1,000,000 inhabitants, although there are exceptions.)
* Administrative divisions of countries, other than the exceptions noted above for constituent countries and dependent
territories, use a qualified key that contains the name of the country or constituent country in it, e.g.
`Normandy, France` (a region), `Calvados, France` (a department in the region of Normandy), `Herefordshire, England`
(a ceremonial county), `Northwest Territories, Canada` (a territory), `Central Finland, Finland` (a region),
`Antalya Province, Turkey` (a province), `Cluj County, Romania` (a county), `County Cork, Ireland` (a county) and
`New York, USA` (a state). As shown in these various examples, (a) first and second-level divisions are sometimes both
included (as in France, the United Kingdom and China); (b) the qualifier after the comma is sometimes a constituent
country (England) instead of a country (United Kingdom), and is sometimes abbreviated (USA rather than United States
or Unites States of America); (c) the word `the` is not normally included in the key even if the location is normally
preceded by `the` when following a preposition (there is a property in the location and alias specs to indicate this),
except in a very few cases (most notably `The Hague`); (d) the country is included as a qualifier even if it creates
an apparent redundancy, as with `Central Finland, Finland`; and (e) sometimes the placetype is included in the key, as
with provinces in Turkey and several other countries; states in Nigeria; and counties in Ireland, Romania and several
other countries. Whether the placetype is included, and whether it follows or precedes the placename, depends on
per-country conventions. For example, provinces in Turkey, Iran and several other countries (likewise for states in
Nigeria, oblasts in Russia, etc.) conventionally include the word "Province", "State", "Oblast" etc. in their name
because they are normally named after the largest city in the division, which would otherwise lead to ambiguity; and
counties in Ireland and Northern Ireland (and likewise County Durham, England) normally have the word "County"
preceding rather than following them in their conventional name, so we follow this practice. The Wikipedia article
naming scheme for a given administrative division is a strong clue as to how the division is normally referred to,
and we usually follow this practice. (A minor exception is that the Wikipedia articles for provinces in Iran, Laos and
Thailand include the word `province` with an initial lowercase letter while provinces elsewhere, e.g. North and South
Korea, Saudi Arabia and Turkey, use uppercase `Province`; we normalize to uppercase `Province` in all cases.)
As mentioned above, associated with canonical keys in the group data table are location specs, which are objects
containing properties. It is important here to distinguish ''initialized specs'' from ''uninitialized specs''.
Unininitialized specs are as directly specified in [[Module:place/locations]], containing only those properties that
differ from the per-group or global defaults. Initialized specs result from calling `initialize_spec()` on an
uninitialized spec (it is idempotent in that it will do nothing if encountering an already-initialized spec). This
copies all group-level defaults that are not overridden in the location spec itself from the group-level metadata table
into the location spec, so that in general, no more reference need be made to the group to fetch the correct value of a
given location property. (The initialization process also does more transformations in a few cases, noted below.) Note
that the default value of a given property is stored under a key in the group metadata table that is preceded by the
string `default_`; for example, the default value corresponding to the `placetype` property of a given location is
specified in the `default_placetype` key in the group metadata table.
The following are the properties of the location spec.
* `placetype`: String specifying the placetype of the location (e.g. "country", "state", province"). This can also be a
table of such types; in this case, the first listed type is the canonical type that will be used in descriptions, but
the location will be recognized (e.g. in a holonym, or for categorizing into the bare category) when tagged with any
of the specified types. The placetype '''must''' be either specified on an individual location or defaulted at the
group level, or an error occurs.
* `container`: Either a string, a ''canonicalized container'' structure or a list of either type, specifying the
immediate ''container'' (or containers) of the given location. A container is another location which this location is
considered to be directly part of, either politically or (above the country level) geographically. Some locations
belong to multiple immediate containers; this applies especially to transcontinental countries such as Russia and
Turkey. Containers can themselves have containers, forming a tree (or more correctly, a [[w:directed acyclic graph]])
of locations. The list of immediate container(s), followed by the container(s) of the container(s), etc., is termed
the ''container trail'', and some functions compute and return this trail as part of their operation. When a location
spec is initialized, the given container spec is canonicalized into ''canonical container form'', which consists of a
list of canonicalized container structures, each of which is of the form
`{key = "``container_key``", placetype = "``container_placetype``"}`, where ``container_key`` is a canonical location
key and ``container_placetype`` should be the listed placetype for the location, or the first listed placetype if
there are multiple. (FIXME: Since the key uniquely identifies the container location, we should eliminate the
placetype from the container structure.) The list of canonicalized container structures is stored into the
`.containers` field of the location spec (this happens even if the container value is unset in its uninitialized spec
form, causing it to default to the corresponding group-level value), and the `.container` field is set to {nil}. The
canonicalization process is described in more detail below under [[#Container spec canonicalization]].
* `divs`: List of recognized political divisions; e.g. for the Netherlands, a specification of the form
`divs = {"provinces", "municipalities"}` will allow categories such as [[:Category:de:Provinces of the Netherlands]]
and [[:Category:pt:Municipalities of the Netherlands]] to be created. Any division that appears here must also be
found in `placetype_data`, or an error occurs. The entities appearing in the `divs` list can be structures as well as
just strings; this is explained more below under [[#Location divisions]]. Additional political divisions that apply to
all locations in a group can be specified at the group level using the group-only property `addl_divs`, which has the
same format as `divs`. This is intended to be used in the situation where some division types are shared among all
locations in the group and others differ from location to location. An example where this is used is the United
States, where `census-designated places` is specified in the group-level `addl_divs` so that all 50 states have
census-designated places categorized as e.g. [[:Category:Census-designated places in Arizona, USA]], but `counties`
and `county seats` are specified in the group-level `default_divs` because not all states have counties and county
seats (Alaska has boroughs and borough seats and Louisiana has parishes and parish seats), and some states have
additional divisions (New Jersey and Pennsylvania also have boroughs, while Colorado and Connecticut have
municipalities). Note that under most circumstances (particularly, if `container_parent_type` is not set as a property
associated with the division type), any division type specified on a sub-country-level location must also be specified
on all containers up through the country. For example, since French departments specify `communes` and
`municipalities` in `default_divs`, the same division types must be (and are) specified on French regions and for
France itself.
* `keydesc`: String directly specifying a description of the location, for use in generating the contents of category
pages related to the location. In place of a string, a function of three arguments (`group`, `key`, `spec`, as is
normal for locations) that computes the location description can also be given. This is used, for example, for
Russian federal subjects; see `construct_russia_federal_subject_keydesc`. The special string `+++` contained in the
keydesc is replaced with the default value of the location description, which specifies the location's placename,
placetype, and the corresponding values for each container in the container trail, generally up through (but not
beyond) the country level; see `no_include_container_in_desc` below. The location description is used to construct
the full description of various categories, such as bare location categories, whose description generally reads
`"{{(((}}langname}}} terms related to the people, culture, or territory of ``keydesc``."` where ``keydesc`` is the
specified or auto-constructed location description.
* `fulldesc`: String overriding the full description for the bare location category (but not for any other category).
This is currently used only for the location `Earth`, at the very top of the tree (because the standard
`people, culture or territory of ...` text doesn't make sense here), and for `Antarctica` (because it has no permanent
inhabitants). FIXME: This should be renamed `bare_category_fulldesc`.
* `addl_parents`: Specify additional parents for the bare location category, in addition to the category or categories
generated based on the immediate container(s). For example, `Hawaii, USA` specifies `Polynesia` as an additional
parent category; both `North Korea` and `South Korea` specify `Korea` (which is a specially handled location category)
as an additional parent; and `Earth` specifies `nature` (not a location category, but still a topic category) as an
additional parent (which in this case becomes the first parent, as `Earth` has no container). The only restriction on
the categories in `addl_parents` is that they must be topic categories, because each language-specific version of the
bare location category gets the corresponding language-specific versions of the categories in `addl_parents`. FIXME:
This shoudl be renamed `bare_category_addl_parents`.
* `wp`: Spec describing how to construct the Wikipedia article for the location. Each spec is either `true` (equivalent
to `"%l"`, i.e. use the full location placename directly) or a string containing formatting directives, indicating how
to construct the article name. The allowed formatting directives are `%l` (the full location placename), `%e` (the
elliptical location placename) and `%c` (the full placename of the first immediate container). For example, the
default value of `wp` for the group of United States cities is `"%l, %c"` since the city articles tend to be named
e.g. `Austin, Texas` (but with many exceptions, specified using `wp` fields at the city level). Another example is
Thai provinces, which specify a group-level default of `"%e province"` as the Wikipedia articles have lowercase
`province` in their name but the Thai province keys specified in this module have uppercase `Province`. Here we have
to use `%e` to get the placename without the word `Province` in it. The default is `true`, which simply uses the full
location placename as the article name. Note that the Wikipedia article, along with the Wikipedia and Commons category
pages, are shown in the upper right of bare category pages.
* `wpcat`: Spec describing how to construct the Wikipedia category page for the location (i.e. the page listing articles
and categories relevant to the location). The format is the same as with `wp`, and it defaults to the value of `wp`.
It rarely needs to be specified because the category page and the article page almost always follow the same format.
* `commonscat`: Spec describing how to construct the Commons category page for the location (i.e. the page on the
MediaWiki Commons site listing articles and categories relevant to the location). It has the same format as `wp` and
`wpcat` and defaults to `wpcat`, which is usually (but not always) correct.
* `the`: Boolean specifying whether a location should be preceded by `the` when following a preposition, e.g. in
category names such as [[:Category:Cities in the Northern Territory, Australia]] and in old-style place descriptions
when the location occurs as the first holonym, such as the city [[Darwin]] described using
{{tl|place|city|terr/Northern Territory|c/Australia}}. Note that the global default for this and all Boolean
properties is {nil}, which amounts to the same as {false}.
* `british_spelling`: Boolean indicating whether the location in question uses British spelling. Currently this only
affects whether the spelling `neighborhoods` or `neighbourhoods` is used in categories such as
[[:Category:Neighborhoods of New York City]] and [[:Category:Neighbourhoods of Sydney]]. This usually needs to be set
only at the top level (i.e. country or country-like entity), because lower-level entities look up the container trail
for any container that has `british_spelling = true` set, and if found, assume that British spelling applies. The
general principle used in setting this is that all countries in Europe, all dependent territories of any such country,
all former British colonies, and any dependent territories of these former colonies, are assumed to use British
spelling, while all other countries and associated dependent territories are assumed to use American spelling. This
can potentially be modified on a case-by-case basis.
* `is_city`: Boolean indicating whether the location in question is a city. This is explicitly set to `true` for
city-states (e.g. Monaco and Vatican City), dependent territories that are cities (e.g. Hong Kong, Macau, Bonaire,
Gibraltar, etc.), certain city-level administrative divisions (such as `City of Belfast, Northern Ireland`) and
(through a group-levell setting) New York boroughs. In addition, it is set to `true` in initialize_spec() whenever
the group-level `default_placetype == "city"`, so that all cities get it set without explicitly needing to add a
group-level setting for this. Note that the condition `default_placetype == "city"` intentionally excludes Chinese
prefecture-level cities, which aren't really cities in that (for example) they don't directly contain neighborhoods,
but do contain cities within them. This setting is used in various places: (a) to add cities, rivers, etc. to
categories like [[:Category:Rivers in Osaka Prefecture, Japan]] and [[:Category:Cities in Wuhan]] for holonyms that
are ''not'' cities; (b) to add districts, neighborhoods, and the like to categories like
[[:Category:Neighborhoods of Brooklyn]] and [[:Category:Neighborhoods of Monaco]] for holoynms that ''are'' cities;
(c) generally, to determine which "generic" placetypes (cities, rivers, neighborhoods, etc.) apply to the location.
(Those that can occur with cities have a `generic_before_cities` setting in [[Module:place/placetypes]], and those
that can occur with non-cities have a `generic_before_non_cities` setting.)
* `is_former_place`: Boolean that should be set on former places such as the Soviet Union and the Roman Empire. For such
places, categories such as [[:Category:fr:Rivers in the Soviet Union]] are neither generated nor recognized (more
generally, no "generic" placetypes apply except for `places`), and category descriptions include the word `former`.
* `overriding_bare_label_parents`: Document me!
* `bare_category_parent_type`: Document me!
* `no_container_cat`: Document me!
* `no_container_parent`: Document me!
* `no_generic_place_cat`: Document me!
* `no_check_holonym_mismatch`: Document me!
* `no_auto_augment_container`: Document me!
* `no_include_container_in_desc`: Document me!
====Location divisions====
The `divs` field of a location describes the recognized political division types of that location. Specifying a given
division type will cause places defined as being of the specified division type and with the location as a holonym will
cause the place to be categorized as ` ``placetypes`` in/of ``location`` `; for example, specifying that the United
States has `"states"` as a division will cause anything defined as {{tl|place|fr|state|c/US}} to be categorized under
[[:Category:fr:States of the United States]]. Note that you do not have to explicitly specify division types for
"generic" placetypes (those that have a `generic_before_non_cities` field if the location is not a city, or that have a
`generic_before_cities` field if the location is a city); this includes things like cities, towns, villages,
neighbo(u)rhoods and rivers. A given element in the `divs` list is usually a string naming a plural placetype; the
placetype is automatically converted to the singular for recognizing the placetype in a {{tl|place}} spec, and irregular
plurals such as `kibbutzim` are handled correctly as long as the placetype specifies an appropriate `plural` field
(if the `plural` isn't explicitly given, the default singularization algorithm in [[Module:en-utilities]] is run, which
gets most things correctly but has problems with `passes` and `fortresses`, which are singularized to `passe` and
`fortresse`; for this reason, an explicit plural entry is added to terms in ''-ss''). In place of a string, an object
can be given with the plural placetype in the `type` field; this allows additional properties to be specified along with
the placetype. An example of this is the `divs` list for Canada:
{
["Canada"] = {divs = {
{type = "provinces", cat_as = "provinces and territories"},
{type = "territories", cat_as = "provinces and territories"},
"counties", "districts", "municipalities", "regional municipalities",
"rural municipalities", "parishes",
"Indian reserves",
"census divisions",
{type = "townships", prep = "in"},
}, ...},
}
Here, both provinces and territories are set to categorize as `provinces and territories`, meaning that there is a
single category [[:Category:Provinces and territories of Canada]] rather than separate categories for provinces and
territories. Similar things are done for other countries that have more than one type of first-level administrative
division (e.g. Australia, China, India and Pakistan). Note that any placetype listed under `cat_as` must exist in the
table of placetypes in [[Module:place/placetypes]], and in fact there is a category-only entry there for `provinces and
territories!` (the use of exclamation point following a plural placetype means that the placetype is present only for
use in categories and won't be recognized as the placetype field in a {{tl|place}} description). In addition, townships
are declared to use `in` rather than `of` as the preposition in the category; hence the category name will be
[[:Category:Townships in Canada]] rather than [[:Category:Townships of Canada]]. (The use of `in` vs. `of` is somewhat
related to whether a given placetype is an official administrative or statistical division of the location in question
and comes in a defined list, in which case `of` should be used, or is more ill-defined, in which case `in` should be
used; the default is `of`, and the use of `in` with `townships` is probably by analogy with the use of `in` with cities
and towns.)
Another more complex example is the divisions given for Quebec:
{
["Quebec, Canada"] = {divs = {
"counties",
{type = "regional county municipalities", container_parent_type = "regional municipalities"},
{type = "regions", container_parent_type = false},
{type = "townships", prep = "in"},
{type = "parish municipalities", cat_as = {{type = "parishes", container_parent_type = "counties"}, "municipalities"}},
{type = "township municipalities", cat_as = {{type = "townships", prep = "in"}, "municipalities"}},
{type = "village municipalities", cat_as = {{type = "villages", prep = "in"}, "municipalities"}},
}, ...},
}
Here, `container_parent_type` controls the second parent category of the placetype/location category associated with the
entry. In this case, for example, [[:Category:Counties of Quebec, Canada]] will have [[:Category:Counties of Canada]] as
its second or ''container-level'' parent. However, this doesn't make sense for `regional county municipalities`, which
exist only in Quebec (so the parent category [[:Category:Regional county municipalities of Canada]] would have only one
subcategory); but they are similar to regional municipalities in British Columbia, Nova Scotia and Ontario, so the
`container_parent_type = "regional municipalities"` spec causes the container-level parent of this category to be
[[:Category:Regional municipalities of Canada]]. Likewise, `regions` as administrative divisions (as opposed to mere
geographic regions) exist only in Quebec; they have no equivalent elsewhere, so we disable the container-level parent
using `container_parent_type = false`. The specs for `parish municipalities`, `township municipalities` and
`village municipalities` show both that multiple types can be specified under `cat_as` (here, for example, we categorize
`parish municipalities` as both `parishes` and `municipalities`) and that these types can themselves have properties,
just as for entries directly under `divs`. Specifically, `{type = "parishes", container_parent_type = "counties"}`
means that any place defined as a parish municipality in Quebec will be categorized under both [[:Category:Parishes of
Quebec, Canada]] and [[:Category:Municipalities of Quebec, Canada]], and that the former will have a container-level
parent of [[:Category:Counties of Canada]] (rather than the default of [[:Category:Parishes of Canada]]). Similarly,
`township municipalities` will be categorized under both [[:Category:Townships in Quebec, Canada]] (''not''
[[:Category:Townships of Quebec, Canada]]) and [[:Category:Municipalities of Quebec, Canada]].
====Container spec canonicalization====
A fully canonicalized container spec for a given location consists of a list of ''canonicalized container objects'',
each with a `key` and `placetype` field. The `key` field should name the canonical key of some other location at a
higher level (e.g. French cities are contained in French departments, which are contained in French regions, which are
contained in France, which is contained in Europe, which is contained in Eurasia, which is contained in the Earth). The
`placetype` field should correspond to the first (canonical) placetype listed for the key in question. The process of
initializing a locaion spec converts the container spec in `.container` into a canonicalized spec in `.containers` and
removes the spec from `.container`. It works as follows:
# If the `container` field is missing, and there is a group-level `default_container` field, it is used in its place.
For example, none of the Brazilian states listed in `brazil_states` specifies a container, but the group specifies
`default_container = "Brazil"`.
# A single string or canonicalized container object is allowed and made into a one-element list.
# If a list element is a string that did ''not'' come from `default_container`, and there is a group-level
`canonicalize_key_container` field, it is assumed to be a one-argument function and is called on the string to get
a canonicalized container object.
# Any remaining strings are assumed to be countries and are used directly as the `key`, with `placetype` set to
`"country"`.
====Alias keys====
Aliases can be provided for canonical keys using ''alias keys''. Alias keys have a very different location spec
structure from canonical keys. This structure does not, in general, have defaults at the group level and is not
initialized using `initialize_spec()`, but is used as-is. The following properties are recognized in an alias location
spec:
* `alias_of`: The canonical key of which this key is an alias. Required.
* `the`: If true, this alias key is preceded by `the` following a preposition. Defaults to the group-level `default_the`
but does not pay attention to the value of `the` for the corresponding canonical key.
* `display`: This is a display alias, meaning that holonyms using the placename corresponding to this alias will be
converted to the placename corresponding to the canonical key when formatting the holonym for display. (Otherwise,
the aliasing applies only to categorization.) If the value is true, the display canonicalization is to the placename
of the canonical key; otherwise, the value should be a key whose corresponding placename is used when display
canonicalizing.
* `placetype`: The placetype of the alias. Rarely needs to be specified as it defaults to the canonical key's placetype,
and if that is unspecified, to the group-level default placetype.
====Location group metadata tables====
As mentioned above, associated with each location group is a ''metadata table'' listing group-level properties. The
metadata table contains two types of keys: group-level defaults (named like the corresponding location-level keys but
preceded by `default_`, e.g. `default_placetype` corresponding to the location-level `placetype` key) and group-only
keys, which are mostly functions. The following are the possible group-only keys:
* `data`: This points to the group data table for the group, as described above.
* `key_to_placename`: This is a function of one argument to transform the location's key (whether canonical or alias)
into the full and elliptical placenames. The difference between full and elliptical placenames is described in the
documentation for [[Module:place]], but in essence, it applies for keys that include the placetype in them (e.g.
`Phuket Province, Thailand` or `County Mayo, Ireland`), in which case the full placename includes the placetype and
the elliptical placename does not. For keys that do not include the placetype in them (e.g. `Arizona, USA` or
`Gloucestershire, England`), the full and elliptical placenames are identical. Note that neither the full nor the
elliptical placename includes the container in it; hence, for `Phuket Province, Thailand`, the full placename is
`Phuket Province` and the elliptical placename is just `Phuket`. (Note that the full vs. elliptical placename
distinction is intended only for handling cases where the placetype follows or precedes the raw placename and there
is no difference between the two in whether they are normally preceded by `the`. More complex situations, such as
`State of Mexico` (which normally takes `the`) vs. just `Mexico` (which doesn't), or `Islamabad Capital Territory` vs.
just `Islamabad`, should be handled instead by aliases.) The `key_to_placename` function takes one argument, the key,
and returns two arguments, the full and elliptical placenames, respectively. If left undefined, the default is to
chop off anything starting with a comma and return the result as both full and elliptical placename, and if
specifically set to `false`, the key is used directly as both full and elliptical placename. If it needs to be
defined, it is best to use the helper function `make_key_to_placename`, if possible (or
`make_irish_type_key_to_placename` in the case of Ireland and Northern Ireland, where `County` precedes), rather than
rolling your own. In addition, you should use the global `key_to_placename` function (which takes care of the default
implementation and such) rather than directly calling the function in the `key_to_placename` field.
* `placename_to_key`: This is approximately the inverse of `key_to_placename`, transforming a placename (which can be
either in full or elliptical form) into the corresponding key. As with `key_to_placename`, if you need to define this
(generally, when the full and elliptical placenames are different), prefer using `make_placename_to_key` (or
`make_irish_type_placename_to_key` for Ireland and Northern Ireland) to rolling your own. In addition, similarly to
`key_to_placename`, use the global `placename_to_key` function to convert placenames to keys rather than directly
invoking the function in the `placename_to_key` field. If the field is set to `false`, the placename is used unchanged
as the key. Otherwise, the default algorithm works as follows:
*# If the group-level `default_placetype == "city"`, use the placename unchanged as the key.
*# Otherwise, if the group-level `default_container` exists and is a string, append it to the placename after a comma +
space and use the result as the key.
*# Otherwise, if the group-level `default_container` is a canonical container object (an object with `key` and
`placetype` fields), and the `placetype` field is either `country` or `constituent country`, append the `key` field
to the placename after a comma + space and use the result as the key.
*# Otherwise, use the placename unchanged as the key.
* `canonicalize_key_container`: A function of one argument to convert the specified `container` field, when a string,
to canonical form. Described in more detail above under [[#Container spec canonicalization]]. It is preferable to
construct the function using `make_canonicalize_key_container`, if possible, rather than rolling your own.
* `addl_divs`: Additional political divisions appended, for all locations in the group, to the list of divisions derived
from the location-level `divs` or group-level `default_divs` fields to get the final list of divisions for the
location. See [[#Location divisions]] for more details.
]==]
-----------------------------------------------------------------------------------
-- Helper functions --
-----------------------------------------------------------------------------------
--[==[
Throw an error. `fmt` is a format string and the remaining arguments are passed through `mw.dumpObject` and then used to
format the format string as if `fmt:format(...)` were called. In general, callers should use `internal_error` unless the
error was due to bad user input rather than a logic error (which usually isn't the case in deep back-end code like
this).
]==]
function export.process_error(fmt, ...)
local args = {...}
for i = 1, select("#", ...) do
args[i] = dump(args[i])
end
return error(string.format(fmt, unpack(args)))
end
--[==[
Throw an internal error (a logic error that should never happen unless there is a bug in the code, as opposed to a user
error triggered by bad input or a system error due to something like running out of memory or hitting a time limit).
`fmt` is a format string and the remaining arguments are passed through `mw.dumpObject` and then used to format the
format string as if `fmt:format(...)` were called.
]==]
function export.internal_error(fmt, ...)
export.process_error("Internal error: " .. fmt, ...)
end
local internal_error = export.internal_error
-- Return whether `list_or_element` (a list of strings, or a single string) "contains" `item` (a string). If
-- `list_or_element` is a list, this returns true if `item` is in the list; otherwise it returns true if `item`
-- equals `list_or_element`.
local function list_or_element_contains(list_or_element, item)
if type(list_or_element) == "table" then
return m_table.contains(list_or_element, item) and true or false
end
return list_or_element == item
end
--[==[
Call the location group's `key_to_placename` function if it exists (see the comment at the top of [[Module:place]] for
the distinction between keys and placenames). Two values are returned, the full and elliptical placenames (e.g. full
`"County Durham"` vs. elliptical `"Durham"`). If the group does not define `key_to_placename`, both full and elliptical
placenames are computed by chopping off anything starting with a comma.
]==]
function export.key_to_placename(group, key)
if group.key_to_placename == false then
return key, key
end
if group.key_to_placename then
local full_placename, elliptical_placename = group.key_to_placename(key)
if type(full_placename) ~= "string" then
internal_error("Key %s returned a non-string full placename: %s", key, full_placename)
end
if type(elliptical_placename) ~= "string" then
internal_error("Key %s returned a non-string elliptical placename: %s", key, elliptical_placename)
end
return full_placename, elliptical_placename
end
key = key:gsub(",.*", "")
return key, key
end
--[==[
Call the location group's `placename_to_key` function if it exists (see the comment at the top of [[Module:place]] for
the distinction between keys and placenames) and return the result. If `placename_to_key` exists with the value `false`,
return the placename unchanged. If the group does not define `placename_to_key`, and it defines a `default_container`
whose placetype is either `country` or `constituent country`, the container name is appended to the placename after a
comma and a space. Otherwise the placename is returned unchanged.
]==]
function export.placename_to_key(group, placename)
if group.placename_to_key == false then
return placename
elseif group.placename_to_key then
local key = group.placename_to_key(placename)
if type(key) ~= "string" then
internal_error("Placename %s returned a non-string key: %s", placename, key)
end
return key
elseif group.default_placetype == "city" then
return placename
else
local defcon = group.default_container
if not defcon then
return placename
elseif type(defcon) == "string" then
return placename .. ", " .. defcon
elseif type(defcon) == "table" and (defcon.placetype == "country" or
defcon.placetype == "constituent country") then
return placename .. ", " .. defcon.key
else
return placename
end
end
end
--[==[
Initialize the location spec `spec`, augmenting it with default values taken from `group` if the spec itself doesn't
specify values for the properties. This sets `containers` to a canonicalized list of objects, each with `key` and
`placetype` keys, describing the immediate containers of the location, and erases (sets to nil) the original
non-canonicalized `container` field. (Most locations have only one immediate container but some, e.g. Russia, have more
than one. Containers should be carefully distinguished from category parents. Generally the container is the first
category parent, or the first ``n`` parents if there are ``n`` containers, but there may be additional category parents,
which indicate some sort of relation between the category parent and the location but not necessarily one of
containment.)
This function is idempotent in that nothing happens if called more than once on the same spec.
FIXME: Consider reimplementing this in a more standardly object-oriented way using metatables.
]==]
function export.initialize_spec(group, key, spec)
if spec.initialized then
return
end
local container = spec.container
local containers
local container_from_default
if not container then
container = group.default_container
container_from_default = true
end
if container then
if type(container) == "string" or container.key then
container = {container}
end
containers = {}
for _, cont in ipairs(container) do
if type(cont) == "string" then
if group.canonicalize_key_container and not container_from_default then
cont = group.canonicalize_key_container(cont)
else
cont = {key = cont, placetype = "country"}
end
end
insert(containers, cont)
end
end
spec.containers = containers
spec.container = nil
local function value_with_default(val, default_val)
if val == nil then
return default_val
else
return val
end
end
local function set_or_default(prop)
spec[prop] = value_with_default(spec[prop], group["default_" .. prop])
end
set_or_default("placetype")
if not spec.placetype then
internal_error("No placetype found in key %s for spec %s or in group `default_placetype`", key, spec)
end
set_or_default("divs")
spec.addl_divs = group.addl_divs
for _, prop in ipairs {
"keydesc",
"fulldesc",
"addl_parents",
"overriding_bare_label_parents",
"bare_category_parent_type",
"wp",
"wpcat",
"commonscat",
"british_spelling",
"the",
"no_container_cat",
"no_container_parent",
"no_generic_place_cat",
"no_check_holonym_mismatch",
"no_auto_augment_container",
"no_include_container_in_desc",
"is_city",
"is_former_place",
} do
set_or_default(prop)
end
-- `default_placetype == "city"` is correct; if `default_placetype` has something else like `prefecture-level city`
-- as the canonical placetype but also lists `city` (as Chinese prefecture-level cities do), don't mark as
-- is_city.
spec.is_city = value_with_default(spec.is_city, group.default_placetype == "city")
spec.initialized = true
end
--[=[
Given a location group, key and possible placetypes that the placename must match, check if the key exists in the group
with at least one of the group's key's placetypes matching one of the passed-in placetypes. If so, return two values:
the group key (which potentially could differ from the passed-in key due to aliases) and the corresponding spec object,
which (as with all functions that return spec objects) has been initialized using `initialize_spec()` (i.e. default
property values have been copied from the group into the spec, if the spec doesn't itself specify a value for the
property in question).
`alias_resolution` controls how aliases are resolved. Normally, both display and category aliases are followed, and
the returned key will reflect the canonical location key. However, if `alias_resolution` is {"none"}, no alias following
happens. In that case, if the key specifies an alias, the spec for the alias rather than the spec for the canonical
location is returned, and importantly, it is returned uninitialized, meaning that properties from the group are not
copied into the spec. (If the key specifies a canonical location, its spec is returned initialized, as in the normal
case where `alias_resolution` is unspecified.) The caller needs to check whether the returned spec is an alias by
looking for an `alias_of` property. If `alias_resolution` is {"display"}, the behavior is the same as for {"none"}
except that if the alias contains a setting `display = true`, the returned key will reflect the canonical location key,
and if the alias contains a setting `display = ``string`` `, the returned key will reflect that string.
This is a low-level function meant for internal use; external callers should generally use `get_matching_location` (for
internally-derived locations), `find_matching_holonym_location` (for externally-derived locations) or
`find_canonical_key` (for known-canonical locations where the placetype isn't known).
]=]
local function find_matching_key_in_group(group, placetypes, key, alias_resolution)
if alias_resolution ~= nil and alias_resolution ~= "none" and alias_resolution ~= "display" and
alias_resolution ~= "all" then
internal_error("Bad value for 'alias_resolution': %s", alias_resolution)
end
local spec = group.data[key]
if not spec then
return nil
end
local function check_correct_placetype(placetype)
if type(placetype) == "table" then
for _, pt in ipairs(placetype) do
if list_or_element_contains(placetypes, pt) then
return true
end
end
return false
else
return list_or_element_contains(placetypes, placetype)
end
end
if spec.alias_of then
local resolved_key = spec.alias_of
local resolved_spec = group.data[resolved_key]
if not resolved_spec then
internal_error("Key %s is an alias of %s, which doesn't exist", key, resolved_key)
elseif resolved_spec.alias_of then
internal_error("Key %s is an alias of %s, which is itself an alias; indirect aliasing not allowed",
key, resolved_key)
end
if alias_resolution == "none" or alias_resolution == "display" then
-- We could be working with non-initialized/defaulted spec, since we're pulling it directly from the group.
local placetype = spec.placetype or resolved_spec.placetype or group.default_placetype
if not placetype then
internal_error("No placetype found for key %s in any of spec %s, alias-resolved spec %s or in group " ..
"`default_placetype`", key, spec, resolved_spec)
end
if not check_correct_placetype(placetype) then
return nil
end
if alias_resolution == "display" then
if spec.display == true then
key = resolved_key
elseif spec.display then
key = spec.display
end
end
return key, spec
end
key = resolved_key
spec = resolved_spec
end
-- We could be working with non-initialized/defaulted spec, since we're pulling it directly from the group.
local placetype = spec.placetype or group.default_placetype
if not placetype then
internal_error("No placetype found for key %s in spec %s or group `default_placetype`", key, spec)
end
if not check_correct_placetype(placetype) then
return nil
end
export.initialize_spec(group, key, spec)
return key, spec
end
--[=[
Given a location group, placename and possible placetypes that the placename must match, check if the placename exists
in the group with at least one of the placetypes of the key in the group that corresponds to the placename matching one
of the passed-in placetypes. If so, return two values: the key corrsponding to the passed-in placename and the
corresponding spec object. This is similar to `find_matching_key_in_group()` but works with placenames rather than keys.
`alias_resolution` is as in `find_matching_key_in_group()`.
This is a low-level function meant for internal use; external callers should generally use `get_matching_location` (for
internally-derived locations), `find_matching_holonym_location` (for externally-derived locations) or
`find_canonical_key` (for known-canonical locations where the placetype isn't known).
]=]
local function find_matching_placename_in_group(group, placetypes, placename, alias_resolution)
local key = export.placename_to_key(group, placename)
return find_matching_key_in_group(group, placetypes, key, alias_resolution)
end
--[==[
If `key` is a canonical known location key (i.e. not an alias), return the corresponding group and initialized spec.
If no such key exists, return {nil}. This throws an internal error if two locations with the same key are found.
]==]
function export.find_canonical_key(key)
local found_locations = {}
for _, group in ipairs(export.locations) do
local spec = group.data[key]
if not spec then
-- do nothing
elseif spec.alias_of then
mw.log(("Skipping alias '%s' of canonical '%s'"):format(key, spec.alias_of))
else
insert(found_locations, {group, spec})
end
end
if not found_locations[1] then
return nil
elseif found_locations[2] then
internal_error("Found multiple matching locations for canonical key %s: %s", key, found_locations)
else
local group, spec = unpack(found_locations[1])
export.initialize_spec(group, key, spec)
return group, spec
end
end
--[==[
Iterator that returns all locations matching a given description, where the description consists of either a placename
or a key along with a list of possible placetypes. Usually there will be at most one such location. The iterator
returns three values at each iteration: the location group, canonical key by which the location is known and the spec
object describing the location. `data` contains the following possible fields:
* `placetypes`: A list of possible placetypes, one of which must match one of the location's placetypes; or a string
specifying a placetype, which must match one of the location's placetypes. This must be specified.
* `placename`: The placename of the location. Either this or `key` must be specified.
* `key`: The key of the location. Either this or `placename` must be specified.
* `alias_resolution`: If specified, it behaves the same as for `find_matching_key_in_group`.
The spec is normally initialized using `initialize_spec()` prior to it being returned (but may not be if
`alias_resolution` is given and the specified key or placename is an alias; see the documentation for
`find_matching_key_in_group`).
]==]
function export.iterate_matching_location(data)
local i = 0
local n = #export.locations
return function()
while true do
i = i + 1
if i > n then
break
end
local group = export.locations[i]
local key, spec
if data.placename then
key, spec = find_matching_placename_in_group(group, data.placetypes, data.placename,
data.alias_resolution)
else
if not data.key then
internal_error("'.placename' or '.key' must be defined: %s", data)
end
key, spec = find_matching_key_in_group(group, data.placetypes, data.key, data.alias_resolution)
end
if key then
return group, key, spec
end
end
end
end
--[==[
Return the location matching a given description, where the description consists of either a placename or a key along
with a list of possible placetypes. This is similar to `iterate_matching_location()` but throws an internal error if
there is not exactly one location found; as such, it is for use with internally specified locations (such as the
containers of known locations) rather than externally specified locations, which may not match a known location and in
some cases may match multiple known locations. For finding an externally specified location, consider using
`find_matching_holonym_location`, which returns {nil} rather than throwing an error if the location isn't found, but
also (more importantly) checks to make sure there are no conflicting holonyms among the user-specified holonyms (e.g.
{{tl|place|city|s/Delaware|c/USA|t=Newark}} will not match the known location `Newark` (in New Jersey, not Delaware).
]==]
function export.get_matching_location(data)
local all_found = {}
for group, key, spec in export.iterate_matching_location(data) do
insert(all_found, {group, key, spec})
end
if not all_found[1] then
internal_error("Couldn't find matching location for data %s", data)
elseif all_found[2] then
internal_error("Found multiple matching locations for data %s: %s", data, all_found)
else
return unpack(all_found[1])
end
end
--[==[
Successively iterate over a location's containers, and then the containers of those containers, etc. Keep in mind that
locations may have multiple containers (e.g. Russia has both Europe and Asia as containers, and both Europe and Asia
have Eurasia as their container). A given container will never be returned twice (e.g. in the case where a specific
location A has locations B and C as containers, and B has C as its container, C will not be returned twice). An
internal error happens if a container loop is detected. The return value is a list of location objects, each of which
contains `group`, `key` and `spec` fields.
]==]
function export.iterate_containers(group, key, spec)
local keys_seen = {}
keys_seen[key] = true
local iterations = 0
local last_iteration_containers = {{group = group, key = key, spec = spec}}
return function()
iterations = iterations + 1
if iterations > 10 then
internal_error("Probable loop in containers when processing key %s", key)
end
local next_iteration_containers = {}
for _, location in ipairs(last_iteration_containers) do
local containers = location.spec.containers
if containers then
for _, container in ipairs(containers) do
local container_group, container_key, container_spec = export.get_matching_location {
placetypes = container.placetype,
key = container.key,
}
if not keys_seen[container_key] then
insert(next_iteration_containers, {
group = container_group, key = container_key, spec = container_spec
})
keys_seen[container_key] = true
end
end
end
end
if not next_iteration_containers[1] then
return nil
end
last_iteration_containers = next_iteration_containers
return next_iteration_containers
end
end
--[==[
Given a placename, convert it into a link (two-part if `display_form` is given and differs from `placename`) and add
`"the "` to the beginning if called for in `spec`.
]==]
function export.construct_linked_placename(spec, placename, display_form)
local linked_placename = display_form and placename ~= display_form and ("[[%s|%s]]"):format(placename,
display_form) or ("[[%s]]"):format(placename)
if spec.the then
linked_placename = "the " .. linked_placename
end
return linked_placename
end
--[=[
This is typically used to define `key_to_placename`. It generates a function that chops off parts of a string (a
location key), typically at the end, in order to get the full and elliptical versions of a placename. (See the
documentation above for `key_to_placename` under "Location group tables" for the difference between full and elliptical
placenames.) `container_patterns` is a Lua pattern or a list of possible patterns matching the container at the end of
the key, which will be used to remove that container. If multiple patterns are specified, each one is tried until one
matches. If `container_patterns` is omitted, this part of the process is skipped. The reulting string becomes the full
placename. If `divtype_patterns` is specified, it is likewise either a Lua pattern or list of possible patterns to match
and remove the political division affixed onto the end (or possibly the beginning) of the key in the keys of certain
countries (such as South Korean and North Korean counties, which include the word "County" in the key). The resulting
chopped string becomes the elliptical placename. If `divtype_patterns` is omitted, this part of the process is skipped
and the full and elliptical placenames are the same.
Typical usage is as follows:
```
key_to_placename = make_key_to_placename(", England$"),
```
or (when the political division is part of the key)
```
key_to_placename = make_key_to_placename(", South Korea$", " County$")
```
]=]
local function make_key_to_placename(container_patterns, divtype_patterns)
if type(container_patterns) == "string" then
container_patterns = {container_patterns}
end
if type(divtype_patterns) == "string" then
divtype_patterns = {divtype_patterns}
end
return function(key)
local full_placename = key
if container_patterns then
for _, container_pattern in ipairs(container_patterns) do
local nsubs
full_placename, nsubs = full_placename:gsub(container_pattern, "")
if nsubs > 0 then
break
end
end
end
local elliptical_placename = full_placename
if divtype_patterns then
for _, divtype_pattern in ipairs(divtype_patterns) do
local nsubs
elliptical_placename, nsubs = elliptical_placename:gsub(divtype_pattern, "")
if nsubs > 0 then
break
end
end
end
return full_placename, elliptical_placename
end
end
--[=[
This is typically used to define `placename_to_key`. It generates a function that appends a string to the end of a given
placename to get the key (see the definition of `placename_to_key` above in the documentation under "Location group
tables"). Optional `divtype_suffix` is a raw string (which should not contain hyphens or other characters that have
special meaning in Lua patterns) to be appended first to the placename; if already present at the end, it is not
appended. `container_suffix` is then added in the same fashion if given. Typical usage is like this:
```
placename_to_key = make_placename_to_key(", England")
```
(which will convert e.g. `"Hampshire"` into `"Hampshire, England"`)
or
```
placename_to_key = make_placename_to_key(", South Korea", " County")
```
(which will convert e.g. `"Gangwon"` or `"Gangwon County"` into `"Gangwon County, South Korea"`).
]=]
local function make_placename_to_key(container_suffix, divtype_suffix)
return function(placename)
local key = placename
if divtype_suffix then
if not key:find(divtype_suffix .. "$") then
key = key .. divtype_suffix
end
end
if container_suffix then
key = key .. container_suffix
end
return key
end
end
--[=[
This is typically used to define `canonicalize_key_container`, which converts a container as specified in the location
data into the canonical form containing both the full container key and its placetype. It generates a function to do
the canonicalization of a given container. If the container is a string, `suffix` is appended onto the string (use {nil}
or {""} if there is no suffix to append), and the placetype is set to `placetype`. Otherwise the container is left
as-is. Typical usage is like this:
```
canonicalize_key_container = make_canonicalize_key_container(", Canada", "province")
```
which will convert e.g. `"Ontario"` into `{key = "Ontario, Canada", placetype = "province"}`.
]=]
local function make_canonicalize_key_container(suffix, placetype)
return function(container)
if type(container) == "string" then
return {key = container .. (suffix or ""), placetype = placetype}
else
return container
end
end
end
-----------------------------------------------------------------------------------
-- Top-level tables --
-----------------------------------------------------------------------------------
export.continents = {
["Earth"] = {the = true, placetype = "planet", addl_parents = {"nature"},
fulldesc = "=the planet [[Earth]] and the features found on it"},
["Africa"] = {placetype = "continent", container = {key = "Earth", placetype = "planet"}},
["America"] = {placetype = {"supercontinent", "continent"}, container = {key = "Earth", placetype = "planet"},
keydesc = "[[America]], in the sense of [[North America]] and [[South America]] combined",
wp = "Americas"},
["Americas"] = {alias_of = "America", the = true},
["North America"] = {placetype = "continent", container = {key = "America", placetype = "supercontinent"}},
["Caribbean"] = {the = true, placetype = {"continental region", "region"}, container = {key = "North America", placetype = "continent"}},
["Central America"] = {placetype = {"continental region", "region"}, container = {key = "North America", placetype = "continent"}},
["South America"] = {placetype = "continent", container = {key = "America", placetype = "supercontinent"}},
["Antarctica"] = {placetype = "continent", container = {key = "Earth", placetype = "planet"},
fulldesc = "=the territory of [[Antarctica]]"},
["Eurasia"] = {placetype = {"supercontinent", "continent"}, container = {key = "Earth", placetype = "planet"},
keydesc = "[[Eurasia]], i.e. [[Europe]] and [[Asia]] together"},
["Asia"] = {placetype = "continent", container = {key = "Eurasia", placetype = "supercontinent"}},
["Europe"] = {placetype = "continent", container = {key = "Eurasia", placetype = "supercontinent"}},
["Oceania"] = {placetype = "continent", container = {key = "Earth", placetype = "planet"}},
["Melanesia"] = {placetype = {"continental region", "region"}, container = {key = "Oceania", placetype = "continent"}},
["Micronesia"] = {placetype = {"continental region", "region"}, container = {key = "Oceania", placetype = "continent"}},
["Polynesia"] = {placetype = {"continental region", "region"}, container = {key = "Oceania", placetype = "continent"}},
}
export.continents_group = {
default_overriding_bare_label_parents = {}, -- container parents should be used
default_divs = {{type = "countries", prep = "in"}},
-- It's enough to mention the first-level continent or continent group. It seems excessive to write e.g.
-- "El Salvador, a country in Central America, a continental region in North America, a continent in America, ...".
default_no_include_container_in_desc = true,
default_no_container_cat = true,
default_no_container_parent = true,
default_no_auto_augment_container = true,
default_no_generic_place_cat = true,
-- French Guyana is in France but not in Europe, which should not be an issue, so don't check holonym mismatches at
-- this level. We also run into problems with supercontinents, which have "continent" as the fallback and cause
-- mismatches.
default_no_check_holonym_mismatch = true,
data = export.continents,
}
-- Countries: including those with partial recognition that are normally considered countries (e.g. Kosovo, Taiwan).
export.countries = {
["Afghanistan"] = {container = "Asia", divs = {"provinces", "districts"}},
["Albania"] = {container = "Europe", divs = {"counties", "municipalities", "communes",
{type = "administrative units", cat_as = "communes"},
}, british_spelling = true},
["Algeria"] = {container = "Africa", divs = {"provinces", "communes", "districts", "municipalities"}},
["Andorra"] = {container = "Europe", divs = {"parishes"}, british_spelling = true},
["Angola"] = {container = "Africa", divs = {"provinces", "municipalities"}},
["Antigua and Barbuda"] = {container = "Caribbean", divs = {"provinces"}, british_spelling = true},
["Argentina"] = {container = "South America", divs = {"provinces", "departments", "municipalities"}},
["Armenia"] = {container = {"Europe", "Asia"}, divs = {"provinces", "districts", "municipalities"},
british_spelling = true},
["Republic of Armenia"] = {alias_of = "Armenia", the = true}, -- differs in "the"
-- Both a country and continent
["Australia"] = {container = "Oceania", divs = {
{type = "states", cat_as = "states and territories"},
{type = "territories", cat_as = "states and territories"},
{type = "ABBREVIATION_OF states", cat_as = "abbreviations of states and territories"},
{type = "ABBREVIATION_OF territories", cat_as = "abbreviations of states and territories"},
"local government areas", "dependent territories",
}, british_spelling = true},
["Austria"] = {container = "Europe", divs = {"states", "districts", "municipalities"}, british_spelling = true},
["Azerbaijan"] = {container = {"Europe", "Asia"}, divs = {"districts", "municipalities"}, british_spelling = true},
["Bahamas"] = {the = true, container = "Caribbean", divs = {"districts"}, british_spelling = true, wp = "The %l"},
["Bahrain"] = {container = "Asia", divs = {"governorates"}},
["Bangladesh"] = {container = "Asia", divs = {"divisions", "districts", "municipalities"}, british_spelling = true},
["Barbados"] = {container = "Caribbean", divs = {"parishes"}, british_spelling = true},
["Belarus"] = {container = "Europe", divs = {"regions", "districts"}, british_spelling = true},
["Belgium"] = {container = "Europe", divs = {"regions", "provinces", "municipalities"}, british_spelling = true},
["Belize"] = {container = "Central America", divs = {"districts"}, british_spelling = true},
["Benin"] = {container = "Africa", divs = {"departments", "communes"}},
["Bhutan"] = {container = "Asia", divs = {"districts", "gewogs", "chiwogs"}},
["Bolivia"] = {container = "South America", divs = {"provinces", "departments", "municipalities"}},
["Bosnia and Herzegovina"] = {container = "Europe", divs = {"entities", "cantons", "municipalities"}, british_spelling = true},
["Bosnia and Hercegovina"] = {alias_of = "Bosnia and Herzegovina", display = true},
["Bosnia-Herzegovina"] = {alias_of = "Bosnia and Herzegovina", display = true},
["Bosnia-Hercegovina"] = {alias_of = "Bosnia and Herzegovina", display = true},
["Bosnia"] = {alias_of = "Bosnia and Herzegovina", display = true},
["Botswana"] = {container = "Africa", divs = {"districts", "subdistricts"}, british_spelling = true},
["Brazil"] = {container = "South America", divs = {
"states", "municipalities", "macroregions",
{type = "ABBREVIATION_OF states", cat_as = "abbreviations of states"},
}},
["Brunei"] = {container = "Asia", divs = {"districts", "mukims"}, british_spelling = true},
["Bulgaria"] = {container = "Europe", divs = {"provinces", "municipalities"}, british_spelling = true},
["Burkina Faso"] = {container = "Africa", divs = {"regions", "departments", "provinces"}},
["Burundi"] = {container = "Africa", divs = {"provinces", "communes"}},
["Cambodia"] = {container = "Asia", divs = {"provinces", "districts"}},
["Cameroon"] = {container = "Africa", divs = {"regions", "departments"}},
["Canada"] = {container = "North America", divs = {
{type = "provinces", cat_as = "provinces and territories"},
{type = "territories", cat_as = "provinces and territories"},
{type = "ABBREVIATION_OF provinces", cat_as = "abbreviations of provinces and territories"},
{type = "ABBREVIATION_OF territories", cat_as = "abbreviations of provinces and territories"},
"counties", "districts", "municipalities", "regional municipalities",
"rural municipalities", "parishes",
-- Don't change the following to something more politically correct (e.g. "First Nations reserves") until/unless
-- the Canadian government makes a similar switch (and note that as of Apr 18 2025, the Wikipedia article is
-- still at [[w:Indian reserves]]).
"Indian reserves",
"census divisions",
{type = "townships", prep = "in"},
},
british_spelling = true},
["Cape Verde"] = {container = "Africa", divs = {"municipalities", "parishes"}},
["Cabo Verde"] = {alias_of = "Cape Verde", display = true},
["Central African Republic"] = {the = true, container = "Africa", divs = {"prefectures", "subprefectures"}},
["CAR"] = {alias_of = "Central African Republic", display = true, the = true},
["C.A.R"] = {alias_of = "Central African Republic", display = true, the = true},
["Chad"] = {container = "Africa", divs = {"regions", "departments"}},
["Chile"] = {container = "South America", divs = {"regions", "provinces", "communes"}},
["China"] = {container = "Asia", divs = {
{type = "provinces", cat_as = "provinces and autonomous regions"},
{type = "autonomous regions", cat_as = "provinces and autonomous regions"},
{type = "FORMER provinces", cat_as = "former provinces"},
"special administrative regions",
"prefectures",
{type = "FORMER prefectures", cat_as = "former prefectures"},
"prefecture-level cities",
{type = "counties", cat_as = "counties and county-level cities"},
{type = "county-level cities", cat_as = "counties and county-level cities"},
{type = "FORMER counties", cat_as = "former counties and county-level cities"},
{type = "FORMER county-level cities", cat_as = "former counties and county-level cities"},
-- "towns" (but not "townships") are automatically added as they are specified as generic_before_non_cities.
"districts",
{type = "FORMER districts", cat_as = "former districts"},
"subdistricts",
"townships",
"municipalities",
{type = "direct-administered municipalities", cat_as = "municipalities"},
}},
["People's Republic of China"] = {alias_of = "China", the = true}, -- differs in "the"
["Colombia"] = {container = "South America", divs = {"departments", "municipalities"}},
["Comoros"] = {the = true, container = "Africa", divs = {"autonomous islands"}},
["Costa Rica"] = {container = "Central America", divs = {"provinces", "cantons"}},
["Croatia"] = {container = "Europe", divs = {"counties", "municipalities"}, british_spelling = true},
["Cuba"] = {container = "Caribbean", divs = {"provinces", "municipalities"}},
["Cyprus"] = {container = {"Europe", "Asia"}, divs = {"districts"}, british_spelling = true},
["Czech Republic"] = {the = true, container = "Europe", divs = {"regions", "districts", "municipalities"}, british_spelling = true},
["Czechia"] = {alias_of = "Czech Republic"}, -- differs in "the"
["Democratic Republic of the Congo"] = {the = true, container = "Africa", divs = {"provinces", "territories"}},
["Congo"] = {alias_of = "Democratic Republic of the Congo", display = true, the = true},
["DRC"] = {alias_of = "Democratic Republic of the Congo", display = true, the = true},
["D.R.C"] = {alias_of = "Democratic Republic of the Congo", display = true, the = true},
["Denmark"] = {container = "Europe", divs = {"regions", "municipalities", "dependent territories"},
british_spelling = true,
-- Wikipedia separates [[w:Denmark]] (constituent country) from [[w:Danish Realm]] (country)
},
["Djibouti"] = {container = "Africa", divs = {"regions", "districts"}},
["Dominica"] = {container = "Caribbean", divs = {"parishes"}, british_spelling = true},
["Dominican Republic"] = {the = true, container = "Caribbean", divs = {"provinces", "municipalities"},
keydesc = "the [[Dominican Republic]], the country that shares the [[Caribbean]] island of [[Hispaniola]] with [[Haiti]]"},
["East Timor"] = {container = "Asia", divs = {"municipalities"}, wp = "Timor-Leste"},
["Timor-Leste"] = {alias_of = "East Timor", display = true},
["Ecuador"] = {container = "South America", divs = {"provinces", "cantons"}},
["Egypt"] = {container = "Africa", divs = {"governorates", "regions"}, british_spelling = true},
["El Salvador"] = {container = "Central America", divs = {"departments", "municipalities"}},
["Equatorial Guinea"] = {container = "Africa", divs = {"provinces"}},
["Eritrea"] = {container = "Africa", divs = {"regions", "subregions"}},
["Estonia"] = {container = "Europe", divs = {"counties", "municipalities"}, british_spelling = true},
["Eswatini"] = {container = "Africa", british_spelling = true},
["Swaziland"] = {alias_of = "Eswatini", display = true},
["Ethiopia"] = {container = "Africa", divs = {"regions", "zones"}},
["Federated States of Micronesia"] = {the = true, container = "Micronesia", divs = {"states"}},
["Micronesia"] = {alias_of = "Federated States of Micronesia"},
["Fiji"] = {container = "Melanesia", divs = {"divisions", "provinces"}, british_spelling = true},
["Finland"] = {container = "Europe", divs = {"regions", "municipalities"}, british_spelling = true},
["France"] = {container = "Europe", divs = {"regions", "cantons", "collectivities",
"communes",
{type = "municipalities", cat_as = "communes"},
"departments",
{type = "prefectures", cat_as = {"prefectures", "departmental capitals"}},
{type = "French prefectures", cat_as = {"prefectures", "departmental capitals"}},
"dependent territories", "territories", "provinces",
}, british_spelling = true},
["Gabon"] = {container = "Africa", divs = {"provinces", "departments"}},
["Gambia"] = {the = true, container = "Africa", divs = {"divisions", "districts"}, british_spelling = true, wp = "The %l"},
["Georgia"] = {container = {"Europe", "Asia"}, divs = {"regions", "districts"},
keydesc = "the country of [[Georgia]], in [[Eurasia]]", british_spelling = true, wp = "%l (country)"},
["Germany"] = {container = "Europe", divs = {
"states",
-- Bavaria, Baden-Württemberg, Hesse and North Rhine-Westphalia have administrative regions as divisions, but
-- there aren't really enough of them to categorize per state.
"regions",
"municipalities", "districts"}, british_spelling = true},
["Ghana"] = {container = "Africa", divs = {"regions", "districts"}, british_spelling = true},
["Greece"] = {container = "Europe", divs = {"regions", "regional units", "municipalities",
{type = "peripheries", cat_as = {"regions"}},
}, british_spelling = true},
["Grenada"] = {container = "Caribbean", divs = {"parishes"}, british_spelling = true},
["Guatemala"] = {container = "Central America", divs = {"departments", "municipalities"}},
["Guinea"] = {container = "Africa", divs = {"regions", "prefectures"}},
["Guinea-Bissau"] = {container = "Africa", divs = {"regions"}},
["Guyana"] = {container = "South America", divs = {"regions"}, british_spelling = true},
["Haiti"] = {container = "Caribbean", divs = {"departments", "arrondissements"}},
["Honduras"] = {container = "Central America", divs = {"departments", "municipalities"}},
["Hungary"] = {container = "Europe", divs = {"counties", "districts"}, british_spelling = true},
["Iceland"] = {container = "Europe", divs = {"regions", "municipalities", "counties"}, british_spelling = true},
["India"] = {container = "Asia", divs = {
{type = "states", cat_as = "states and union territories"},
{type = "union territories", cat_as = "states and union territories"},
{type = "ABBREVIATION_OF states", cat_as = "abbreviations of states and union territories"},
{type = "ABBREVIATION_OF union territories", cat_as = "abbreviations of states and union territories"},
"divisions", "districts", "municipalities",
}, british_spelling = true},
["Indonesia"] = {container = "Asia", divs = {"regencies", "provinces",
{type = "ABBREVIATION_OF provinces", cat_as = "abbreviations of provinces"},
}},
["Iran"] = {container = "Asia", divs = {"provinces", "counties"}},
["Iraq"] = {container = "Asia", divs = {"governorates", "districts"}},
["Ireland"] = {container = "Europe", addl_parents = {"British Isles"},
divs = {"counties", "districts", "provinces"}, british_spelling = true, wp = "Republic of %l"},
["Republic of Ireland"] = {alias_of = "Ireland", the = true}, -- differs in "the"
["Israel"] = {container = "Asia", divs = {"districts"}},
["Italy"] = {container = "Europe", divs = {
"regions", "provinces", "metropolitan cities", "municipalities",
{type = "autonomous regions", cat_as = "regions"},
}, british_spelling = true},
["Ivory Coast"] = {container = "Africa", divs = {"districts", "regions"}},
-- We should really be using Ivory Coast (common name) but there are political ramifications to the use of
-- Côte d'Ivoire so don't make it a display alias.
["Côte d'Ivoire"] = {alias_of = "Ivory Coast"},
["Jamaica"] = {container = "Caribbean", divs = {"parishes"}, british_spelling = true},
["Japan"] = {container = "Asia", divs = {"prefectures", "subprefectures", "municipalities"}},
["Jordan"] = {container = "Asia", divs = {"governorates"}},
["Kazakhstan"] = {container = {"Asia", "Europe"}, divs = {"regions", "districts"}},
["Kenya"] = {container = "Africa", divs = {"counties"}, british_spelling = true},
["Kiribati"] = {container = "Micronesia", british_spelling = true},
["Kosovo"] = {container = "Europe", divs = {"districts", "municipalities"}, british_spelling = true},
["Kuwait"] = {container = "Asia", divs = {"governorates", "areas"}},
["Kyrgyzstan"] = {container = "Asia", divs = {"regions", "districts"}},
["Laos"] = {container = "Asia", divs = {"provinces", "districts"}},
["Latvia"] = {container = "Europe", divs = {"municipalities"}, british_spelling = true},
["Lebanon"] = {container = "Asia", divs = {"governorates", "districts"}},
["Lesotho"] = {container = "Africa", divs = {"districts"}, british_spelling = true},
["Liberia"] = {container = "Africa", divs = {"counties", "districts"}},
["Libya"] = {container = "Africa", divs = {"districts", "municipalities"}},
["Liechtenstein"] = {container = "Europe", divs = {"municipalities"}, british_spelling = true},
["Lithuania"] = {container = "Europe", divs = {"counties", "municipalities"}, british_spelling = true},
["Luxembourg"] = {container = "Europe", divs = {"cantons", "districts"}, british_spelling = true},
["Madagascar"] = {container = "Africa", divs = {"regions", "districts"}},
["Malawi"] = {container = "Africa", divs = {"regions", "districts"}, british_spelling = true},
["Malaysia"] = {container = "Asia", divs = {"states", "federal territories", "districts"}, british_spelling = true},
["Maldives"] = {the = true, container = "Asia", divs = {"provinces", "administrative atolls"}, british_spelling = true},
["Mali"] = {container = "Africa", divs = {"regions", "cercles"}},
["Malta"] = {container = "Europe", divs = {"regions", "local councils"}, british_spelling = true},
["Marshall Islands"] = {the = true, container = "Micronesia", divs = {"municipalities"}},
["Mauritania"] = {container = "Africa", divs = {"regions", "departments"}},
["Mauritius"] = {container = "Africa", divs = {"districts"}, british_spelling = true},
["Mexico"] = {container = "North America", addl_parents = {"Central America"}, divs = {
"states", "municipalities",
{type = "ABBREVIATION_OF states", cat_as = "abbreviations of states"},
}},
["Moldova"] = {container = "Europe", divs = {
{type = "districts", cat_as = "districts and autonomous territorial units"},
{type = "autonomous territorial units", cat_as = "districts and autonomous territorial units"},
"communes", "municipalities",
}, british_spelling = true},
["Monaco"] = {placetype = {"city-state", "country"}, container = "Europe",
-- We want the first placetype to be 'city-state' so the description of Monaco says it's a city-state, but we
-- want its parent to be "countries in Europe".
bare_category_parent_type = {type = "countries", prep = "in"},
is_city = true, british_spelling = true},
["Mongolia"] = {container = "Asia", divs = {"provinces", "districts"}},
["Montenegro"] = {container = "Europe", divs = {"municipalities"}},
["Morocco"] = {container = "Africa", divs = {"regions", "prefectures", "provinces"}},
["Mozambique"] = {container = "Africa", divs = {"provinces", "districts"}},
["Myanmar"] = {container = "Asia",
divs = {"regions", "states", "union territories",
{type = "self-administered zones", cat_as = "self-administered areas"},
{type = "self-administered divisions", cat_as = "self-administered areas"},
"districts"}},
["Burma"] = {alias_of = "Myanmar"}, -- not display-canonicalizing; has political connotations
["Namibia"] = {container = "Africa", divs = {"regions", "constituencies"}, british_spelling = true},
["Nauru"] = {container = "Micronesia", divs = {"districts"}, british_spelling = true},
["Nepal"] = {container = "Asia", divs = {"provinces", "districts"}},
["Netherlands"] = {the = true, placetype = {"country", "constituent country"}, container = "Europe",
divs = {"provinces", "municipalities",
{type = "FORMER municipalities", cat_as = "former municipalities"},
"dependent territories", "constituent countries"}, british_spelling = true,
-- Wikipedia separates [[w:Netherlands]] (constituent country) from [[w:Kingdom of the Netherlands]]
-- (country)
},
["New Zealand"] = {container = "Polynesia", divs = {
"regions", "dependent territories", "territorial authorities",
{type = "districts", cat_as = "territorial authorities"},
},
british_spelling = true},
["Nicaragua"] = {container = "Central America", divs = {"departments", "municipalities"}},
["Niger"] = {container = "Africa", divs = {"regions", "departments"}},
["Nigeria"] = {container = "Africa", divs = {
"states",
-- Categorize the Federal Capital Territory as a state because there's only one of it; we could categorize
-- everything under 'states and territories' but that seems a bit pointless.
{type = "federal territories", cat_as = "states"},
"local government areas",
}, british_spelling = true},
["North Korea"] = {container = "Asia", addl_parents = {"Korea"}, divs = {"provinces", "counties"}},
["North Macedonia"] = {container = "Europe", divs = {"regions", "municipalities"}, british_spelling = true},
["Macedonia"] = {alias_of = "North Macedonia", display = true},
["Republic of North Macedonia"] = {alias_of = "North Macedonia", the = true}, -- differs in "the"
["Republic of Macedonia"] = {alias_of = "North Macedonia", the = true}, -- differs in "the"
["Norway"] = {container = "Europe",
divs = {"counties", "municipalities", "dependent territories", "districts", "unincorporated areas"},
british_spelling = true},
["Oman"] = {container = "Asia", divs = {"governorates", "provinces"}},
["Pakistan"] = {container = "Asia", divs = {
{type = "provinces", cat_as = "provinces and territories"},
{type = "administrative territories", cat_as = "provinces and territories"},
{type = "federal territories", cat_as = "provinces and territories"},
{type = "territories", cat_as = "provinces and territories"},
"divisions", "districts",
}, british_spelling = true},
["Palau"] = {container = "Micronesia", divs = {"states"}},
["Palestine"] = {container = "Asia", divs = {"governorates"}},
["State of Palestine"] = {alias_of = "Palestine", the = true}, -- differs in "the"
["Panama"] = {container = "Central America", divs = {"provinces", "districts"}},
["Papua New Guinea"] = {container = "Melanesia", divs = {"provinces", "districts"}, british_spelling = true},
["Paraguay"] = {container = "South America", divs = {"departments", "districts"}},
["Peru"] = {container = "South America", divs = {"regions", "provinces", "districts"}},
["Philippines"] = {the = true, container = "Asia", divs = {"regions", "provinces", "districts", "municipalities", "barangays"}},
["Poland"] = {divs = {"voivodeships", "counties",
{type = "Polish colonies", cat_as = {{type = "villages", prep = "in"}}},
}, container = "Europe", british_spelling = true},
["Portugal"] = {container = "Europe", divs = {
{type = "autonomous regions", cat_as = "districts and autonomous regions"},
{type = "districts", cat_as = "districts and autonomous regions"},
"provinces", "municipalities"}, british_spelling = true},
["Qatar"] = {container = "Asia", divs = {"municipalities", "zones"}},
["Republic of the Congo"] = {the = true, container = "Africa", divs = {"departments", "districts"}},
["Congo Republic"] = {alias_of = "Republic of the Congo", display = true, the = true},
["Romania"] = {container = "Europe", divs = {
"regions", "counties", "communes",
{type = "ABBREVIATION_OF counties", cat_as = "abbreviations of counties"},
}, british_spelling = true},
["Russia"] = {container = {"Europe", "Asia"}, divs = {
"federal subjects", "republics", "autonomous oblasts", "autonomous okrugs", "oblasts", "krais", "federal cities",
"districts", "federal districts"},
british_spelling = true},
["Rwanda"] = {container = "Africa", divs = {"provinces", "districts"}},
["Saint Kitts and Nevis"] = {container = "Caribbean", divs = {"parishes"}, british_spelling = true},
["Saint Kitts"] = {alias_of = "Saint Kitts and Nevis", display = true},
["Saint Lucia"] = {container = "Caribbean", divs = {"districts"}, british_spelling = true},
["Saint Vincent and the Grenadines"] = {container = "Caribbean", divs = {"parishes"}, british_spelling = true},
["Saint Vincent"] = {alias_of = "Saint Vincent and the Grenadines", display = true},
["SVG"] = {alias_of = "Saint Vincent and the Grenadines", display = true},
["S.V.G"] = {alias_of = "Saint Vincent and the Grenadines", display = true},
["Samoa"] = {container = "Polynesia", divs = {"districts"}, british_spelling = true},
["San Marino"] = {container = "Europe", divs = {"municipalities"}, british_spelling = true},
["São Tomé and Príncipe"] = {container = "Africa", divs = {"districts"}},
["São Tome and Principe"] = {alias_of = "São Tomé and Príncipe", display = true},
["São Tomé"] = {alias_of = "São Tomé and Príncipe", display = true},
["São Tome"] = {alias_of = "São Tomé and Príncipe", display = true},
["Saudi Arabia"] = {container = "Asia", divs = {"provinces", "governorates"}},
["Senegal"] = {container = "Africa", divs = {"regions", "departments"}},
["Serbia"] = {container = "Europe", divs = {"districts", "municipalities", "autonomous provinces"}},
["Seychelles"] = {container = "Africa", divs = {"districts"}, british_spelling = true},
["Sierra Leone"] = {container = "Africa", divs = {"provinces", "districts"}, british_spelling = true},
["Singapore"] = {container = "Asia", divs = {"districts", "regions"}, british_spelling = true},
["Slovakia"] = {container = "Europe", divs = {"regions", "districts"}, british_spelling = true},
["Slovenia"] = {container = "Europe", divs = {"statistical regions", "municipalities"}, british_spelling = true},
-- Note: While the official name does not include "the" at the beginning,
-- it sounds strange in English to leave it out and it's commonly included.
["Solomon Islands"] = {the = true, container = "Melanesia", divs = {"provinces"}, british_spelling = true},
["Somalia"] = {container = "Africa", divs = {"regions", "districts"}},
["South Africa"] = {container = "Africa", divs = {
"provinces",
"districts",
{type = "district municipalities", cat_as = "districts"},
{type = "metropolitan municipalities", cat_as = "districts"},
"municipalities",
}, british_spelling = true},
["South Korea"] = {container = "Asia", addl_parents = {"Korea"}, divs = {"provinces", "counties", "districts"}},
["South Sudan"] = {container = "Africa", divs = {"regions", "states", "counties"}, british_spelling = true},
["Spain"] = {container = "Europe", divs = {"autonomous communities", "provinces", "municipalities",
"comarcas", "autonomous cities"},
british_spelling = true},
["Sri Lanka"] = {container = "Asia", divs = {"provinces", "districts"}, british_spelling = true},
["Sudan"] = {container = "Africa", divs = {"states", "districts"}, british_spelling = true},
["Suriname"] = {container = "South America", divs = {"districts"}},
["Sweden"] = {container = "Europe", divs = {"provinces", "counties", "municipalities"}, british_spelling = true},
["Switzerland"] = {container = "Europe", divs = {"cantons", "municipalities", "districts"}, british_spelling = true},
["Syria"] = {container = "Asia", divs = {"governorates", "districts"}},
["Taiwan"] = {container = "Asia", divs = {"counties", "districts", "townships", "special municipalities"}},
["Republic of China"] = {alias_of = "Taiwan", the = true}, -- differs in "the", different political connotations
["Tajikistan"] = {container = "Asia", divs = {"regions", "districts"}},
["Tanzania"] = {container = "Africa", divs = {"regions", "districts"}, british_spelling = true},
["Thailand"] = {container = "Asia", divs = {"provinces", "districts", "subdistricts"}},
["Togo"] = {container = "Africa", divs = {"provinces", "prefectures"}},
["Tonga"] = {container = "Polynesia", divs = {"divisions"}, british_spelling = true},
["Trinidad and Tobago"] = {container = "Caribbean", divs = {"regions", "municipalities"}, british_spelling = true},
["Tunisia"] = {container = "Africa", divs = {"governorates", "delegations"}},
["Turkey"] = {container = {"Europe", "Asia"}, divs = {"provinces", "districts"}},
-- Foreign names generally get display-canonicalized.
["Türkiye"] = {alias_of = "Turkey", display = true},
["Turkmenistan"] = {container = "Asia", divs = {
-- The 5 regions are often also called provinces
"regions", {type = "provinces", cat_as = "regions"}, "districts"},
},
["Tuvalu"] = {container = "Polynesia", divs = {"atolls"}, british_spelling = true},
["Uganda"] = {container = "Africa", divs = {"districts", "counties"}, british_spelling = true},
["Ukraine"] = {container = "Europe", divs = {
{type = "oblasts", cat_as = "oblasts and autonomous republics"},
{type = "autonomous republics", cat_as = "oblasts and autonomous republics"},
"raions", "hromadas",
}, british_spelling = true},
["United Arab Emirates"] = {the = true, container = "Asia", divs = {"emirates"}},
-- Abbreviations get display-canonicalized.
["UAE"] = {alias_of = "United Arab Emirates", display = true, the = true},
["U.A.E."] = {alias_of = "United Arab Emirates", display = true, the = true},
["United Kingdom"] = {the = true, container = "Europe", addl_parents = {"British Isles"},
divs = {"constituent countries", "counties", "districts", "boroughs", "territories", "dependent territories",
"traditional counties"},
keydesc = "the [[United Kingdom]] of Great Britain and Northern Ireland", british_spelling = true},
-- Abbreviations get display-canonicalized.
["UK"] = {alias_of = "United Kingdom", display = true, the = true},
["U.K."] = {alias_of = "United Kingdom", display = true, the = true},
["United States"] = {the = true, container = "North America",
divs = {"counties", "county seats", "states", "territories", "dependent territories",
{type = "ABBREVIATION_OF states", cat_as = "abbreviations of states"},
{type = "DEROGATORY_NAME_FOR states", cat_as = "derogatory names for states"},
{type = "NICKNAME_FOR states", cat_as = "nicknames for states"},
{type = "OFFICIAL_NICKNAME_FOR states", cat_as = "official nicknames for states"},
{type = "boroughs", prep = "in"}, -- exist in Pennsylvania and New Jersey
"municipalities", -- these exist politically at least in Colorado and Connecticut
{type = "census-designated places", prep = "in"},
{type = "unincorporated communities", prep = "in"},
-- Don't change the following to something more politically correct until/unless the US government makes a
-- similar switch (and note that as of Apr 18 2025, the Wikipedia article is still at
-- [[w:Indian reservations]]).
"Indian reservations",
}},
-- Abbreviations and long forms (when possible) get display-canonicalized.
["US"] = {alias_of = "United States", display = true, the = true},
["U.S."] = {alias_of = "United States", display = true, the = true},
["USA"] = {alias_of = "United States", display = true, the = true},
["U.S.A."] = {alias_of = "United States", display = true, the = true},
["United States of America"] = {alias_of = "United States", display = true, the = true},
["Uruguay"] = {container = "South America", divs = {"departments", "municipalities"}},
["Uzbekistan"] = {container = "Asia", divs = {"regions", "districts"}},
["Vanuatu"] = {container = "Melanesia", divs = {"provinces"}, british_spelling = true},
["Vatican City"] = {placetype = {"city-state", "country"}, container = "Europe",
-- First placetype should be 'city-state' for to shown up in its description,
-- Its parent should still be "countries in Europe".
bare_category_parent_type = {type = "countries", prep = "in"},
addl_parents = {"Rome"}, is_city = true, british_spelling = true},
["Vatican"] = {alias_of = "Vatican City", the = true}, -- differs in "the"
["Venezuela"] = {container = "South America", divs = {"states", "municipalities"}},
["Vietnam"] = {container = "Asia", divs = {"provinces", "districts", "municipalities"}},
["Western Sahara"] = {placetype = {"territory", "country"}, container = "Africa",
bare_category_parent_type = {type = "countries", prep = "in"},
},
-- Not display-canonicalizable both due to differences in 'the' and the sovereignty dispute over Western Sahara
["Sahrawi Arab Democratic Republic"] = {alias_of = "Western Sahara", the = true},
["SADR"] = {alias_of = "Sahrawi Arab Democratic Republic", display = true, the = true},
["Yemen"] = {container = "Asia", divs = {"governorates", "districts"}},
["Zambia"] = {container = "Africa", divs = {"provinces", "districts"}, british_spelling = true},
["Zimbabwe"] = {container = "Africa", divs = {"provinces", "districts"}, british_spelling = true},
}
local function canonicalize_continent_container(key)
if type(key) ~= "string" then
return key
end
if export.continents[key] then
return {key = key, placetype = export.continents[key].placetype}
end
internal_error("Unrecognized key %s in `canonicalize_continent_like`", key)
end
export.countries_group = {
canonicalize_key_container = canonicalize_continent_container,
default_overriding_bare_label_parents = {"+++", "countries"},
default_placetype = "country",
default_no_container_cat = true,
default_no_container_parent = true,
-- No need to augment country holonyms with continents; not needed for disambiguation.
default_no_auto_augment_container = true,
data = export.countries,
}
-- Country-like entities: typically overseas territories or de-facto independent countries, which in both cases
-- are not internationally recognized as sovereign nations but which we treat similarly to countries.
export.country_like_entities = {
-- British Overseas Territory
["Akrotiri and Dhekelia"] = {
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"Cyprus", "Europe", "Asia"},
british_spelling = true,
},
-- Åland: Listed as a region of Finland. Wikipedia lists this under "dependent territories" in
-- [[w:List of sovereign states and dependent territories by continent]].
-- unincorporated territory of the United States
["American Samoa"] = {
placetype = {"unincorporated territory", "overseas territory", "territory"},
container = "United States",
addl_parents = {"Polynesia"},
},
-- British Overseas Territory
["Anguilla"] = {
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"Caribbean"},
british_spelling = true,
},
-- de-facto independent state, internationally recognized as part of Georgia
["Abkhazia"] = {
placetype = {"unrecognized country", "country"},
addl_parents = {"Georgia", "Europe", "Asia"},
divs = {"districts"},
keydesc = "the de-facto independent state of [[Abkhazia]], internationally recognized as part of the country of [[Georgia]]",
british_spelling = true,
},
-- Australian external territory
["Ashmore and Cartier Islands"] = {
the = true,
placetype = {"external territory", "territory"},
container = "Australia",
addl_parents = {"Asia"},
},
-- constituent country of the Netherlands
["Aruba"] = {
placetype = {"constituent country", "country"},
container = "Netherlands",
addl_parents = {"Caribbean"},
british_spelling = true,
},
-- British Overseas Territory
["Bermuda"] = {
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"North America"},
british_spelling = true,
},
-- special municipality of the Netherlands
["Bonaire"] = {
placetype = {"special municipality", "municipality", "overseas territory", "territory"},
container = "Netherlands",
addl_parents = {"Caribbean"},
is_city = true,
british_spelling = true,
},
-- British Overseas Territory
["British Indian Ocean Territory"] = {
the = true,
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"Asia"},
british_spelling = true,
},
-- British Overseas Territory
["British Virgin Islands"] = {
the = true,
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"Caribbean"},
british_spelling = true,
},
-- Norwegian dependent territory
["Bouvet Island"] = {
placetype = {"dependent territory", "territory"},
container = "Norway",
addl_parents = {"Africa"},
british_spelling = true,
},
-- British Overseas Territory
["Cayman Islands"] = {
the = true,
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"Caribbean"},
british_spelling = true,
},
-- Australian external territory
["Christmas Island"] = {
placetype = {"external territory", "territory"},
container = "Australia",
addl_parents = {"Asia"},
british_spelling = true,
},
-- Sui generis French "state private property" per Wikipedia; classify as overseas territory like the
-- French Southern and Antarctic Lands.
["Clipperton Island"] = {
placetype = {"overseas territory", "territory"},
container = "France",
addl_parents = {"North America"},
},
-- Australian external territory; also called the Keeling Islands or (officially) the Cocos (Keeling) Islands
["Cocos Islands"] = {
the = true,
placetype = {"external territory", "territory"},
container = "Australia",
addl_parents = {"Asia"},
wp = "Cocos (Keeling) Islands",
british_spelling = true,
},
["Cocos (Keeling) Islands"] = {alias_of = "Cocos Islands", display = true, the = true},
["Keeling Islands"] = {alias_of = "Cocos Islands", display = true, the = true},
-- self-governing but in free association with New Zealand
["Cook Islands"] = {
the = true,
placetype = {"country"},
container = "New Zealand",
addl_parents = {"Polynesia"},
british_spelling = true,
},
-- constituent country of the Netherlands
["Curaçao"] = {
placetype = {"constituent country", "country"},
container = "Netherlands",
addl_parents = {"Caribbean"},
british_spelling = true,
},
-- special territory of Chile
["Easter Island"] = {
placetype = {"special territory", "territory"},
container = "Chile",
addl_parents = {"Polynesia"},
},
-- British Overseas Territory
["Falkland Islands"] = {
the = true,
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"South America"},
british_spelling = true,
},
-- autonomous territory of Denmark
["Faroe Islands"] = {
the = true,
placetype = {"autonomous territory", "territory"},
container = "Denmark",
addl_parents = {"Europe"},
british_spelling = true,
},
-- overseas department and region of France
["French Guiana"] = {
placetype = {"overseas department", "department", "administrative region", "region"},
container = "France",
divs = {"communes"},
addl_parents = {"South America"},
british_spelling = true,
},
-- overseas collectivity of France
["French Polynesia"] = {
placetype = {"overseas collectivity", "collectivity"},
container = "France",
addl_parents = {"Polynesia"},
british_spelling = true,
},
-- French overseas territory
["French Southern and Antarctic Lands"] = {
the = true,
placetype = {"overseas territory", "territory"},
container = "France",
addl_parents = {"Africa"},
},
-- British Overseas Territory
["Gibraltar"] = {
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"Europe"},
is_city = true,
british_spelling = true,
},
-- autonomous territory of Denmark
["Greenland"] = {
placetype = {"autonomous territory", "territory"},
container = "Denmark",
addl_parents = {"North America"},
divs = {"municipalities"},
british_spelling = true,
},
-- overseas department and region of France
["Guadeloupe"] = {
placetype = {"overseas department", "department", "administrative region", "region"},
container = "France",
addl_parents = {"Caribbean"},
divs = {"communes"},
british_spelling = true,
},
-- unincorporated territory of the United States
["Guam"] = {
placetype = {"unincorporated territory", "overseas territory", "territory"},
container = "United States",
addl_parents = {"Micronesia"},
},
-- self-governing British Crown dependency; technically called the Bailiwick of Guernsey
["Guernsey"] = {
placetype = {"crown dependency", "dependency", "dependent territory", "bailiwick", "territory"},
container = "United Kingdom",
addl_parents = {"British Isles", "Europe"},
british_spelling = true,
wp = "Bailiwick of %l",
},
["Bailiwick of Guernsey"] = {alias_of = "Guernsey", the = true},
-- Australian external territory
["Heard Island and McDonald Islands"] = {
the = true,
placetype = {"external territory", "territory"},
container = "Australia",
addl_parents = {"Africa"},
},
-- special administrative region of China
["Hong Kong"] = {
placetype = {"special administrative region", "city"},
container = "China",
is_city = true,
british_spelling = true,
},
-- self-governing British Crown dependency
["Isle of Man"] = {
the = true,
placetype = {"crown dependency", "dependency", "dependent territory", "territory"},
container = "United Kingdom",
addl_parents = {"British Isles", "Europe"},
british_spelling = true,
},
-- Norwegian unincorporated area
["Jan Mayen"] = {
placetype = {"unincorporated area", "dependent territory", "territory", "island"},
container = "Norway",
addl_parents = {"Europe"},
british_spelling = true,
},
-- self-governing British Crown dependency; technically called the Bailiwick of Jersey
["Jersey"] = {
placetype = {"crown dependency", "dependency", "dependent territory", "bailiwick", "territory"},
container = "United Kingdom",
addl_parents = {"British Isles", "Europe"},
british_spelling = true,
},
["Bailiwick of Jersey"] = {alias_of = "Jersey", the = true},
-- special administrative region of China
["Macau"] = {
placetype = {"special administrative region", "city"},
container = "China",
is_city = true,
british_spelling = true,
},
-- overseas department and region of France
["Martinique"] = {
placetype = {"overseas department", "department", "administrative region", "region"},
container = "France",
divs = {"communes"},
addl_parents = {"Caribbean"},
british_spelling = true,
},
-- overseas department and region of France
["Mayotte"] = {
placetype = {"overseas department", "department", "administrative region", "region"},
container = "France",
divs = {"communes"},
addl_parents = {"Africa"},
british_spelling = true,
},
-- British Overseas Territory
["Montserrat"] = {
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"Caribbean"},
british_spelling = true,
},
-- special collectivity of France
["New Caledonia"] = {
placetype = {"special collectivity", "collectivity"},
container = "France",
addl_parents = {"Melanesia"},
british_spelling = true,
},
-- dependent territory of New Zealand
["New Zealand Subantarctic Islands"] = {
the = true,
placetype = {"dependent territory", "territory"},
container = "New Zealand",
addl_parents = {"Antarctica"},
british_spelling = true,
},
-- self-governing but in free association with New Zealand
["Niue"] = {
placetype = {"country"},
container = "New Zealand",
addl_parents = {"Polynesia"},
british_spelling = true,
},
-- Australian external territory
["Norfolk Island"] = {
placetype = {"external territory", "territory"},
container = "Australia",
addl_parents = {"Polynesia"},
british_spelling = true,
},
-- de-facto independent state, internationally recognized as part of Cyprus
["Northern Cyprus"] = {
placetype = {"unrecognized country", "country"},
addl_parents = {"Cyprus", "Turkey", "Europe", "Asia"},
divs = {"districts"},
keydesc = "the de-facto independent state of [[Northern Cyprus]], internationally recognized as part of the country of [[Cyprus]]",
british_spelling = true,
},
-- commonwealth, unincorporated territory of the United States
["Northern Mariana Islands"] = {
the = true,
placetype = {"commonwealth", "unincorporated territory", "overseas territory", "territory"},
container = "United States",
addl_parents = {"Micronesia"},
},
-- British Overseas Territory
["Pitcairn Islands"] = {
the = true,
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"Polynesia"},
british_spelling = true,
},
-- commonwealth of the United States
["Puerto Rico"] = {
placetype = {"commonwealth", "overseas territory", "territory"},
container = "United States",
addl_parents = {"Caribbean"},
divs = {"municipalities"},
},
-- overseas department and region of France
["Réunion"] = {
placetype = {"overseas department", "department", "administrative region", "region"},
container = "France",
divs = {"communes"},
addl_parents = {"Africa"},
british_spelling = true,
},
-- special municipality of the Netherlands
["Saba"] = {
placetype = {"special municipality", "municipality", "overseas territory", "territory"},
container = "Netherlands",
addl_parents = {"Caribbean"},
is_city = true,
british_spelling = true,
},
-- overseas collectivity of France
["Saint Barthélemy"] = {
placetype = {"overseas collectivity", "collectivity"},
container = "France",
addl_parents = {"Caribbean"},
british_spelling = true,
},
-- British Overseas Territory
["Saint Helena, Ascension and Tristan da Cunha"] = {
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
divs = {{type = "constituent parts", container_parent_type = false}},
addl_parents = {"Atlantic Ocean", "Africa"},
british_spelling = true,
},
-- constituent parts of the combined oveseas territory
["Ascension Island"] = {
placetype = {"constituent part", "territory", "island"},
container = {key = "Saint Helena, Ascension and Tristan da Cunha", placetype = "overseas territory"},
addl_parents = {"Atlantic Ocean"},
overriding_bare_label_parents = {},
no_container_cat = false,
no_container_parent = false,
no_auto_augment_container = false,
},
["Saint Helena"] = {
placetype = {"constituent part", "territory", "island"},
container = {key = "Saint Helena, Ascension and Tristan da Cunha", placetype = "overseas territory"},
addl_parents = {"Atlantic Ocean"},
overriding_bare_label_parents = {},
no_container_cat = false,
no_container_parent = false,
no_auto_augment_container = false,
},
["Tristan da Cunha"] = {
placetype = {"constituent part", "territory", "archipelago"},
container = {key = "Saint Helena, Ascension and Tristan da Cunha", placetype = "overseas territory"},
addl_parents = {"Atlantic Ocean"},
overriding_bare_label_parents = {},
no_container_cat = false,
no_container_parent = false,
no_auto_augment_container = false,
},
-- overseas collectivity of France
["Saint Martin"] = {
placetype = {"overseas collectivity", "collectivity"},
container = "France",
addl_parents = {"Caribbean"},
british_spelling = true,
},
-- overseas collectivity of France
["Saint Pierre and Miquelon"] = {
placetype = {"overseas collectivity", "collectivity"},
container = "France",
divs = {"communes"},
addl_parents = {"North America"},
british_spelling = true,
},
-- special municipality of the Netherlands
["Sint Eustatius"] = {
placetype = {"special municipality", "municipality", "overseas territory", "territory"},
container = "Netherlands",
addl_parents = {"Caribbean"},
is_city = true,
british_spelling = true,
},
-- constituent country of the Netherlands
["Sint Maarten"] = {
placetype = {"constituent country", "country"},
container = "Netherlands",
addl_parents = {"Caribbean"},
british_spelling = true,
},
-- de-facto independent state, internationally recognized as part of Somalia
["Somaliland"] = {
placetype = {"unrecognized country", "country"},
addl_parents = {"Somalia", "Africa"},
keydesc = "the de-facto independent state of [[Somaliland]], internationally recognized as part of the country of [[Somalia]]",
british_spelling = true,
},
-- British Overseas Territory
-- FIXME: We should form the group "South Georgia and the South Sandwich Islands" like we did for
-- "Saint Helena, Ascension and Tristan da Cunha".
["South Georgia"] = {
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"Atlantic Ocean"},
british_spelling = true,
},
-- de-facto independent state, internationally recognized as part of Georgia
["South Ossetia"] = {
placetype = {"unrecognized country", "country"},
addl_parents = {"Georgia", "Europe", "Asia"},
keydesc = "the de-facto independent state of [[South Ossetia]], internationally recognized as part of the country of [[Georgia]]",
british_spelling = true,
},
-- British Overseas Territory
["South Sandwich Islands"] = {
the = true,
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"Atlantic Ocean"},
wp = true,
wpcat = "South Georgia and the South Sandwich Islands",
british_spelling = true,
},
-- Norwegian unincorporated area
["Svalbard"] = {
placetype = {"unincorporated area", "dependent territory", "territory", "archipelago"},
container = "Norway",
addl_parents = {"Europe"},
british_spelling = true,
},
-- dependent territory of New Zealand
["Tokelau"] = {
placetype = {"dependent territory", "territory"},
container = "New Zealand",
addl_parents = {"Polynesia"},
british_spelling = true,
},
-- de-facto independent state, internationally recognized as part of Moldova
["Transnistria"] = {
placetype = {"unrecognized country", "country"},
addl_parents = {"Moldova", "Europe"},
keydesc = "the de-facto independent state of [[Transnistria]], internationally recognized as part of [[Moldova]]",
british_spelling = true,
},
-- British Overseas Territory
["Turks and Caicos Islands"] = {
the = true,
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"Caribbean"},
british_spelling = true,
},
-- unincorporated territory of the United States
["United States Minor Outlying Islands"] = {
the = true,
placetype = {"unincorporated territory", "overseas territory", "territory"},
container = "United States",
addl_parents = {"Islands", "Micronesia", "Polynesia", "Caribbean"},
},
-- FIXME: We should add entries for the other minor outlying islands.
-- Baker Island (Oceania)
-- Howland Island (Oceania)
-- Jarvis Island (Oceania)
-- Johnston Atoll (Oceania)
-- Kingman Reef (Oceania)
-- Midway Atoll (Oceania)
-- Navassa Island (Caribbean)
-- Palmyra Atoll (Oceania)
-- Wake Island (Oceania)
["Wake Island"] = {
placetype = {"unincorporated territory", "overseas territory", "territory"},
container = "United States",
addl_parents = {"Micronesia"},
},
-- unincorporated territory of the United States
["United States Virgin Islands"] = {
the = true,
placetype = {"unincorporated territory", "overseas territory", "territory"},
container = "United States",
addl_parents = {"Caribbean"},
},
["U.S. Virgin Islands"] = {alias_of = "United States Virgin Islands", display = true, the = true},
["US Virgin Islands"] = {alias_of = "United States Virgin Islands", display = true, the = true},
-- overseas collectivity of France
["Wallis and Futuna"] = {
placetype = {"overseas collectivity", "collectivity"},
container = "France",
addl_parents = {"Polynesia"},
british_spelling = true,
},
}
export.country_like_entities_group = {
-- don't do any transformations between key and placename; in particular, don't chop off anything from
-- "Saint Helena, Ascension and Tristan da Cunha".
key_to_placename = false,
placename_to_key = false,
canonicalize_key_container = make_canonicalize_key_container(nil, "country"),
default_overriding_bare_label_parents = {"country-like entities"},
default_no_container_cat = true,
default_no_container_parent = true,
-- These entities often aren't really part of their container; a village in Wallis and Futuna (an overseas
-- collectivity of France in Polynesia), for example, shouldn't be treated as a village in France, nor as a village
-- in Europe.
default_no_auto_augment_container = true,
data = export.country_like_entities,
}
-- Former countries and such; we don't create "Cities in ..." categories because they don't exist anymore
export.former_countries = {
-- de-facto independent state of Armenian ethnicity, internationally recognized as part of Azerbaijan
-- (also known as Nagorno-Karabakh)
-- NOTE: Formerly listed Armenia as a parent; this seems politically non-neutral so I've taken it out.
["Artsakh"] = {
placetype = {"unrecognized country", "country"},
addl_parents = {"Azerbaijan", "Europe", "Asia"},
keydesc = "the former de-facto independent state of [[Artsakh]], internationally recognized as part of [[Azerbaijan]]",
british_spelling = true,
},
["Nagorno-Karabakh"] = {alias_of = "Artsakh"},
["Czechoslovakia"] = {container = "Europe", british_spelling = true},
["East Germany"] = {container = "Europe", addl_parents = {"Germany"}, british_spelling = true},
["North Vietnam"] = {container = "Asia", addl_parents = {"Vietnam"}},
["Persia"] = {placetype = {"empire", "country"}, container = "Asia", divs = {"provinces"}},
["Byzantine Empire"] = {
the = true, placetype = {"empire", "country"}, container = {"Europe", "Africa", "Asia"},
addl_parents = {"Ancient Europe", "Ancient Near East"},
divs = {
"provinces", "themes",
}},
["Roman Empire"] = {
the = true, placetype = {"empire", "country"}, container = {"Europe", "Africa", "Asia"}, addl_parents = {"Rome"},
divs = {
"provinces",
{type = "FORMER provinces", cat_as = "provinces"},
}},
["South Vietnam"] = {container = "Asia", addl_parents = {"Vietnam"}},
["Soviet Union"] = {
the = true, container = {"Europe", "Asia"}, divs = {"republics", "autonomous republics"},
british_spelling = true},
["West Germany"] = {container = "Europe", addl_parents = {"Germany"}, british_spelling = true},
["Yugoslavia"] = {container = "Europe", divs = {"districts"},
keydesc = "the former [[Kingdom of Yugoslavia]] (1918–1943) or the former [[Socialist Federal Republic of Yugoslavia]] (1943–1992)", british_spelling = true},
}
export.former_countries_group = {
canonicalize_key_container = canonicalize_continent_container,
default_overriding_bare_label_parents = {"former countries and country-like entities"},
default_is_former_place = true,
default_placetype = "country",
default_no_container_cat = true,
default_no_container_parent = true,
-- No need to augment country holonyms with continents; not needed for disambiguation.
default_no_auto_augment_container = true,
data = export.former_countries,
}
-----------------------------------------------------------------------------------
-- Subpolity tables --
-----------------------------------------------------------------------------------
export.australia_states_and_territories = {
["Australian Capital Territory, Australia"] = {the = true, placetype = "territory"},
["Jervis Bay Territory, Australia"] = {the = true, placetype = "territory"},
["New South Wales, Australia"] = {},
["Northern Territory, Australia"] = {the = true, placetype = "territory"},
["Queensland, Australia"] = {},
["South Australia, Australia"] = {},
["Tasmania, Australia"] = {},
["Victoria, Australia"] = {},
["Western Australia, Australia"] = {},
}
-- states and territories of Australia
export.australia_group = {
default_container = "Australia",
default_placetype = "state",
default_divs = "local government areas",
data = export.australia_states_and_territories,
}
export.austria_states = {
["Vienna, Austria"] = {},
["Lower Austria, Austria"] = {},
["Upper Austria, Austria"] = {},
["Styria, Austria"] = {},
["Tyrol, Austria"] = {wp = "Tyrol (state)"},
["Carinthia, Austria"] = {},
["Salzburg, Austria"] = {wp = "Salzburg (state)"},
["Vorarlberg, Austria"] = {},
["Burgenland, Austria"] = {},
}
-- states of Austria
export.austria_group = {
default_container = "Austria",
default_placetype = "state",
default_divs = "municipalities",
data = export.austria_states,
}
export.bangladesh_divisions = {
["Barisal Division, Bangladesh"] = {},
["Chittagong Division, Bangladesh"] = {},
["Dhaka Division, Bangladesh"] = {},
["Khulna Division, Bangladesh"] = {},
["Mymensingh Division, Bangladesh"] = {},
["Rajshahi Division, Bangladesh"] = {},
["Rangpur Division, Bangladesh"] = {},
["Sylhet Division, Bangladesh"] = {},
}
-- divisions of Bangladesh
export.bangladesh_group = {
key_to_placename = make_key_to_placename(", Bangladesh$", " Division$"),
placename_to_key = make_placename_to_key(", Bangladesh", " Division"),
default_container = "Bangladesh",
default_placetype = "division",
default_divs = "districts",
data = export.bangladesh_divisions,
}
export.brazil_states = {
["Acre, Brazil"] = {wp = "%l (state)"},
["Alagoas, Brazil"] = {},
["Amapá, Brazil"] = {},
["Amazonas, Brazil"] = {wp = "%l (Brazilian state)"},
["Bahia, Brazil"] = {},
["Ceará, Brazil"] = {},
["Distrito Federal, Brazil"] = {wp = "Federal District (Brazil)"},
["Espírito Santo, Brazil"] = {},
["Goiás, Brazil"] = {},
["Maranhão, Brazil"] = {},
["Mato Grosso, Brazil"] = {},
["Mato Grosso do Sul, Brazil"] = {},
["Minas Gerais, Brazil"] = {},
["Pará, Brazil"] = {},
["Paraíba, Brazil"] = {},
["Paraná, Brazil"] = {wp = "%l (state)"},
["Pernambuco, Brazil"] = {},
["Piauí, Brazil"] = {},
["Rio de Janeiro, Brazil"] = {wp = "%l (state)"},
["Rio Grande do Norte, Brazil"] = {},
["Rio Grande do Sul, Brazil"] = {},
["Rondônia, Brazil"] = {},
["Roraima, Brazil"] = {},
["Santa Catarina, Brazil"] = {wp = "%l (state)"},
["São Paulo, Brazil"] = {wp = "%l (state)"},
["Sergipe, Brazil"] = {},
["Tocantins, Brazil"] = {},
}
-- states of Brazil
export.brazil_group = {
default_container = "Brazil",
default_placetype = "state",
default_divs = "municipalities",
data = export.brazil_states,
}
-- provinces (a.k.a. oblasts) of Bulgaria
export.bulgaria_provinces = {
["Blagoevgrad Province, Bulgaria"] = {},
["Burgas Province, Bulgaria"] = {},
["Dobrich Province, Bulgaria"] = {},
["Gabrovo Province, Bulgaria"] = {},
["Haskovo Province, Bulgaria"] = {},
["Kardzhali Province, Bulgaria"] = {},
["Kyustendil Province, Bulgaria"] = {},
["Lovech Province, Bulgaria"] = {},
["Montana Province, Bulgaria"] = {},
["Pazardzhik Province, Bulgaria"] = {},
["Pernik Province, Bulgaria"] = {},
["Pleven Province, Bulgaria"] = {},
["Plovdiv Province, Bulgaria"] = {},
["Razgrad Province, Bulgaria"] = {},
["Ruse Province, Bulgaria"] = {},
["Shumen Province, Bulgaria"] = {},
["Silistra Province, Bulgaria"] = {},
["Sliven Province, Bulgaria"] = {},
["Smolyan Province, Bulgaria"] = {},
["Sofia City Province, Bulgaria"] = {},
["Sofia Province, Bulgaria"] = {},
["Stara Zagora Province, Bulgaria"] = {},
["Targovishte Province, Bulgaria"] = {},
["Varna Province, Bulgaria"] = {},
["Veliko Tarnovo Province, Bulgaria"] = {},
["Vidin Province, Bulgaria"] = {},
["Vratsa Province, Bulgaria"] = {},
["Yambol Province, Bulgaria"] = {},
}
export.bulgaria_group = {
key_to_placename = make_key_to_placename(", Bulgaria$", " Province$"),
placename_to_key = make_placename_to_key(", Bulgaria", " Province"),
default_container = "Bulgaria",
--== source: https://en.wikipedia.org/wiki/NUTS_statistical_regions_of_Bulgaria ==
divs = {"regions", "planning regions", "provinces", "municipalities", "settlements"},
default_placetype = "province",
data = export.bulgaria_provinces,
}
export.canada_provinces_and_territories = {
["Alberta, Canada"] = {divs = {
{type = "municipal districts", container_parent_type = "rural municipalities"},
}},
["British Columbia, Canada"] = {divs =
{type = "regional districts", container_parent_type = false},
"regional municipalities",
},
["Manitoba, Canada"] = {divs = {"rural municipalities"}},
["New Brunswick, Canada"] = {divs = {"counties", "parishes", {type = "civil parishes", cat_as = "parishes"}}},
["Newfoundland and Labrador, Canada"] = {},
["Northwest Territories, Canada"] = {the = true, placetype = "territory"},
["Nova Scotia, Canada"] = {divs = {"counties", "regional municipalities"}},
["Nunavut, Canada"] = {placetype = "territory"},
["Ontario, Canada"] = {divs = {"counties", "regional municipalities", {type = "townships", prep = "in"}}},
["Prince Edward Island, Canada"] = {divs = {"counties", "parishes", "rural municipalities"}},
["Saskatchewan, Canada"] = {divs = {"rural municipalities"}},
["Quebec, Canada"] = {divs = {
"counties",
{type = "regional county municipalities", container_parent_type = "regional municipalities"},
-- administrative regions have an official (but non-governmental) function but there don't appear to be any
-- equivalent regions elsewhere in Canada, so disable the [[Category:Regions of Canada]] grouping
{type = "regions", container_parent_type = false},
{type = "townships", prep = "in"},
{type = "parish municipalities", cat_as = {{type = "parishes", container_parent_type = "counties"}, "municipalities"}},
{type = "township municipalities", cat_as = {{type = "townships", prep = "in"}, "municipalities"}},
{type = "village municipalities", cat_as = {{type = "villages", prep = "in"}, "municipalities"}},
}},
["Yukon, Canada"] = {placetype = "territory"},
["Yukon Territory, Canada"] = {alias_of = "Yukon, Canada", the = true},
}
-- provinces and territories of Canada
export.canada_group = {
default_container = "Canada",
default_placetype = "province",
data = export.canada_provinces_and_territories,
}
export.china_provinces_and_autonomous_regions = {
-- direct-administered municipalities are not here but below under prefecture-level cities
["Anhui, China"] = {},
["Fujian, China"] = {},
["Fuchien, China"] = {alias_of = "Fujian, China", display = true},
["Gansu, China"] = {},
["Guangdong, China"] = {},
["Guangxi, China"] = {placetype = "autonomous region"},
["Guizhou, China"] = {},
["Hainan, China"] = {},
["Hebei, China"] = {},
["Heilongjiang, China"] = {},
["Henan, China"] = {},
["Hubei, China"] = {},
["Hunan, China"] = {},
["Inner Mongolia, China"] = {placetype = "autonomous region"},
["Jiangsu, China"] = {},
["Jiangxi, China"] = {},
["Jilin, China"] = {},
["Liaoning, China"] = {},
["Ningxia, China"] = {placetype = "autonomous region"},
["Qinghai, China"] = {},
["Shaanxi, China"] = {},
["Shandong, China"] = {},
["Shanxi, China"] = {},
["Sichuan, China"] = {},
["Tibet, China"] = {placetype = "autonomous region", wp = "Tibet Autonomous Region"},
["Xinjiang, China"] = {placetype = "autonomous region"},
["Yunnan, China"] = {},
["Zhejiang, China"] = {},
}
-- provinces and autonomous regions of China
export.china_group = {
default_container = "China",
default_placetype = "province",
default_divs = {
"prefectures", "prefecture-level cities",
"districts", "subdistricts", "townships",
{type = "counties", cat_as = "counties and county-level cities"},
{type = "county-level cities", cat_as = "counties and county-level cities"},
},
data = export.china_provinces_and_autonomous_regions,
}
export.china_prefecture_level_cities = {
-- In China, a "prefecture-level city" is not a city in any real sense. It is rather a prefecture, which is an
-- administrative unit smaller than a province but bigger than a county, which is administratively controlled by
-- the chief city of the prefecture (which bears the same name as the prefecture), in a unified government. Prior
-- to the mid-1980's, in fact, prefecture-level cities *were* prefectures, and a few of them (especially in the
-- western portion of China) have not yet been converted. Generally a given province is entirely tiled by
-- prefecture-level cities, another indication that they should be treated as prefectures and not cities per se.
-- Yet another indication is that prefecture-level cities can contain counties and county-level cities (which, much
-- like prefecture-level cities, are effectively counties surrounding a chief city of the county, again which bears
-- the same name as the county-level city).
--
-- For this reason, we treat prefecture-level cities as non-city political divisions, and separately enumerate the
-- most populous so we can separately categorize districts and counties under them instead of lumping them at the
-- province level.
--
-- Note also that China separately distinguishes "urban area" from "metro area". Sometimes the two figures are
-- identical but sometimes the metro area is larger (and very occasionally smaller, which I assume is an error). I'm
-- guessing that the "urban area" is the contiguous urban area over a certain density while the metro area includes
-- all urban areas above a certain density; when the latter is greater, it's because of satellite cities in the
-- metro area separated by suburban/exurban or rural land.
-- At first I chose all prefecture/province-level cities with a total prefecture/province-level population of at
-- least 6,000,000 per the 2020 census with data taken from https://www.citypopulation.de/en/china/admin/ (a total
-- of 67, including the four direct-administered municipalities), and also chose all prefecture/province-level
-- cities whose "urban population" was at least 2,000,000 per the 2020 census with data taken from Wikipedia
-- [[w:List of cities in China by population#Cities and towns by population]] (a total of 61 cities; if we cut off
-- at 1.5 million we'd have 84 cities, and if we cut off at 1 million we'd have 105 cities). Merging them produces
-- 87 cities. Note that this leaves off a few well-known cities (Guilin, Qiqihar, Kashgar, Lhasa, ...) but includes
-- a lot of obscure cities.
--
-- At a later date I added all cities from citypopulation.de whose "urban" population per the 2020 China census was
-- >= 1 million, and then finally added all urban agglomerations from citypopulation.de whose 2025-01-01 estimate
-- was >= 1 million. These are sorted below by the urban agglomeration value (which is generally of the "adm-urb" =
-- "administrative area (urban population)" type) and sometimes groups nearby cities into a single agglomeration
-- (most notably in the case of the Pearl River Delta, grouped under Guangzhou with an agglomeration population of
-- 72,700,000 but including a large number of nearby large cities in the agglomeration (although for some reason not
-- Hong Kong, maybe due to the administrative issues involved). In addition, citypopulation.de includes divisions
-- under a prefecture-level city if they are city-like and have an agglomeration population of at least 1 million;
-- this includes several county-level cities, one county and one district (Wanzhou, a "district" of Chongqing
-- despite being 142 miles away). None of the county-level cities or counties have districts under them, only
-- subdistricts, towns and townships.
["Guangzhou"] = {container = "Guangdong"}, -- 18.7 prefectural, 18.8 urban; sub-provincial city; 16.097 urban (72.700 adm-urb including Dongguan, Foshan, Huizhou, Jiangmen, Shenzhen, Zhongshan) per citypopulation.de
["Dongguan"] = {container = "Guangdong"}, -- 10.5 prefectural, 10.5 urban; 9.645 per citypopulation.de; included by citypopulation.de in Guangzhou agglomeration
["Foshan"] = {container = "Guangdong"}, -- 9.5 prefectural, 9.5 urban; 9.043 per citypopulation.de; included by citypopulation.de in Guangzhou agglomeration
["Huizhou"] = {container = "Guangdong"}, -- 6.0 prefectural, 2.5 urban; 2.900 per citypopulation.de; included by citypopulation.de in Guangzhou agglomeration
["Jiangmen"] = {container = "Guangdong"}, -- 4.798 prefectural, 2.7 urban; 1.795 per citypopulation.de; included by citypopulation.de in Guangzhou agglomeration
["Shenzhen"] = {container = "Guangdong"}, -- 17.5 prefectural, 14.7 urban; sub-provincial city; 17.445 per citypopulation.de; included by citypopulation.de in Guangzhou agglomeration
["Zhongshan"] = {container = "Guangdong"}, -- 4.418 prefectural, 4.4 urban; 3.842 per citypopulation.de; included by citypopulation.de in Guangzhou agglomeration
["Shanghai"] = {placetype = {"direct-administered municipality", "municipality", "city"}}, -- 24.9 prefectural, 29.9 urban; 21.910 urban (41.600 adm-urb including Changshu, Changzhou, Suzhou, Wuxi) per citypopulation.de
["Changshu"] = {container = "Jiangsu"}, -- 1.231 urban per citypopulation.de; included by citypopulation.de in Shanghai agglomeration
-- NOTE: Not to be confused with Cangzhou in Hebei
["Changzhou"] = {container = "Jiangsu"}, -- 5.278 prefectural, 3.6 urban; 3.187 urban per citypopulation.de; included by citypopulation.de in Shanghai agglomeration
-- NOTE: There is also a prefecture-level city Suzhou in Anhui with 5.3 million prefectural inhabitants
["Suzhou"] = {container = "Jiangsu"}, -- 12.8 prefectural, 4.3 urban; 5.893 urban per citypopulation.de; included by citypopulation.de in Shanghai agglomeration
["Wuxi"] = {container = "Jiangsu"}, -- 7.5 prefectural, 3.3 urban; 3.957 per citypopulation.de; included by citypopulation.de in Shanghai agglomeration
["Beijing"] = {placetype = {"direct-administered municipality", "municipality", "city"}}, -- 21.9 prefectural, 21.9 urban; 18.961 urban (21.500 adm-urb) per citypopulation.de
["Chengdu"] = {container = "Sichuan"}, -- 20.9 prefectural, 16.9 urban; sub-provincial city; 13.568 urban (18.100 adm-urb) per citypopulation.de
["Xiamen"] = {container = "Fujian"}, -- 5.163 prefectural, 5.2 urban; sub-provincial city; 4.617 urban (15.400 adm-urb including Jinjiang, Quanzhou, Putian) per citypopulation.de
["Jinjiang"] = {container = "Fujian"}, -- 1.416 urban per citypopulation.de; included by citypopulation.de in Xiamen agglomeration
["Quanzhou"] = {container = "Fujian"}, -- 8.8 prefectural, 1.7 urban (6.7 metro); 1.469 urban per citypopulation.de; included by citypopulation.de in Xiamen agglomeration
["Putian"] = {container = "Fujian"}, -- 3.210 prefectural, 2.0 urban; 1.539 urban per citypopulation.de; included by citypopulation.de in Xiamen agglomeration
["Hangzhou"] = {container = "Zhejiang"}, -- 11.9 prefectural, 10.7 urban; sub-provincial city; 9.236 urban (14.600 adm-urb including Shaoxing) per citypopulation.de
["Shaoxing"] = {container = "Zhejiang"}, -- 5.270 prefectural, 2.5 urban; 2.333 urban per citypopulation.de; included by citypopulation.de in Hangzhou agglomeration
["Xi'an"] = {container = "Shaanxi"}, -- 12.1 prefectural, 11.9 urban; sub-provincial city; 9.393 urban (13.400 adm-urb including Xianyang) per citypopulation.de
["Xianyang"] = {container = "Shaanxi"}, -- 1.193 urban per citypopulation.de; included by citypopulation.de in Xi'an agglomeration
["Chongqing"] = {placetype = {"direct-administered municipality", "municipality", "city"}}, -- 32.1 prefectural, 16.9 urban; 9.581 urban (12.900 adm-urb) per citypopulation.de
["Wuhan"] = {container = "Hubei"}, -- 12.4 prefectural, 12.3 urban; sub-provincial city; 10.495 urban (12.600 adm-urb) per citypopulation.de
["Tianjin"] = {placetype = {"direct-administered municipality", "municipality", "city"}}, -- 13.9 prefectural, 13.9 urban; 11.052 urban (11.700 adm-urb) per citypopulation.de
["Changsha"] = {container = "Hunan"}, -- 10.0 prefectural, 6.0 urban; 5.630 urban (11.500 adm-urb including Xiangtan, Zhuzhou) per citypopulation.de
-- Changsha County -- 1.024 urban per citypopulation.de
["Zhuzhou"] = {container = "Hunan"}, -- 1.510 urban per citypopulation.de; included by citypopulation.de in Changsha agglomeration
["Zhengzhou"] = {container = "Henan"}, -- 12.6 prefectural, 6.7 urban; 6.461 urban (10.300 adm-urb) per citypopulation.de
["Nanjing"] = {container = "Jiangsu"}, -- 9.3 prefectural, 9.3 urban; sub-provincial city; 7.520 urban (9.500 adm-urb including Ma'anshan) per citypopulation.de
["Shenyang"] = {container = "Liaoning"}, -- 9.1 prefectural, 7.9 urban; sub-provincial city; 7.026 urban (8.800 adm-urb including Fushun) per citypopulation.de
["Fushun"] = {container = "Liaoning"}, -- 1.229 urban per citypopulation.de; included by citypopulation.de in Shenyang agglomeration
["Hefei"] = {container = "Anhui"}, -- 9.4 prefectural, 4.2 urban; 5.056 urban (8.200 adm-urb) per citypopulation.de
["Shantou"] = {container = "Guangdong"}, -- 5.502 prefectural, 4.3 urban; 3.839 urban (8.050 adm-urb including Chaozhou, Jieyang, Puning) per citypopulation.de
["Chaozhou"] = {container = "Guangdong"}, -- 1.254 urban per citypopulation.de; included by citypopulation.de in Shantou agglomeration
["Jieyang"] = {container = "Guangdong"}, -- 1.243 urban per citypopulation.de; included by citypopulation.de in Shantou agglomeration
["Qingdao"] = {container = "Shandong"}, -- 10.1 prefectural, 7.1 urban; sub-provincial city; 6.165 urban (7.700 adm-urb) per citypopulation.de
["Ningbo"] = {container = "Zhejiang"}, -- 9.4 prefectural, 5.1 urban; sub-provincial city; 3.731 urban (7.600 adm-urb including Cixi, Yuyao) per citypopulation.de
["Cixi"] = {container = "Zhejiang"}, -- 1.458 urban per citypopulation.de; included by citypopulation.de in Ningbo agglomeration
["Yuyao"] = {container = "Zhejiang"}, -- 1.014 urban per citypopulation.de; included by citypopulation.de in Ningbo agglomeration
-- Hong Kong 7.500 agglomeration per citypopulation.de 2025-01-01 estimate including Kowloon, Victoria
["Wenzhou"] = {container = "Zhejiang"}, -- 9.6 prefectural, 3.6 urban; 2.582 urban (7.000 adm-urb including Rui'an, Cangnan, Pingyang) per citypopulation.de
-- Rui'an is a "county-level city" of the "prefecture-level city" of Wenzhou but in fact is 19 miles away from Wenzhou city proper (urban core to urban core).
["Rui'an"] = {placetype = "county-level city", container = {key = "Wenzhou", placetype = "prefecture-level city"}, divs = {"subdistricts", "townships"}}, -- 1.013 urban per citypopulation.de; included by citypopulation.de in Wenzhou agglomeration
["Kunming"] = {container = "Yunnan"}, -- 8.5 prefectural, 6.0 urban; 5.273 urban (6.800 adm-urb) per citypopulation.de
-- includes Láiwú city
["Jinan"] = {container = "Shandong", wp = "%l, %c"}, -- 9.2 prefectural, 8.4 urban; sub-provincial city; 5.648 urban (6.750 adm-urb) per citypopulation.de
-- includes Xīnjí city
["Shijiazhuang"] = {container = "Hebei"}, -- 11.2 prefectural, 4.1 urban; 5.090 urban (6.450 adm-urb) per citypopulation.de
["Taiyuan"] = {container = "Shanxi"}, -- 5.304 prefectural, 4.5 urban; 4.304 urban (6.150 adm-urb) per citypopulation.de
["Harbin"] = {container = "Heilongjiang"}, -- 10.0 prefectural, 7.0 urban; sub-provincial city; 5.243 urban (5.550 adm-urb) per citypopulation.de
["Nanning"] = {container = {key = "Guangxi, China", placetype = "autonomous region"}}, -- 8.7 prefectural, 3.8 urban; 4.583 urban (5.550 adm-urb) per citypopulation.de
["Dalian"] = {container = "Liaoning"}, -- 7.5 prefectural, 5.7 urban; sub-provincial city; 4.914 urban (5.400 adm-urb) per citypopulation.de
["Guiyang"] = {container = "Guizhou"}, -- 5.987 prefectural, 3.5 urban; 4.021 urban (5.300 adm-urb) per citypopulation.de
["Changchun"] = {container = "Jilin"}, -- 9.1 prefectural, 5.7 urban; sub-provincial city; 4.557 urban (5.200 adm-urb) per citypopulation.de
["Nanchang"] = {container = "Jiangxi"}, -- 6.3 prefectural, 3.6 (3.9?) urban, 5.3 metro; 3.519 urban (5.150 adm-urb) per citypopulation.de
["Ürümqi"] = {container = {key = "Xinjiang, China", placetype = "autonomous region"}}, -- 4.054 prefectural, 4.3 urban; 3.843 urban (5.000 adm-urb) per citypopulation.de
["Urumqi"] = {alias_of = "Ürümqi", display = true},
["Fuzhou"] = {container = "Fujian"}, -- 8.3 prefectural, 4.1 urban; 3.723 urban (4.775 adm-urb) per citypopulation.de
["Linyi"] = {container = "Shandong"}, -- 11.0 prefectural, 2.3 urban; 2.744 urban (4.650 adm-urb) per citypopulation.de
["Zibo"] = {container = "Shandong"}, -- 4.704 prefectural, 2.6 urban; 2.750 urban (3.975 adm-urb) per citypopulation.de
["Luoyang"] = {container = "Henan"}, -- 7.1 prefectural, 2.4 urban; 2.231 urban (3.750 adm-urb) per citypopulation.de
["Lanzhou"] = {container = "Gansu"}, -- 4.359 prefectural, 3.1 urban; 3.013 urban (3.575 adm-urb) per citypopulation.de
["Nantong"] = {container = "Jiangsu"}, -- 7.7 prefectural, 2.3 urban; 2.988 urban (3.475 adm-urb) citypopulation.de
["Weifang"] = {container = "Shandong"}, -- 9.4 prefectural, 2.7 urban; 1.998 urban (3.325 adm-urb) per citypopulation.de
["Jiangyin"] = {container = "Jiangsu"}, -- 1.331 urban (3.200 adm-urb including Zhangjiagang) per citypopulation.de
["Zhangjiagang"] = {container = "Jiangsu"}, -- 1.056 urban per citypopulation.de; included in Jiangyin figures
["Xuzhou"] = {container = "Jiangsu"}, -- 9.1 prefectural, 2.6 urban; 2.846 urban (3.150 adm-urb) per citypopulation.de
["Handan"] = {container = "Hebei"}, -- 9.4 prefectural, 2.8 urban; 2.095 urban (2.925 adm-urb) per citypopulation.de
["Hohhot"] = {container = {key = "Inner Mongolia, China", placetype = "autonomous region"}}, -- 3.446 prefectural, 2.7 urban; 2.373 urban (2.850 adm-urb) per citypopulation.de
["Haikou"] = {container = "Hainan"}, -- 2.873 prefectural, 2.3 urban; 2.349 urban (2.800 adm-urb) per citypopulation.de
["Tangshan"] = {container = "Hebei"}, -- 7.7 prefectural, 3.4 urban; 2.550 urban (2.750 adm-urb) per citypopulation.de
["Xinxiang"] = {container = "Henan"}, -- 6.3 prefectural, 1.2 urban, 2.7 metro; 1.271 urban (2.700 adm-urb) per citypopulation.de
["Yiwu"] = {container = "Zhejiang"}, -- 1.481 urban (2.700 adm-urb) per citypopulation.de
["Zhuhai"] = {container = "Guangdong"}, -- 2.439 prefectural, 2.4 urban; 2.207 urban (2.675 adm-urb) per citypopulation.de
["Taizhou, Zhejiang"] = {container = "Zhejiang"}, -- 6.6 prefectural, 1.6 urban; 1.486 urban (2.625 adm-urb) per citypopulation.de
["Taizhou"] = {alias_of = "Taizhou, Zhejiang"},
["Yantai"] = {container = "Shandong"}, -- 7.1 prefectural, 2.5 urban; 2.312 urban (2.550 adm-urb) per citypopulation.de
["Yinchuan"] = {container = {key = "Ningxia, China", placetype = "autonomous region"}}, -- 1.663 urban (2.525 adm-urb) per citypopulation.de
["Liuzhou"] = {container = {key = "Guangxi, China", placetype = "autonomous region"}}, -- 4.157 prefectural, 2.2 urban; 2.205 urban (2.500 adm-urb) per citypopulation.de
["Anshan"] = {container = "Liaoning"}, -- 1.480 urban (2.350 adm-urb including Liáoyáng) per citypopulation.de
["Yangzhou"] = {container = "Jiangsu"}, -- 2.067 urban (2.300 adm-urb) per citypopulation.de
["Jiaxing"] = {container = "Zhejiang"}, -- 1.188 urban (2.275 adm-urb) per citypopulation.de
["Xining"] = {container = "Qinghai"}, -- 1.677 urban (2.250 adm-urb) per citypopulation.de
-- includes Dìngzhōu city and Xióngān Xīnqū
["Baoding"] = {container = "Hebei"}, -- 11.5 prefectural, 2.0 urban; 1.940 urban (2.225 adm-urb) per citypopulation.de
["Baotou"] = {container = {key = "Inner Mongolia, China", placetype = "autonomous region"}}, -- 2.709 prefectural, 2.2 urban; 2.104 urban (2.200 adm-urb) per citypopulation.de
["Ganzhou"] = {container = "Jiangxi"}, -- 9.0 prefectural, 1.6 urban; 1.778 urban (2.150 adm-urb) per citypopulation.de
["Pingdingshan"] = {container = "Henan"}, -- 1.046 urban (2.100 adm-urb) per citypopulation.de
["Zunyi"] = {container = "Guizhou"}, -- 6.6 prefectural, 2.4 urban/metro; 1.675 urban (2.025 adm-urb) per citypopulation.de
["Bengbu"] = {container = "Anhui"}, -- 1.078 urban (2.000 adm-urb) per citypopulation.de
["Datong"] = {container = "Shanxi"}, -- 3.105 prefectural, 2.0 urban; 1.810 urban (2.000 adm-urb) per citypopulation.de
["Anyang"] = {container = "Henan"}, -- 1.188 urban (1.960 adm-urb) per citypopulation.de
["Huai'an"] = {container = "Jiangsu"}, -- 4.556 prefectural, 2.6 urban; 1.805 urban (1.940 adm-urb) per citypopulation.de
["Zaozhuang"] = {container = "Shandong"}, -- 1.350 urban (1.900 adm-urb) per citypopulation.de
["Zhanjiang"] = {container = "Guangdong"}, -- 7.0 prefectural, 1.9 urban; 1.401 urban (1.890 adm-urb) per citypopulation.de
["Huainan"] = {container = "Anhui"}, -- 1.256 urban (1.880 adm-urb) per citypopulation.de
["Jining"] = {container = "Shandong"}, -- 8.4 prefectural, 1.5 urban; 1.700 urban (1.880 adm-urb) per citypopulation.de
["Daqing"] = {container = "Heilongjiang"}, -- 1.604 urban (1.860 adm-urb) per citypopulation.de
["Wuhu"] = {container = "Anhui"}, -- 1.598 urban (1.850 adm-urb) per citypopulation.de
["Guilin"] = {container = {key = "Guangxi, China", placetype = "autonomous region"}}, -- 1.361 urban (1.830 adm-urb) per citypopulation.de
["Mianyang"] = {container = "Sichuan"}, -- 1.549 urban (1.800 adm-urb) per citypopulation.de
["Xiangyang"] = {container = "Hubei"}, -- 1.686 urban (1.800 adm-urb) per citypopulation.de
["Huzhou"] = {container = "Zhejiang"}, -- 1.084 urban (1.750 adm-urb) per citypopulation.de
["Puyang"] = {container = "Henan"}, -- 0.824 urban (1.750 adm-urb) per citypopulation.de
["Shangqiu"] = {container = "Henan"}, -- 7.8 prefectural, 1.9 urban (2.8 metro); 1.031 urban (1.750 adm-urb) per citypopulation.de
["Qinhuangdao"] = {container = "Hebei"}, -- 1.520 urban (1.740 adm-urb) per citypopulation.de
["Xingtai"] = {container = "Hebei"}, -- 7.1 prefectural, 971,000 urban; 1.5 urban (1.700 adm-urb) per citypopulation.de
["Nanyang"] = {container = "Henan", wp = "%l, %c"}, -- 9.7 prefectural, 2.1 urban/metro; 1.481 urban (1.680 adm-urb) per citypopulation.de
["Jiaozuo"] = {container = "Henan"}, -- 0.875 urban (1.640 adm-urb) per citypopulation.de
["Jilin City"] = {container = "Jilin"}, -- 1.509 urban (1.610 adm-urb) per citypopulation.de
["Jilin"] = {alias_of = "Jilin City"},
["Jinhua"] = {container = "Zhejiang"}, -- 7.1 prefectural, 1.5 urban; 1.041 urban (1.590 adm-urb) per citypopulation.de
["Shangrao"] = {container = "Jiangxi"}, -- 6.5 prefectural, 2.1 urban, 1.3 metro [sic]; 1.342 urban (1.580 adm-urb) per citypopulation.de
["Heze"] = {container = "Shandong"}, -- 8.8 prefectural, 1.3 urban; 1.294 urban (1.570 adm-urb) per citypopulation.de
["Yulin"] = {container = {key = "Guangxi, China", placetype = "autonomous region"}, wp = "%l, %c"}, -- 0.878 urban (1.570 adm-urb) per citypopulation.de
["Tai'an"] = {container = "Shandong"}, -- 1.417 urban (1.560 adm-urb) per citypopulation.de
["Weihai"] = {container = "Shandong"}, -- 1.340 urban (1.510 adm-urb) per citypopulation.de
-- Taizhou, Jiangsu would be here (1.490 adm-urb) but moved to china_prefecture_level_cities_2 to avoid clash
["Yancheng"] = {container = "Jiangsu"}, -- 6.7 prefectural, 1.6 urban; 1.353 urban (1.460 adm-urb) per citypopulation.de
["Zhangjiakou"] = {container = "Hebei"}, -- 1.339 urban (1.450 adm-urb) per citypopulation.de
["Maoming"] = {container = "Guangdong"}, -- 6.2 prefectural, 2.5 urban; 1.308 urban (1.440 adm-urb) per citypopulation.de
["Nanchong"] = {container = "Sichuan"}, -- 1.254 urban (1.440 adm-urb) per citypopulation.de
["Fuyang"] = {container = "Anhui", wp = "%l, %c"}, -- 8.2 prefectural, 2.1 urban; 1.191 urban (1.410 adm-urb) per citypopulation.de
["Xuchang"] = {container = "Henan"}, -- 0.850 urban (1.390 adm-urb) per citypopulation.de
["Yichang"] = {container = "Hubei"}, -- 1.284 urban (1.390 adm-urb) per citypopulation.de
["Dazhou"] = {container = "Sichuan"}, -- 1.136 urban (1.380 adm-urb) per citypopulation.de
["Kaifeng"] = {container = "Henan"}, -- 1.194 urban (1.340 adm-urb) per citypopulation.de
["Luzhou"] = {container = "Sichuan"}, -- 1.128 urban (1.340 adm-urb) per citypopulation.de
["Qingyuan"] = {container = "Guangdong"}, -- 1.198 urban (1.340 adm-urb) per citypopulation.de
["Huaibei"] = {container = "Anhui"}, -- 0.831 urban (1.330 adm-urb) per citypopulation.de
["Yibin"] = {container = "Sichuan"}, -- 1.101 urban (1.310 adm-urb) per citypopulation.de
["Lu'an"] = {container = "Anhui"}, -- 1.070 urban (1.300 adm-urb) per citypopulation.de
["Dezhou"] = {container = "Shandong"}, -- 0.843 urban (1.290 adm-urb) per citypopulation.de
["Rizhao"] = {container = "Shandong"}, -- 1.147 urban (1.270 adm-urb) per citypopulation.de
["Changzhi"] = {container = "Shanxi"}, -- 1.047 urban (1.250 adm-urb) per citypopulation.de
["Hengyang"] = {container = "Hunan"}, -- 6.6 prefectural, 1.5 urban; 1.185 urban (1.250 adm-urb) per citypopulation.de
["Jinzhou"] = {container = "Liaoning"}, -- 1.021 urban (1.240 adm-urb) per citypopulation.de
["Liaocheng"] = {container = "Shandong"}, -- 1.020 urban (1.240 adm-urb) per citypopulation.de
["Changde"] = {container = "Hunan"}, -- 1.101 urban (1.230 adm-urb) per citypopulation.de
["Suqian"] = {container = "Jiangsu"}, -- 1.082 urban (1.230 adm-urb) per citypopulation.de
["Xinyang"] = {container = "Henan"}, -- 6.2 prefectural, 1.4 urban/metro; 1.015 urban (1.230 adm-urb) per citypopulation.de
["Baoji"] = {container = "Shaanxi"}, -- 1.108 urban (1.220 adm-urb) per citypopulation.de
["Yueyang"] = {container = "Hunan"}, -- 1.125 urban (1.220 adm-urb) per citypopulation.de
["Zhenjiang"] = {container = "Jiangsu"}, -- 1.124 urban (1.210 adm-urb) per citypopulation.de
-- Wanzhou is a "district" of the "direct-administered municipality" of Chongqing but in fact is 142 miles away from Chongqing city proper.
["Wanzhou"] = {placetype = "district", container = {key = "Chongqing", placetype = "direct-administered municipality"}, divs = {"subdistricts", "townships"}, wp = "%l, %c"}, -- 1.078 urban (1.190 adm-urb) per citypopulation.de
["Ulanhad"] = {container = {key = "Inner Mongolia, China", placetype = "autonomous region"}}, -- 1.093 urban (1.180 adm-urb) per citypopulation.de
["Chifeng"] = {alias_of = "Ulanhad"},
["Ulankhad"] = {alias_of = "Ulanhad", display = true},
["Ezhou"] = {container = "Hubei"}, -- < 0.750 urban (1.180 adm-urb) per citypopulation.de
["Zhaoqing"] = {container = "Guangdong"}, -- 1.036 urban (1.160 adm-urb) per citypopulation.de
["Lianyungang"] = {container = "Jiangsu"}, -- 4.599 prefectural, 2.0 urban; 1.071 urban (1.150 adm-urb) per citypopulation.de
["Qujing"] = {container = "Yunnan"}, -- 0.976 urban (1.150 adm-urb) per citypopulation.de
-- Shuyang is a "county" of the "prefecture-level city" of Suqian but in fact is 38 miles away from Suqian city proper (urban core to urban core).
-- The county itself is 37 miles by 34 miles.
["Shuyang"] = {placetype = "county", container = {key = "Suqian", placetype = "prefecture-level city"}, divs = {"subdistricts", "townships"}, wp = "%l County"}, -- 0.986 urban (1.120 adm-urb) per citypopulation.de
-- Yongkang is a "county-level city" of the "prefecture-level city" of Jinhua but in fact is 32 miles away from Jinhua city proper (urban core to urban core).
["Yongkang"] = {placetype = "county-level city", container = {key = "Jinhua", placetype = "prefecture-level city"}, divs = {"subdistricts", "townships"}, wp = "%l, Zhejiang"}, -- < 0.750 urban (1.110 adm-urb) per citypopulation.de
["Zhoukou"] = {container = "Henan"}, -- 9.0 prefectural, 721,000 urban (1.6 metro); < 0.750 urban (1.100 adm-urb) per citypopulation.de
["Beihai"] = {container = {key = "Guangxi, China", placetype = "autonomous region"}}, -- < 1 urban (1.090 adm-urb) per citypopulation.de
["Jiujiang"] = {container = "Jiangxi"}, -- < 0.750 urban (1.080 adm-urb) per citypopulation.de
["Shaoyang"] = {container = "Hunan"}, -- 6.6 prefectural, 802,000 urban, 1.4 metro; < 1 urban (1.080 adm-urb) per citypopulation.de
["Chuzhou"] = {container = "Anhui"}, -- < 0.750 urban (1.070 adm-urb) per citypopulation.de
["Hengshui"] = {container = "Hebei"}, -- 0.885 urban (1.070 adm-urb) per citypopulation.de
["Shiyan"] = {container = "Hubei"}, -- 0.955 urban (1.070 adm-urb) per citypopulation.de
["Huludao"] = {container = "Liaoning"}, -- 0.764 urban (1.060 adm-urb) per citypopulation.de
["Dongying"] = {container = "Shandong"}, -- 0.961 urban (1.050 adm-urb) per citypopulation.de
["Guigang"] = {container = {key = "Guangxi, China", placetype = "autonomous region"}}, -- 0.921 urban (1.050 adm-urb) per citypopulation.de
-- Liuyang is a "county-level city" of the "prefecture-level city" of Changsha but in fact is 47 miles away from Changsha city proper (urban core to urban core).
["Liuyang"] = {placetype = "county-level city", container = {key = "Changsha", placetype = "prefecture-level city"}, divs = {"subdistricts", "townships"}}, -- 0.886 urban (1.040 adm-urb) per citypopulation.de
-- NOTE: Not to be confused with Changzhou in Jiangsu
["Cangzhou"] = {container = "Hebei"}, -- 7.3 prefectural, 621,000 urban; 0.759 urban (1.030 adm-urb) per citypopulation.de
["Liupanshui"] = {container = "Guizhou"}, -- < 0.750 urban (1.030 adm-urb) per citypopulation.de
["Panjin"] = {container = "Liaoning"}, -- 0.980 urban (1.030 adm-urb) per citypopulation.de
["Qiqihar"] = {container = "Heilongjiang"}, -- 1.030 urban (1.030 adm-urb) per citypopulation.de
["Linfen"] = {container = "Shanxi"}, -- < 0.750 urban (1.010 adm-urb) per citypopulation.de
-- Tengzhou is a "county-level city" of the "prefecture-level city" of Zaozhuang but in fact is 30 miles away from Zaozhuang city proper (urban core to urban core).
["Tengzhou"] = {placetype = "county-level city", container = {key = "Zaozhuang", placetype = "prefecture-level city"}, divs = {"subdistricts", "townships"}}, -- 0.937 urban (1.010 adm-urb) per citypopulation.de
-- 3 extra that got added in earlier incarnations and aren't found in the "major agglomerations of the world" page https://citypopulation.de/en/world/agglomerations/ reference date 2025-01-01
["Kunshan"] = {container = "Jiangsu"}, -- 1.652 urban (2020 China census) per citypopulation.de
["Zhumadian"] = {container = "Henan"}, -- 7.0 prefectural, 722,000 urban per Wikipedia; 0.754 urban per citypopulation.de
["Bijie"] = {container = "Guizhou"}, -- 6.9 prefectural, ? urban, ? metro (not listed in Wikipedia); < 0.750 urban per citypopulation.de
}
export.china_prefecture_level_cities_group = {
-- don't do any transformations between key and placename; in particular, don't chop off anything from
-- "Taizhou, Zhejiang" or "Suzhou, Anhui".
key_to_placename = false,
placename_to_key = false, -- don't add ", China" to make the key
default_container = "China",
canonicalize_key_container = make_canonicalize_key_container(", China", "province"),
-- Prefecture-level cities aren't really cities but allow them to be identified that way, as many people
-- don't understand how Chinese administrative divisions work.
default_placetype = {"prefecture-level city", "city"},
default_divs = {
-- "towns" (but not "townships") are automatically added as they are specified as generic_before_non_cities,
-- and prefecture-level cities (as well as county-level cities) are considered non-cities.
"districts", "subdistricts", "townships",
{type = "counties", cat_as = "counties and county-level cities"},
{type = "county-level cities", cat_as = "counties and county-level cities"},
},
data = export.china_prefecture_level_cities,
}
-- Needed to avoid problems with two cities called Taizhou and Suzhou.
export.china_prefecture_level_cities_2 = {
-- NOTE: There is also a larger and better-known prefecture-level city Taizhou in Zhejiang.
["Taizhou, Jiangsu"] = {container = "Jiangsu"}, -- 1.3 urban (1.490 adm-urb) per citypopulation.de 2020 census
["Taizhou"] = {alias_of = "Taizhou, Jiangsu"},
-- NOTE: There is also a larger and better-known prefecture-level city Suzhou in Jiangsu.
["Suzhou, Anhui"] = {container = "Anhui"}, -- 5.3 prefectural, 1.766 metro and "urban"; < 1 urban (1.010 adm-urb) per citypopulation.de 2020 census
-- hopefully this will work because we also have Suzhou as a key by itself for the larger, more-well-known Suzhou in Jiangsu
["Suzhou"] = {alias_of = "Suzhou, Anhui"},
}
export.china_prefecture_level_cities_group_2 = {
-- don't do any transformations between key and placename; in particular, don't chop off anything from
-- "Taizhou, Jiangsu".
placename_to_key = false, -- don't add ", China" to make the key
default_container = "China",
canonicalize_key_container = make_canonicalize_key_container(", China", "province"),
-- Prefecture-level cities aren't really cities but allow them to be identified that way, as many people
-- don't understand how Chinese administrative divisions work.
default_placetype = {"prefecture-level city", "city"},
default_divs = {
-- "towns" (but not "townships") are automatically added as they are specified as generic_before_non_cities,
-- and prefecture-level cities (as well as county-level cities) are considered non-cities.
"districts", "subdistricts", "townships",
{type = "counties", cat_as = "counties and county-level cities"},
{type = "county-level cities", cat_as = "counties and county-level cities"},
},
data = export.china_prefecture_level_cities_2,
}
export.finland_regions = {
["Lapland, Finland"] = {wp = "%l (%c)"},
["North Ostrobothnia, Finland"] = {},
["Northern Ostrobothnia, Finland"] = {alias_of = "North Ostrobothnia, Finland", display = true},
["Kainuu, Finland"] = {},
["North Karelia, Finland"] = {},
["Northern Savonia, Finland"] = {},
["North Savo, Finland"] = {alias_of = "Northern Savonia, Finland", display = true},
["Southern Savonia, Finland"] = {},
["South Savo, Finland"] = {alias_of = "Southern Savonia, Finland", display = true},
["South Karelia, Finland"] = {},
["Central Finland, Finland"] = {},
["South Ostrobothnia, Finland"] = {},
["Southern Ostrobothnia, Finland"] = {alias_of = "South Ostrobothnia, Finland", display = true},
["Ostrobothnia, Finland"] = {wp = "%l (region)"},
["Central Ostrobothnia, Finland"] = {},
["Pirkanmaa, Finland"] = {},
["Satakunta, Finland"] = {},
["Päijänne Tavastia, Finland"] = {},
["Päijät-Häme, Finland"] = {alias_of = "Päijänne Tavastia, Finland", display = true},
["Tavastia Proper, Finland"] = {},
["Kanta-Häme, Finland"] = {alias_of = "Tavastia Proper, Finland", display = true},
["Kymenlaakso, Finland"] = {},
["Uusimaa, Finland"] = {},
["Southwest Finland, Finland"] = {},
["Åland Islands, Finland"] = {the = true, wp = "Åland"},
["Åland, Finland"] = {alias_of = "Åland Islands, Finland"}, -- differs in "the"
}
-- regions of Finland
export.finland_group = {
default_container = "Finland",
default_placetype = "region",
default_divs = "municipalities",
data = export.finland_regions,
}
export.france_administrative_regions = {
["Auvergne-Rhône-Alpes, France"] = {},
["Bourgogne-Franche-Comté, France"] = {},
["Brittany, France"] = {wp = "%l (administrative region)"},
["Centre-Val de Loire, France"] = {},
["Corsica, France"] = {},
-- overseas departments are handled in `export.country_like_entities`
-- ["French Guiana"] = {},
["Grand Est, France"] = {},
-- ["Guadeloupe"] = {},
["Hauts-de-France, France"] = {},
["Île-de-France, France"] = {},
-- ["Martinique"] = {},
-- ["Mayotte"] = {},
["Normandy, France"] = {wp = "%l (administrative region)"},
["Nouvelle-Aquitaine, France"] = {},
["Occitania, France"] = {wp = "%l (administrative region)"},
["Occitanie, France"] = {alias_of = "Occitania, France", display = true},
["Pays de la Loire, France"] = {},
["Provence-Alpes-Côte d'Azur, France"] = {},
-- ["Réunion"] = {},
}
-- administrative regions of France
export.france_group = {
default_container = "France",
-- Canonically these are 'administrative regions' but also treat as 'region' ('administrative region' falls back
-- to 'region').
default_placetype = "region",
default_divs = {
"communes",
{type = "municipalities", cat_as = "communes"},
"departments",
{type = "prefectures", cat_as = {"prefectures", "departmental capitals"}},
{type = "French prefectures", cat_as = {"prefectures", "departmental capitals"}},
},
data = export.france_administrative_regions,
}
export.france_departments = {
["Ain, France"] = {container = "Auvergne-Rhône-Alpes"}, -- 01
["Aisne, France"] = {container = "Hauts-de-France"}, -- 02
["Allier, France"] = {container = "Auvergne-Rhône-Alpes"}, -- 03
["Alpes-de-Haute-Provence, France"] = {container = "Provence-Alpes-Côte d'Azur"}, -- 04
["Hautes-Alpes, France"] = {container = "Provence-Alpes-Côte d'Azur"}, -- 05
["Alpes-Maritimes, France"] = {container = "Provence-Alpes-Côte d'Azur"}, -- 06
["Ardèche, France"] = {container = "Auvergne-Rhône-Alpes"}, -- 07
["Ardennes, France"] = {container = "Grand Est", wp = "%l (department)"}, -- 08
["Ariège, France"] = {container = "Occitania", wp = "%l (department)"}, -- 09
["Aube, France"] = {container = "Grand Est"}, -- 10
["Aude, France"] = {container = "Occitania"}, -- 11
["Aveyron, France"] = {container = "Occitania"}, -- 12
["Bouches-du-Rhône, France"] = {container = "Provence-Alpes-Côte d'Azur"}, -- 13
["Calvados, France"] = {container = "Normandy", wp = "%l (department)"}, -- 14
["Cantal, France"] = {container = "Auvergne-Rhône-Alpes"}, -- 15
["Charente, France"] = {container = "Nouvelle-Aquitaine"}, -- 16
["Charente-Maritime, France"] = {container = "Nouvelle-Aquitaine"}, -- 17
["Cher, France"] = {container = "Centre-Val de Loire", wp = "%l (department)"}, -- 18
["Corrèze, France"] = {container = "Nouvelle-Aquitaine"}, -- 19
["Corse-du-Sud, France"] = {container = "Corsica"}, -- 2A
["Haute-Corse, France"] = {container = "Corsica"}, -- 2B
["Côte-d'Or, France"] = {container = "Bourgogne-Franche-Comté"}, -- 21
["Côte d'Or, France"] = {alias_of = "Côte-d'Or, France", display = true},
["Côtes-d'Armor, France"] = {container = "Brittany"}, -- 22
["Côtes d'Armor, France"] = {alias_of = "Côtes-d'Armor, France", display = true},
["Creuse, France"] = {container = "Nouvelle-Aquitaine"}, -- 23
["Dordogne, France"] = {container = "Nouvelle-Aquitaine"}, -- 24
["Doubs, France"] = {container = "Bourgogne-Franche-Comté"}, -- 25
["Drôme, France"] = {container = "Auvergne-Rhône-Alpes"}, -- 26
["Eure, France"] = {container = "Normandy"}, -- 27
["Eure-et-Loir, France"] = {container = "Centre-Val de Loire"}, -- 28
["Finistère, France"] = {container = "Brittany"}, -- 29
["Gard, France"] = {container = "Occitania"}, -- 30
["Haute-Garonne, France"] = {container = "Occitania"}, -- 31
["Gers, France"] = {container = "Occitania"}, -- 32
["Gironde, France"] = {container = "Nouvelle-Aquitaine"}, -- 33
["Hérault, France"] = {container = "Occitania"}, -- 34
["Ille-et-Vilaine, France"] = {container = "Brittany"}, -- 35
["Indre, France"] = {container = "Centre-Val de Loire"}, -- 36
["Indre-et-Loire, France"] = {container = "Centre-Val de Loire"}, -- 37
["Isère, France"] = {container = "Auvergne-Rhône-Alpes"}, -- 38
["Jura, France"] = {container = "Bourgogne-Franche-Comté", wp = "%l (department)"}, -- 39
["Landes, France"] = {container = "Nouvelle-Aquitaine", wp = "%l (department)"}, -- 40
["Loir-et-Cher, France"] = {container = "Centre-Val de Loire"}, -- 41
["Loire, France"] = {container = "Auvergne-Rhône-Alpes", wp = "%l (department)"}, -- 42
["Haute-Loire, France"] = {container = "Auvergne-Rhône-Alpes"}, -- 43
["Loire-Atlantique, France"] = {container = "Pays de la Loire"}, -- 44
["Loiret, France"] = {container = "Centre-Val de Loire"}, -- 45
["Lot, France"] = {container = "Occitania", wp = "%l (department)"}, -- 46
["Lot-et-Garonne, France"] = {container = "Nouvelle-Aquitaine"}, -- 47
["Lozère, France"] = {container = "Occitania"}, -- 48
["Maine-et-Loire, France"] = {container = "Pays de la Loire"}, -- 49
["Manche, France"] = {container = "Normandy"}, -- 50
["Marne, France"] = {container = "Grand Est", wp = "%l (department)"}, -- 51
["Haute-Marne, France"] = {container = "Grand Est"}, -- 52
["Mayenne, France"] = {container = "Pays de la Loire"}, -- 53
["Meurthe-et-Moselle, France"] = {container = "Grand Est"}, -- 54
["Meuse, France"] = {container = "Grand Est", wp = "%l (department)"}, -- 55
["Morbihan, France"] = {container = "Brittany"}, -- 56
["Moselle, France"] = {container = "Grand Est", wp = "%l (department)"}, -- 57
["Nièvre, France"] = {container = "Bourgogne-Franche-Comté"}, -- 58
["Nord, France"] = {container = "Hauts-de-France", wp = "%l (French department)"}, -- 59
["Oise, France"] = {container = "Hauts-de-France"}, -- 60
["Orne, France"] = {container = "Normandy"}, -- 61
["Pas-de-Calais, France"] = {container = "Hauts-de-France"}, -- 62
["Puy-de-Dôme, France"] = {container = "Auvergne-Rhône-Alpes"}, -- 63
["Pyrénées-Atlantiques, France"] = {container = "Nouvelle-Aquitaine"}, -- 64
["Hautes-Pyrénées, France"] = {container = "Occitania"}, -- 65
["Pyrénées-Orientales, France"] = {container = "Occitania"}, -- 66
["Bas-Rhin, France"] = {container = "Grand Est"}, -- 67
["Haut-Rhin, France"] = {container = "Grand Est"}, -- 68
["Rhône, France"] = {container = "Auvergne-Rhône-Alpes", wp = "%l (department)"}, -- 69D
["Metropolis of Lyon, France"] = {container = "Auvergne-Rhône-Alpes", the = true}, -- 69M
["Lyon Metropolis, France"] = {alias_of = "Metropolis of Lyon, France"},
["Lyon, France"] = {alias_of = "Metropolis of Lyon, France"},
["Haute-Saône, France"] = {container = "Bourgogne-Franche-Comté"}, -- 70
["Saône-et-Loire, France"] = {container = "Bourgogne-Franche-Comté"}, -- 71
["Sarthe, France"] = {container = "Pays de la Loire"}, -- 72
["Savoie, France"] = {container = "Auvergne-Rhône-Alpes"}, -- 73
["Haute-Savoie, France"] = {container = "Auvergne-Rhône-Alpes"}, -- 74
["Paris, France"] = {container = "Île-de-France"}, -- 75
["Seine-Maritime, France"] = {container = "Normandy"}, -- 76
["Seine-et-Marne, France"] = {container = "Île-de-France"}, -- 77
["Yvelines, France"] = {container = "Île-de-France"}, -- 78
["Deux-Sèvres, France"] = {container = "Nouvelle-Aquitaine"}, -- 79
["Somme, France"] = {container = "Hauts-de-France", wp = "%l (department)"}, -- 80
["Tarn, France"] = {container = "Occitania", wp = "%l (department)"}, -- 81
["Tarn-et-Garonne, France"] = {container = "Occitania"}, -- 82
["Var, France"] = {container = "Provence-Alpes-Côte d'Azur", wp = "%l (department)"}, -- 83
["Vaucluse, France"] = {container = "Provence-Alpes-Côte d'Azur"}, -- 84
["Vendée, France"] = {container = "Pays de la Loire"}, -- 85
["Vienne, France"] = {container = "Nouvelle-Aquitaine", wp = "%l (department)"}, -- 86
["Haute-Vienne, France"] = {container = "Nouvelle-Aquitaine"}, -- 87
["Vosges, France"] = {container = "Grand Est", wp = "%l (department)"}, -- 88
["Yonne, France"] = {container = "Bourgogne-Franche-Comté"}, -- 89
["Territoire de Belfort, France"] = {container = "Bourgogne-Franche-Comté"}, -- 90
["Essonne, France"] = {container = "Île-de-France"}, -- 91
["Hauts-de-Seine, France"] = {container = "Île-de-France"}, -- 92
["Seine-Saint-Denis, France"] = {container = "Île-de-France"}, -- 93
["Val-de-Marne, France"] = {container = "Île-de-France"}, -- 94
["Val-d'Oise, France"] = {container = "Île-de-France"}, -- 95
--["Guadeloupe"] = {container = "Guadeloupe"}, -- 971
--["Martinique"] = {container = "Martinique"}, -- 972
--["Guyane"] = {container = "French Guiana", wp = "French Guiana"}, -- 973
--["La Réunion"] = {container = "Réunion", wp = "Réunion"}, -- 974
--["Mayotte"] = {container = "Mayotte"}, -- 976
}
export.france_departments_group = {
placename_to_key = make_placename_to_key(", France"),
canonicalize_key_container = make_canonicalize_key_container(", France", "region"),
default_placetype = "department",
default_divs = {
"communes",
{type = "municipalities", cat_as = "communes"},
},
data = export.france_departments,
}
export.germany_states = {
["Baden-Württemberg, Germany"] = {},
["Bavaria, Germany"] = {},
-- Berlin, Bremen and Hamburg are effectively city-states and don't have districts ([[Kreise]]), so override
-- the default_divs setting. Better not to include them at all since they're included as cities down below.
-- ["Berlin"] = {divs = {}},
["Brandenburg, Germany"] = {},
-- ["Bremen"] = {divs = {}},
-- ["Hamburg"] = {divs = {}},
["Hesse, Germany"] = {},
["Lower Saxony, Germany"] = {},
["Mecklenburg-Vorpommern, Germany"] = {},
["Mecklenburg-Western Pomerania, Germany"] = {alias_of = "Mecklenburg-Vorpommern, Germany", display = true},
["North Rhine-Westphalia, Germany"] = {},
["Rhineland-Palatinate, Germany"] = {},
["Saarland, Germany"] = {},
["Saxony, Germany"] = {},
["Saxony-Anhalt, Germany"] = {},
["Schleswig-Holstein, Germany"] = {},
["Thuringia, Germany"] = {},
}
-- states of Germany
export.germany_group = {
default_container = "Germany",
default_placetype = "state",
default_divs = {"districts", "municipalities"},
data = export.germany_states,
}
export.greece_regions = {
["Attica, Greece"] = {wp = "%l (region)"},
["Central Greece, Greece"] = {wp = "%l (administrative region)"},
["Central Macedonia, Greece"] = {},
["Crete, Greece"] = {},
["Eastern Macedonia and Thrace, Greece"] = {},
["Epirus, Greece"] = {wp = "%l (region)"},
["Ionian Islands, Greece"] = {the = true, wp = "%l (region)"},
["North Aegean, Greece"] = {the = true},
-- I would expect 'the Peloponnese' but Wikipedia mostly has categories like [[w:Category:Geography of Peloponnese (region)]]
-- and [[w:Category:Buildings and structures in Peloponnese (region)]]; only [[w:Category:People from the Peloponnese (region)]]
-- has "the" in it.
["Peloponnese, Greece"] = {wp = "%l (region)"},
["South Aegean, Greece"] = {the = true},
["Thessaly, Greece"] = {},
["Western Greece, Greece"] = {},
["Western Macedonia, Greece"] = {},
["Mount Athos, Greece"] = {placetype = {"autonomous region", "region"}, wp = "Monastic community of Mount Athos"},
}
-- regions of Greece
export.greece_group = {
default_container = "Greece",
default_placetype = "region",
data = export.greece_regions,
}
local india_polity_with_divisions = {"divisions", "districts"}
local india_polity_without_divisions = {"districts"}
-- States and union territories of India. Only some of them are divided into divisions.
export.india_states_and_union_territories = {
["Andaman and Nicobar Islands, India"] =
{the = true, placetype = "union territory", divs = india_polity_without_divisions},
["Andhra Pradesh, India"] = {divs = india_polity_without_divisions},
["Arunachal Pradesh, India"] = {divs = india_polity_with_divisions},
["Assam, India"] = {divs = india_polity_with_divisions},
["Bihar, India"] = {divs = india_polity_with_divisions},
["Chandigarh, India"] = {placetype = "union territory", divs = india_polity_without_divisions},
["Chhattisgarh, India"] = {divs = india_polity_with_divisions},
["Dadra and Nagar Haveli and Daman and Diu, India"] = {placetype = "union territory", divs = india_polity_without_divisions},
["Delhi, India"] = {placetype = "union territory", divs = india_polity_with_divisions},
["Goa, India"] = {divs = india_polity_without_divisions},
["Gujarat, India"] = {divs = india_polity_without_divisions},
["Haryana, India"] = {divs = india_polity_with_divisions},
["Himachal Pradesh, India"] = {divs = india_polity_with_divisions},
["Jammu and Kashmir, India"] = {placetype = "union territory", divs = india_polity_with_divisions,
wp = "%l (union territory)"},
["Jharkhand, India"] = {divs = india_polity_with_divisions},
["Karnataka, India"] = {divs = india_polity_with_divisions},
["Kerala, India"] = {divs = india_polity_without_divisions},
["Ladakh, India"] = {placetype = "union territory", divs = india_polity_with_divisions},
["Lakshadweep, India"] = {placetype = "union territory", divs = india_polity_without_divisions},
["Madhya Pradesh, India"] = {divs = india_polity_with_divisions},
["Maharashtra, India"] = {divs = india_polity_with_divisions},
["Manipur, India"] = {divs = india_polity_without_divisions},
["Meghalaya, India"] = {divs = india_polity_with_divisions},
["Mizoram, India"] = {divs = india_polity_without_divisions},
["Nagaland, India"] = {divs = india_polity_with_divisions},
["Odisha, India"] = {divs = india_polity_with_divisions},
["Puducherry, India"] = {placetype = "union territory", divs = india_polity_without_divisions,
wp = "%l (union territory)"},
["Pondicherry, India"] = {alias_of = "Puducherry, India", display = true},
["Punjab, India"] = {divs = india_polity_with_divisions, wp = "%l, %c"},
["Rajasthan, India"] = {divs = india_polity_with_divisions},
["Sikkim, India"] = {divs = india_polity_without_divisions},
["Tamil Nadu, India"] = {divs = india_polity_without_divisions},
["Telangana, India"] = {divs = india_polity_without_divisions},
["Tripura, India"] = {divs = india_polity_without_divisions},
["Uttar Pradesh, India"] = {divs = india_polity_with_divisions},
["Uttarakhand, India"] = {divs = india_polity_with_divisions},
["West Bengal, India"] = {divs = india_polity_with_divisions},
}
-- states and union territories of India
export.india_group = {
default_container = "India",
default_placetype = "state",
data = export.india_states_and_union_territories,
}
export.indonesia_provinces = {
["Aceh, Indonesia"] = {},
["Bali, Indonesia"] = {},
["Bangka Belitung Islands, Indonesia"] = {the = true},
["Banten, Indonesia"] = {},
["Bengkulu, Indonesia"] = {},
["Central Java, Indonesia"] = {},
["Central Kalimantan, Indonesia"] = {},
["Central Papua, Indonesia"] = {},
["Central Sulawesi, Indonesia"] = {},
["East Java, Indonesia"] = {},
["East Kalimantan, Indonesia"] = {},
["East Nusa Tenggara, Indonesia"] = {},
["Gorontalo, Indonesia"] = {},
["Highland Papua, Indonesia"] = {wp = "%l"},
["Special Capital Region of Jakarta, Indonesia"] = {the = true, wp = "Jakarta"},
["Jakarta, Indonesia"] = {alias_of = "Special Capital Region of Jakarta, Indonesia"},
["Jambi, Indonesia"] = {},
["Lampung, Indonesia"] = {},
["Maluku, Indonesia"] = {},
["North Kalimantan, Indonesia"] = {},
["North Maluku, Indonesia"] = {},
["North Sulawesi, Indonesia"] = {},
["North Papua, Indonesia"] = {},
["North Sumatra, Indonesia"] = {},
["Papua, Indonesia"] = {wp = "%l (province)"},
["Riau, Indonesia"] = {},
["Riau Islands, Indonesia"] = {the = true},
["Southeast Sulawesi, Indonesia"] = {},
["South Kalimantan, Indonesia"] = {},
["South Papua, Indonesia"] = {},
["South Sulawesi, Indonesia"] = {},
["South Sumatra, Indonesia"] = {},
["Southwest Papua, Indonesia"] = {},
["West Java, Indonesia"] = {},
["West Kalimantan, Indonesia"] = {},
["West Nusa Tenggara, Indonesia"] = {},
["West Papua, Indonesia"] = {wp = "%l (province)"},
["West Sulawesi, Indonesia"] = {},
["West Sumatra, Indonesia"] = {},
["Special Region of Yogyakarta, Indonesia"] = {the = true},
["Yogyakarta, Indonesia"] = {alias_of = "Special Region of Yogyakarta, Indonesia"},
}
-- provinces of Indonesia
export.indonesia_group = {
default_container = "Indonesia",
default_placetype = "province",
-- per https://www.quora.com/Does-Indonesia-use-British-or-American-English, Indonesia tends to use American
-- spellings.
data = export.indonesia_provinces,
}
export.iran_provinces = {
["Alborz Province, Iran"] = {}, -- abbreviation AL, capital [[w:Karaj]]
["Ardabil Province, Iran"] = {}, -- abbreviation AR, capital [[w:Ardabil]]
["Bushehr Province, Iran"] = {}, -- abbreviation BU, capital [[w:Bushehr]]
["Chaharmahal and Bakhtiari Province, Iran"] = {}, -- abbreviation CB, capital [[w:Shahr-e Kord]]
["East Azerbaijan Province, Iran"] = {}, -- abbreviation EA, capital [[w:Tabriz]]
["Fars Province, Iran"] = {}, -- abbreviation FA, capital [[w:Shiraz]]
["Pars Province, Iran"] = {alias_of = "Fars Province, Iran", display = true},
["Gilan Province, Iran"] = {}, -- abbreviation GN, capital [[w:Rasht]]
["Golestan Province, Iran"] = {}, -- abbreviation GO, capital [[w:Gorgan]]
["Hamadan Province, Iran"] = {}, -- abbreviation HA, capital [[w:Hamadan]]
["Hormozgan Province, Iran"] = {}, -- abbreviation HO, capital [[w:Bandar Abbas]]
["Ilam Province, Iran"] = {}, -- abbreviation IL, capital [[w:Ilam, Iran|Ilam]]
["Isfahan Province, Iran"] = {}, -- abbreviation IS, capital [[w:Isfahan]]
["Kerman Province, Iran"] = {}, -- abbreviation KN, capital [[w:Kerman]]
["Kermanshah Province, Iran"] = {}, -- abbreviation KE, capital [[w:Kermanshah]]
["Khuzestan Province, Iran"] = {}, -- abbreviation KH, capital [[w:Ahvaz]]
["Kohgiluyeh and Boyer-Ahmad Province, Iran"] = {}, -- abbreviation KB, capital [[w:Yasuj]]
["Kurdistan Province, Iran"] = {}, -- abbreviation KU, capital [[w:Sanandaj]]
["Lorestan Province, Iran"] = {}, -- abbreviation LO, capital [[w:Khorramabad]]
["Markazi Province, Iran"] = {}, -- abbreviation MA, capital [[w:Arak, Iran|Arak]]
["Mazandaran Province, Iran"] = {}, -- abbreviation MN, capital [[w:Sari, Iran|Sari]]
["North Khorasan Province, Iran"] = {}, -- abbreviation NK, capital [[w:Bojnord]]
["Qazvin Province, Iran"] = {}, -- abbreviation QA, capital [[w:Qazvin]]
["Qom Province, Iran"] = {}, -- abbreviation QM, capital [[w:Qom]]
["Razavi Khorasan Province, Iran"] = {}, -- abbreviation RK, capital [[w:Mashhad]]
["Semnan Province, Iran"] = {}, -- abbreviation SE, capital [[w:Semnan, Iran|Semnan]]
["Sistan and Baluchestan Province, Iran"] = {}, -- abbreviation SB, capital [[w:Zahedan]]
["South Khorasan Province, Iran"] = {}, -- abbreviation SK, capital [[w:Birjand]]
["Tehran Province, Iran"] = {}, -- abbreviation TE, capital [[w:Tehran]]
["West Azerbaijan Province, Iran"] = {}, -- abbreviation WA, capital [[w:Urmia]]
["Yazd Province, Iran"] = {}, -- abbreviation YA, capital [[w:Yazd]]
["Zanjan Province, Iran"] = {}, -- abbreviation ZA, capital [[w:Zanjan, Iran|Zanjan]]
}
-- provinces of Iran
export.iran_group = {
key_to_placename = make_key_to_placename(", Iran", " Province$"),
placename_to_key = make_placename_to_key(", Iran", " Province"),
default_container = "Iran",
default_placetype = "province",
-- There aren't nearly enough counties of Iran currently entered in any language to allow for categorizing them
-- per-province. (As of 2025-05-09, there are only 6 counties in each of [[Category:en:Counties of Iran]],
-- [[Category:fa:Counties of Iran]] and [[Category:ar:Counties of Iran]].)
-- default_divs = "counties",
-- For obscure reasons, provinces of Iran, Laos, Thailand and Vietnam use lowercase 'province'
default_wp = "%e province",
data = export.iran_provinces,
}
export.ireland_counties = {
["County Carlow, Ireland"] = {},
["County Cavan, Ireland"] = {},
["County Clare, Ireland"] = {},
["County Cork, Ireland"] = {},
["County Donegal, Ireland"] = {},
["County Dublin, Ireland"] = {},
["County Galway, Ireland"] = {},
["County Kerry, Ireland"] = {},
["County Kildare, Ireland"] = {},
["County Kilkenny, Ireland"] = {},
["County Laois, Ireland"] = {},
["County Leitrim, Ireland"] = {},
["County Limerick, Ireland"] = {},
["County Longford, Ireland"] = {},
["County Louth, Ireland"] = {},
["County Mayo, Ireland"] = {},
["County Meath, Ireland"] = {},
["County Monaghan, Ireland"] = {},
["County Offaly, Ireland"] = {},
["County Roscommon, Ireland"] = {},
["County Sligo, Ireland"] = {},
["County Tipperary, Ireland"] = {},
["County Waterford, Ireland"] = {},
["County Westmeath, Ireland"] = {},
["County Wexford, Ireland"] = {},
["County Wicklow, Ireland"] = {},
}
local function make_irish_type_key_to_placename(container_pattern)
return function(key)
key = key:gsub(container_pattern, "")
local elliptical_key = key:gsub("^County ", "")
return key, elliptical_key
end
end
local function make_irish_type_placename_to_key(container_suffix)
return function(placename)
if not placename:find("^County ") and not placename:find("^City ") then
placename = "County " .. placename
end
return placename .. container_suffix
end
end
-- counties of Ireland
export.ireland_group = {
key_to_placename = make_irish_type_key_to_placename(", Ireland$"),
placename_to_key = make_irish_type_placename_to_key(", Ireland"),
default_container = "Ireland",
default_placetype = "county",
data = export.ireland_counties,
}
export.italy_administrative_regions = {
["Abruzzo, Italy"] = {},
["Aosta Valley, Italy"] = {placetype = {"autonomous region", "administrative region", "region"}},
["Apulia, Italy"] = {},
["Basilicata, Italy"] = {},
["Calabria, Italy"] = {},
["Campania, Italy"] = {},
["Emilia-Romagna, Italy"] = {},
["Friuli-Venezia Giulia, Italy"] = {placetype = {"autonomous region", "administrative region", "region"}},
["Lazio, Italy"] = {},
["Liguria, Italy"] = {},
["Lombardy, Italy"] = {},
["Marche, Italy"] = {},
["Molise, Italy"] = {},
["Piedmont, Italy"] = {},
["Sardinia, Italy"] = {placetype = {"autonomous region", "administrative region", "region"}},
["Sicily, Italy"] = {placetype = {"autonomous region", "administrative region", "region"}},
["Trentino-Alto Adige, Italy"] = {placetype = {"autonomous region", "administrative region", "region"}},
["Tuscany, Italy"] = {},
["Umbria, Italy"] = {},
["Veneto, Italy"] = {},
}
-- administrative regions of Italy
export.italy_group = {
default_container = "Italy",
default_placetype = "region",
data = export.italy_administrative_regions,
}
-- table of Japanese prefectures; interpolated into the main 'places' table, but also needed separately
export.japan_prefectures = {
["Aichi Prefecture, Japan"] = {},
["Akita Prefecture, Japan"] = {},
["Aomori Prefecture, Japan"] = {},
["Chiba Prefecture, Japan"] = {},
["Ehime Prefecture, Japan"] = {},
["Fukui Prefecture, Japan"] = {},
["Fukuoka Prefecture, Japan"] = {},
["Fukushima Prefecture, Japan"] = {},
["Gifu Prefecture, Japan"] = {},
["Gunma Prefecture, Japan"] = {},
["Hiroshima Prefecture, Japan"] = {},
["Hokkaido Prefecture, Japan"] = {divs = "subprefectures", wp = "Hokkaido"},
["Hyōgo Prefecture, Japan"] = {},
["Hyogo Prefecture, Japan"] = {alias_of = "Hyōgo Prefecture, Japan", display = true},
["Ibaraki Prefecture, Japan"] = {},
["Ishikawa Prefecture, Japan"] = {},
["Iwate Prefecture, Japan"] = {},
["Kagawa Prefecture, Japan"] = {},
["Kagoshima Prefecture, Japan"] = {},
["Kanagawa Prefecture, Japan"] = {},
["Kōchi Prefecture, Japan"] = {},
["Kochi Prefecture, Japan"] = {alias_of = "Kōchi Prefecture, Japan", display = true},
["Kumamoto Prefecture, Japan"] = {},
["Kyoto Prefecture, Japan"] = {},
["Mie Prefecture, Japan"] = {},
["Miyagi Prefecture, Japan"] = {},
["Miyazaki Prefecture, Japan"] = {},
["Nagano Prefecture, Japan"] = {},
["Nagasaki Prefecture, Japan"] = {},
["Nara Prefecture, Japan"] = {},
["Niigata Prefecture, Japan"] = {},
["Ōita Prefecture, Japan"] = {},
["Oita Prefecture, Japan"] = {alias_of = "Ōita Prefecture, Japan", display = true},
["Okayama Prefecture, Japan"] = {},
["Okinawa Prefecture, Japan"] = {},
["Osaka Prefecture, Japan"] = {},
["Saga Prefecture, Japan"] = {},
["Saitama Prefecture, Japan"] = {},
["Shiga Prefecture, Japan"] = {},
["Shimane Prefecture, Japan"] = {},
["Shizuoka Prefecture, Japan"] = {},
["Tochigi Prefecture, Japan"] = {},
["Tokushima Prefecture, Japan"] = {},
["Tottori Prefecture, Japan"] = {},
["Toyama Prefecture, Japan"] = {},
["Wakayama Prefecture, Japan"] = {},
["Yamagata Prefecture, Japan"] = {},
["Yamaguchi Prefecture, Japan"] = {},
["Yamanashi Prefecture, Japan"] = {},
}
-- prefectures of Japan
export.japan_group = {
key_to_placename = make_key_to_placename(", Japan$", " Prefecture$"),
placename_to_key = make_placename_to_key(", Japan", " Prefecture"),
default_container = "Japan",
default_placetype = "prefecture",
data = export.japan_prefectures,
}
export.laos_provinces = {
["Attapeu Province, Laos"] = {},
["Bokeo Province, Laos"] = {},
["Bolikhamxai Province, Laos"] = {},
["Champasak Province, Laos"] = {},
["Houaphanh Province, Laos"] = {},
["Khammouane Province, Laos"] = {},
["Luang Namtha Province, Laos"] = {},
["Luang Prabang Province, Laos"] = {},
["Oudomxay Province, Laos"] = {},
["Phongsaly Province, Laos"] = {},
["Salavan Province, Laos"] = {},
["Savannakhet Province, Laos"] = {},
["Vientiane Province, Laos"] = {},
["Vientiane Prefecture, Laos"] = {placetype = "prefecture", wp = "%l"},
["Sainyabuli Province, Laos"] = {},
["Sekong Province, Laos"] = {},
["Xaisomboun Province, Laos"] = {},
["Xiangkhouang Province, Laos"] = {},
}
local function laos_placename_to_key(placename)
if placename == "Vientiane Prefecture" then
return placename .. ", Laos"
end
if placename:find(" Province$") then
return placename .. ", Laos"
end
return placename .. " Province, Laos"
end
-- provinces of Laos
export.laos_group = {
key_to_placename = make_key_to_placename(", Laos$", {" Province$", " Prefecture$"}),
placename_to_key = laos_placename_to_key,
default_container = "Laos",
default_placetype = "province",
-- For obscure reasons, provinces of Iran, Laos, Thailand and Vietnam use lowercase 'province'
default_wp = "%e province",
data = export.laos_provinces,
}
export.lebanon_governorates = {
["Akkar Governorate, Lebanon"] = {},
["Baalbek-Hermel Governorate, Lebanon"] = {},
["Beirut Governorate, Lebanon"] = {},
["Beqaa Governorate, Lebanon"] = {},
["Keserwan-Jbeil Governorate, Lebanon"] = {},
["Mount Lebanon Governorate, Lebanon"] = {},
["Nabatieh Governorate, Lebanon"] = {},
-- These two are generic enough that we don't want to automatically augment a use of `gov/North Governorate` or
-- `gov/South Governorate` with `c/Lebanon`.
["North Governorate, Lebanon"] = {no_auto_augment_container = true},
["South Governorate, Lebanon"] = {no_auto_augment_container = true},
}
-- governorates of Lebanon
export.lebanon_group = {
key_to_placename = make_key_to_placename(", Lebanon$", " Governorate$"),
placename_to_key = make_placename_to_key(", Lebanon", " Governorate"),
default_container = "Lebanon",
default_placetype = "governorate",
data = export.lebanon_governorates,
}
export.malaysia_states = {
["Johor, Malaysia"] = {},
["Kedah, Malaysia"] = {},
["Kelantan, Malaysia"] = {},
["Malacca, Malaysia"] = {},
["Negeri Sembilan, Malaysia"] = {},
["Pahang, Malaysia"] = {},
["Penang, Malaysia"] = {},
["Perak, Malaysia"] = {},
["Perlis, Malaysia"] = {},
["Sabah, Malaysia"] = {},
["Sarawak, Malaysia"] = {},
["Selangor, Malaysia"] = {},
["Terengganu, Malaysia"] = {},
}
-- states of Malaysia
export.malaysia_group = {
default_container = "Malaysia",
default_placetype = "state",
default_wp = "%l, %c",
data = export.malaysia_states,
}
export.malta_regions = {
-- Some of the regions are generic enough that we don't want to automatically augment a use of e.g.
-- `r/Northern Region` with `c/Malta`. In particular;
-- * "Eastern Region" also occurs at least in Ghana, Uganda, Iceland, Nigeria, Venezuela, North Macedonia and
-- El Salvador;
-- * "Northern Region" also occurs at least in Ghana, Uganda, Malawi, Nigeria, Canada and South Africa;
-- * "Western Region" also occurs at least in Abu Dhabi, Bahrain, South Africa, Ghana, Iceland, Nepal, Nigeria,
-- Serbia and Uganda;
-- * "Southern Region" also occurs at least in Nigeria, Eritrea, Iceland, Ireland, Malawi and Serbia.
["Eastern Region, Malta"] = {no_auto_augment_container = true},
["Gozo Region, Malta"] = {wp = "%l"},
["Northern Region, Malta"] = {no_auto_augment_container = true},
["Port Region, Malta"] = {},
["Southern Region, Malta"] = {no_auto_augment_container = true},
["Western Region, Malta"] = {no_auto_augment_container = true},
}
-- regions of Malta
export.malta_group = {
key_to_placename = make_key_to_placename(", Malta$", " Region"),
placename_to_key = make_placename_to_key(", Malta", " Region"),
default_container = "Malta",
default_placetype = "region",
default_wp = "%l, %c",
default_the = true,
data = export.malta_regions,
}
export.mexico_states = {
["Aguascalientes, Mexico"] = {},
["Baja California, Mexico"] = {},
-- not display-canonicalizing because the "Norte" could be for emphasis
["Baja California Norte, Mexico"] = {alias_of = "Baja California, Mexico"},
["Baja California Sur, Mexico"] = {},
["Campeche, Mexico"] = {},
["Chiapas, Mexico"] = {},
["Chihuahua, Mexico"] = {wp = "%l (state)"},
["Coahuila, Mexico"] = {},
["Colima, Mexico"] = {},
["Durango, Mexico"] = {},
["Guanajuato, Mexico"] = {},
["Guerrero, Mexico"] = {},
["Hidalgo, Mexico"] = {wp = "%l (state)"},
["Jalisco, Mexico"] = {},
["State of Mexico, Mexico"] = {the = true},
["Mexico, Mexico"] = {alias_of = "State of Mexico, Mexico"}, -- differs in "the"
-- ["Mexico City, Mexico"] = {}, doesn't belong here because it's a city
["Michoacán, Mexico"] = {},
["Michoacan, Mexico"] = {alias_of = "Michoacán, Mexico", display = true},
["Morelos, Mexico"] = {},
["Nayarit, Mexico"] = {},
["Nuevo León, Mexico"] = {},
["Nuevo Leon, Mexico"] = {alias_of = "Nuevo León, Mexico", display = true},
["Oaxaca, Mexico"] = {},
["Puebla, Mexico"] = {},
["Querétaro, Mexico"] = {},
["Queretaro, Mexico"] = {alias_of = "Querétaro, Mexico", display = true},
["Quintana Roo, Mexico"] = {},
["San Luis Potosí, Mexico"] = {},
["San Luis Potosi, Mexico"] = {alias_of = "San Luis Potosí, Mexico", display = true},
["Sinaloa, Mexico"] = {},
["Sonora, Mexico"] = {},
["Tabasco, Mexico"] = {},
["Tamaulipas, Mexico"] = {},
["Tlaxcala, Mexico"] = {},
["Veracruz, Mexico"] = {},
["Yucatán, Mexico"] = {},
["Yucatan, Mexico"] = {alias_of = "Yucatán, Mexico", display = true},
["Zacatecas, Mexico"] = {},
}
-- Mexican states
export.mexico_group = {
default_container = "Mexico",
default_placetype = "state",
data = export.mexico_states,
}
export.moldova_districts_and_autonomous_territorial_units = {
["Anenii Noi District, Moldova"] = {}, -- capital [[Anenii Noi]]
["Basarabeasca District, Moldova"] = {}, -- capital [[Basarabeasca]]
["Briceni District, Moldova"] = {}, -- capital [[Briceni]]
["Cahul District, Moldova"] = {}, -- capital [[Cahul]]
["Cantemir District, Moldova"] = {}, -- capital [[Cantemir, Moldova|Cantemir]]
["Călărași District, Moldova"] = {}, -- capital [[Călărași, Moldova|Călărași]]
["Căușeni District, Moldova"] = {}, -- capital [[Căușeni]]
["Cimișlia District, Moldova"] = {}, -- capital [[Cimișlia]]
["Criuleni District, Moldova"] = {}, -- capital [[Criuleni]]
["Dondușeni District, Moldova"] = {}, -- capital [[Dondușeni]]
["Drochia District, Moldova"] = {}, -- capital [[Drochia]]
["Dubăsari District, Moldova"] = {}, -- capital [[Cocieri]]
["Edineț District, Moldova"] = {}, -- capital [[Edineț]]
["Fălești District, Moldova"] = {}, -- capital [[Fălești]]
["Florești District, Moldova"] = {}, -- capital [[Florești, Moldova|Florești]]
["Glodeni District, Moldova"] = {}, -- capital [[Glodeni]]
["Hîncești District, Moldova"] = {}, -- capital [[Hîncești]]
["Ialoveni District, Moldova"] = {}, -- capital [[Ialoveni]]
["Leova District, Moldova"] = {}, -- capital [[Leova]]
["Nisporeni District, Moldova"] = {}, -- capital [[Nisporeni]]
["Ocnița District, Moldova"] = {}, -- capital [[Ocnița]]
["Orhei District, Moldova"] = {}, -- capital [[Orhei]]
["Rezina District, Moldova"] = {}, -- capital [[Rezina]]
["Rîșcani District, Moldova"] = {}, -- capital [[Rîșcani]]
["Sîngerei District, Moldova"] = {}, -- capital [[Sîngerei]]
["Soroca District, Moldova"] = {}, -- capital [[Soroca]]
["Strășeni District, Moldova"] = {}, -- capital [[Strășeni]]
["Șoldănești District, Moldova"] = {}, -- capital [[Șoldănești]]
["Ștefan Vodă District, Moldova"] = {}, -- capital [[Ștefan Vodă]]
["Taraclia District, Moldova"] = {}, -- capital [[Taraclia]]
["Telenești District, Moldova"] = {}, -- capital [[Telenești]]
["Ungheni District, Moldova"] = {}, -- capital [[Ungheni]]
["Chișinău, Moldova"] = {placetype = "municipality"},
["Bălți, Moldova"] = {placetype = "municipality"},
["Gagauzia, Moldova"] = {placetype = {"autonomous territorial unit", "autonomous region", "region"}}, -- capital [[Comrat]]
-- the remainder are under the de-facto control of the unrecognized state of Transnistria
["Bender, Moldova"] = {placetype = "municipality"},
["Tighina, Moldova"] = {alias_of = "Bender, Moldova"},
["Transnistria, Moldova"] = {placetype = {"autonomous territorial unit", "autonomous region", "region"}}, -- capital [[Tiraspol]]
["Left Bank of the Dniester, Moldova"] = {alias_of = "Transnistria, Moldova", the = true},
["Administrative-Territorial Units of the Left Bank of the Dniester, Moldova"] = {alias_of = "Transnistria, Moldova", the = true},
}
local function moldova_placename_to_key(placename)
local elliptical_key = placename .. ", Moldova"
if export.moldova_districts_and_autonomous_territorial_units[elliptical_key] then
return elliptical_key
end
if placename:find(" District$") then
return placename .. ", Moldova"
end
return placename .. " District, Moldova"
end
-- Moldovan districts (raions) and autonomous territorial units
export.moldova_group = {
key_to_placename = make_key_to_placename(", Moldova$", " District"),
placename_to_key = moldova_placename_to_key,
default_container = "Moldova",
default_placetype = {"district", "raion"},
default_divs = "communes",
data = export.moldova_districts_and_autonomous_territorial_units,
}
export.morocco_regions = {
["Tangier-Tetouan-Al Hoceima, Morocco"] = {},
["Oriental, Morocco"] = {wp = "%l (%c)"},
["L'Oriental, Morocco"] = {alias_of = "Oriental, Morocco", display = true},
["Fez-Meknes, Morocco"] = {},
["Rabat-Sale-Kenitra, Morocco"] = {wp = "Rabat-Salé-Kénitra"},
["Rabat-Salé-Kénitra, Morocco"] = {alias_of = "Rabat-Sale-Kenitra, Morocco", display = true},
["Beni Mellal-Khenifra, Morocco"] = {wp = "Béni Mellal-Khénifra"},
["Béni Mellal-Khénifra, Morocco"] = {alias_of = "Beni Mellal-Khenifra, Morocco", display = true},
["Casablanca-Settat, Morocco"] = {},
["Marrakesh-Safi, Morocco"] = {wp = "Marrakesh–Safi"}, -- WP title has en-dash
["Marrakech-Safi, Morocco"] = {alias_of = "Marrakesh-Safi, Morocco", display = true},
["Draa-Tafilalet, Morocco"] = {wp = "Drâa-Tafilalet"},
["Drâa-Tafilalet, Morocco"] = {alias_of = "Draa-Tafilalet, Morocco", display = true},
["Souss-Massa, Morocco"] = {},
["Guelmim-Oued Noun, Morocco"] = {
keydesc = "+++. '''NOTE:''' This region lies partly within the disputed territory of [[Western Sahara]]"
},
["Laayoune-Sakia El Hamra, Morocco"] = {
wp = "Laâyoune-Sakia El Hamra",
keydesc = "+++. '''NOTE:''' This region lies almost completely within the disputed territory of [[Western Sahara]]",
},
["Laâyoune-Sakia El Hamra, Morocco"] = {alias_of = "Laayoune-Sakia El Hamra, Morocco", display = true},
["Dakhla-Oued Ed-Dahab, Morocco"] = {
keydesc = "+++. '''NOTE:''' This region lies completely within the disputed territory of [[Western Sahara]]",
},
}
-- regions of Morocco
export.morocco_group = {
default_container = "Morocco",
default_placetype = "region",
data = export.morocco_regions,
}
export.egypt_governorates = {
["Cairo Governorate, Egypt"] = {},
["Giza Governorate, Egypt"] = {},
["Sharqia Governorate, Egypt"] = {},
["Dakahlia Governorate, Egypt"] = {},
["Beheira Governorate, Egypt"] = {},
["Minya Governorate, Egypt"] = {},
["Qalyubia Governorate, Egypt"] = {},
["Sohag Governorate, Egypt"] = {},
["Alexandria Governorate, Egypt"] = {},
["Gharbia Governorate, Egypt"] = {},
["Asyut Governorate, Egypt"] = {},
["Monufia Governorate, Egypt"] = {},
["Faiyum Governorate, Egypt"] = {},
["Kafr El Sheikh Governorate, Egypt"] = {},
["Qena Governorate, Egypt"] = {},
["Beni Suef Governorate, Egypt"] = {},
["Damietta Governorate, Egypt"] = {},
["Aswan Governorate, Egypt"] = {},
["Ismailia Governorate, Egypt"] = {},
["Luxor Governorate, Egypt"] = {},
["Suez Governorate, Egypt"] = {},
["Port Said Governorate, Egypt"] = {},
["Matrouh Governorate, Egypt"] = {},
["North Sinai Governorate, Egypt"] = {},
["Red Sea Governorate, Egypt"] = {},
["New Valley Governorate, Egypt"] = {},
["South Sinai Governorate, Egypt"] = {},
}
-- governorates of Egypt
export.egypt_group = {
key_to_placename = make_key_to_placename(", Egypt$", " Governorate$"),
placename_to_key = make_placename_to_key(", Egypt", " Governorate"),
default_container = "Egypt",
default_placetype = "governorate",
data = export.egypt_governorates,
}
export.netherlands_provinces = {
["Drenthe, Netherlands"] = {},
["Flevoland, Netherlands"] = {},
["Friesland, Netherlands"] = {},
["Gelderland, Netherlands"] = {},
["Groningen, Netherlands"] = {wp = "%l (province)"},
["Limburg, Netherlands"] = {wp = "%l (%c)"},
["North Brabant, Netherlands"] = {},
-- Foreign forms get display-canonicalized.
["Noord-Brabant, Netherlands"] = {alias_of = "North Brabant, Netherlands", display = true},
["North Holland, Netherlands"] = {},
["Noord-Holland, Netherlands"] = {alias_of = "North Holland, Netherlands", display = true},
["Overijssel, Netherlands"] = {},
["South Holland, Netherlands"] = {},
["Zuid-Holland, Netherlands"] = {alias_of = "South Holland, Netherlands", display = true},
["Utrecht, Netherlands"] = {wp = "%l (province)"},
["Zeeland, Netherlands"] = {},
}
-- provinces of the Netherlands
export.netherlands_group = {
default_container = "Netherlands",
default_placetype = "province",
default_divs = "municipalities",
data = export.netherlands_provinces,
}
export.new_zealand_regions = {
-- North Island regions
["Northland, New Zealand"] = {wp = "%l Region"}, -- ISO 3166-2 code NZ-NTL, number 1, capital [[Whangārei]]
["Auckland, New Zealand"] = {wp = "%l Region"}, -- ISO 3166-2 code NZ-AUK, number 2, capital [[Auckland]]
["Waikato, New Zealand"] = {}, -- ISO 3166-2 code NZ-WKO, number 3, capital [[Hamilton, New Zealand|Hamilton]]
["Bay of Plenty, New Zealand"] = {the = true, wp = "%l Region"}, -- ISO 3166-2 code NZ-BOP, number 4, capital [[Whakatāne]]
["Gisborne, New Zealand"] = {placetype = {"region", "district"}, wp = "%l District"}, -- ISO 3166-2 code NZ-GIS, number 5, capital [[Gisborne, New Zealand|Gisborne]]
["Hawke's Bay, New Zealand"] = {}, -- ISO 3166-2 code NZ-HKB, number 6, capital [[Napier, New Zealand|Napier]]
["Taranaki, New Zealand"] = {}, -- ISO 3166-2 code NZ-TKI, number 7, capital [[Stratford, New Zealand|Stratford]]
["Manawatū-Whanganui, New Zealand"] = {}, -- ISO 3166-2 code NZ-MWT, number 8, capital [[Palmerston North]]
["Manawatu-Whanganui, New Zealand"] = {alias_of = "Manawatū-Whanganui, New Zealand", display = true},
["Manawatu-Wanganui, New Zealand"] = {alias_of = "Manawatū-Whanganui, New Zealand", display = true},
["Wellington, New Zealand"] = {wp = "%l Region"}, -- ISO 3166-2 code NZ-WGN, number 9, capital [[Wellington]]
-- South Island regions
["Tasman, New Zealand"] = {placetype = {"region", "district"}, wp = "%l District"}, -- ISO 3166-2 code NZ-TAS, number 10, capital [[Richmond, New Zealand|Richmond]]
["Nelson, New Zealand"] = {placetype = {"region", "city"}, wp = "%l, %c", is_city = true}, -- ISO 3166-2 code NZ-NSN, number 11, capital [[Nelson, New Zealand|Nelson]]
["Marlborough, New Zealand"] = {placetype = {"region", "district"}, wp = "%l District"}, -- ISO 3166-2 code NZ-MBH, number 12, capital [[Blenheim, New Zealand|Blenheim]]
["West Coast, New Zealand"] = {the = true, wp = "%l Region"}, -- ISO 3166-2 code NZ-WTC, number 13, capital [[Greymouth]]
["Canterbury, New Zealand"] = {wp = "%l Region"}, -- ISO 3166-2 code NZ-CAN, number 14, capital [[Christchurch]]
["Otago, New Zealand"] = {}, -- ISO 3166-2 code NZ-OTA, number 15, capital [[Dunedin]]
["Southland, New Zealand"] = {wp = "%l Region"}, -- ISO 3166-2 code NZ-STL, number 16, capital [[Invercargill]]
}
-- regions of New Zealand
export.new_zealand_group = {
default_container = "New Zealand",
default_placetype = "region",
data = export.new_zealand_regions,
}
export.nigeria_states = {
["Abia State, Nigeria"] = {},
["Adamawa State, Nigeria"] = {},
["Akwa Ibom State, Nigeria"] = {},
["Anambra State, Nigeria"] = {},
["Bauchi State, Nigeria"] = {},
["Bayelsa State, Nigeria"] = {},
["Benue State, Nigeria"] = {},
["Borno State, Nigeria"] = {},
["Cross River State, Nigeria"] = {},
["Delta State, Nigeria"] = {},
["Ebonyi State, Nigeria"] = {},
["Edo State, Nigeria"] = {},
["Ekiti State, Nigeria"] = {},
["Enugu State, Nigeria"] = {},
["Federal Capital Territory, Nigeria"] = {
-- not a state but allow it to be referenced as one in holonyms
placetype = {"federal territory", "territory", "state"}, the = true, wp = "%l (%c)",
},
["Gombe State, Nigeria"] = {},
["Imo State, Nigeria"] = {},
["Jigawa State, Nigeria"] = {},
["Kaduna State, Nigeria"] = {},
["Kano State, Nigeria"] = {},
["Katsina State, Nigeria"] = {},
["Kebbi State, Nigeria"] = {},
["Kogi State, Nigeria"] = {},
["Kwara State, Nigeria"] = {},
["Lagos State, Nigeria"] = {},
["Nasarawa State, Nigeria"] = {},
["Niger State, Nigeria"] = {},
["Ogun State, Nigeria"] = {},
["Ondo State, Nigeria"] = {},
["Osun State, Nigeria"] = {},
["Oyo State, Nigeria"] = {},
["Plateau State, Nigeria"] = {},
["Rivers State, Nigeria"] = {},
["Sokoto State, Nigeria"] = {},
["Taraba State, Nigeria"] = {},
["Yobe State, Nigeria"] = {},
["Zamfara State, Nigeria"] = {},
}
-- states of Nigeria
export.nigeria_group = {
key_to_placename = make_key_to_placename(", Nigeria$", " State$"),
placename_to_key = make_placename_to_key(", Nigeria", " State"),
default_container = "Nigeria",
default_placetype = "state",
data = export.nigeria_states,
}
export.north_korea_provinces = {
["Chagang Province, North Korea"] = {},
["North Hamgyong Province, North Korea"] = {},
["South Hamgyong Province, North Korea"] = {},
["North Hwanghae Province, North Korea"] = {},
["South Hwanghae Province, North Korea"] = {},
["Kangwon Province, North Korea"] = {wp = "%l (%c)"},
["North Pyongan Province, North Korea"] = {},
["South Pyongan Province, North Korea"] = {},
["Ryanggang Province, North Korea"] = {},
}
-- provinces of North Korea
export.north_korea_group = {
key_to_placename = make_key_to_placename(", North Korea$", " Province$"),
placename_to_key = make_placename_to_key(", North Korea", " Province"),
default_container = "North Korea",
default_placetype = "province",
data = export.north_korea_provinces,
}
export.norwegian_counties = {
["Oslo, Norway"] = {},
["Rogaland, Norway"] = {},
["Møre og Romsdal, Norway"] = {},
["Nordland, Norway"] = {},
["Østfold, Norway"] = {},
["Akershus, Norway"] = {},
["Buskerud, Norway"] = {},
-- the following two were merged into Innlandet
-- ["Hedmark, Norway"] = {},
-- ["Oppland, Norway"] = {},
["Innlandet, Norway"] = {},
["Vestfold, Norway"] = {},
["Telemark, Norway"] = {},
-- the following two were merged into Agder
-- ["Aust-Agder, Norway"] = {},
-- ["Vest-Agder, Norway"] = {},
["Agder, Norway"] = {},
-- the following two were merged into Vestland
-- ["Hordaland, Norway"] = {},
-- ["Sogn og Fjordane, Norway"] = {},
["Vestland, Norway"] = {},
["Trøndelag, Norway"] = {},
["Troms, Norway"] = {},
["Finnmark, Norway"] = {},
}
-- counties of Norway
export.norway_group = {
default_container = "Norway",
default_placetype = "county",
data = export.norwegian_counties,
}
export.pakistan_provinces_and_territories = {
["Azad Kashmir, Pakistan"] = {
placetype = {"administrative territory", "autonomous territory", "territory"},
},
["Azad Jammu and Kashmir, Pakistan"] = {alias_of = "Azad Kashmir, Pakistan", display = true},
["Balochistan, Pakistan"] = {wp = "%l, %c"},
["Gilgit-Baltistan, Pakistan"] = {
placetype = {"administrative territory", "territory"},
},
["Islamabad Capital Territory, Pakistan"] = {
the = true,
divs = {}, -- no divisions
placetype = {"federal territory", "administrative territory", "territory"},
},
-- Islamabad is an accepted alias for Islamabad Capital Territory given the above placetypes
["Islamabad, Pakistan"] = {alias_of = "Islamabad Capital Territory, Pakistan"},
["Khyber Pakhtunkhwa, Pakistan"] = {},
["Punjab, Pakistan"] = {wp = "%l, %c"},
["Sindh, Pakistan"] = {},
}
-- provinces and territories of Pakistan
export.pakistan_group = {
default_container = "Pakistan",
default_placetype = "province",
default_divs = "divisions",
data = export.pakistan_provinces_and_territories,
}
export.philippines_provinces = {
["Abra, Philippines"] = {wp = "%l (province)"},
["Agusan del Norte, Philippines"] = {},
["Agusan del Sur, Philippines"] = {},
["Aklan, Philippines"] = {},
["Albay, Philippines"] = {},
["Antique, Philippines"] = {wp = "%l (province)"},
["Apayao, Philippines"] = {},
["Aurora, Philippines"] = {wp = "%l (province)"},
["Basilan, Philippines"] = {},
["Bataan, Philippines"] = {},
["Batanes, Philippines"] = {},
["Batangas, Philippines"] = {},
["Benguet, Philippines"] = {},
["Biliran, Philippines"] = {},
["Bohol, Philippines"] = {},
["Bukidnon, Philippines"] = {},
["Bulacan, Philippines"] = {},
["Cagayan, Philippines"] = {},
["Camarines Norte, Philippines"] = {},
["Camarines Sur, Philippines"] = {},
["Camiguin, Philippines"] = {},
["Capiz, Philippines"] = {},
["Catanduanes, Philippines"] = {},
["Cavite, Philippines"] = {},
["Cebu, Philippines"] = {},
["Cotabato, Philippines"] = {},
["Davao de Oro, Philippines"] = {},
["Davao del Norte, Philippines"] = {},
["Davao del Sur, Philippines"] = {},
["Davao Occidental, Philippines"] = {},
["Davao Oriental, Philippines"] = {},
["Dinagat Islands, Philippines"] = {the = true},
["Eastern Samar, Philippines"] = {},
["Guimaras, Philippines"] = {},
["Ifugao, Philippines"] = {},
["Ilocos Norte, Philippines"] = {},
["Ilocos Sur, Philippines"] = {},
["Iloilo, Philippines"] = {},
["Isabela, Philippines"] = {wp = "%l (province)"},
["Kalinga, Philippines"] = {wp = "%l (province)"},
["La Union, Philippines"] = {},
["Laguna, Philippines"] = {wp = "%l (province)"},
["Lanao del Norte, Philippines"] = {},
["Lanao del Sur, Philippines"] = {},
["Leyte, Philippines"] = {wp = "%l (province)"},
["Maguindanao del Norte, Philippines"] = {},
["Maguindanao del Sur, Philippines"] = {},
["Marinduque, Philippines"] = {},
["Masbate, Philippines"] = {},
["Misamis Occidental, Philippines"] = {},
["Misamis Oriental, Philippines"] = {},
["Mountain Province, Philippines"] = {},
["Negros Occidental, Philippines"] = {},
["Negros Oriental, Philippines"] = {},
["Northern Samar, Philippines"] = {},
["Nueva Ecija, Philippines"] = {},
["Nueva Vizcaya, Philippines"] = {},
["Occidental Mindoro, Philippines"] = {},
["Oriental Mindoro, Philippines"] = {},
["Palawan, Philippines"] = {},
["Pampanga, Philippines"] = {},
["Pangasinan, Philippines"] = {},
["Quezon, Philippines"] = {},
["Quirino, Philippines"] = {},
["Rizal, Philippines"] = {wp = "%l (province)"},
["Romblon, Philippines"] = {},
["Samar, Philippines"] = {wp = "%l (province)"},
["Sarangani, Philippines"] = {},
["Siquijor, Philippines"] = {},
["Sorsogon, Philippines"] = {},
["South Cotabato, Philippines"] = {},
["Southern Leyte, Philippines"] = {},
["Sultan Kudarat, Philippines"] = {},
["Sulu, Philippines"] = {},
["Surigao del Norte, Philippines"] = {},
["Surigao del Sur, Philippines"] = {},
["Tarlac, Philippines"] = {},
["Tawi-Tawi, Philippines"] = {},
["Zambales, Philippines"] = {},
["Zamboanga del Norte, Philippines"] = {},
["Zamboanga del Sur, Philippines"] = {},
["Zamboanga Sibugay, Philippines"] = {},
-- not a province but treated as one; allow it to be referred to as a province in holonyms
["Metro Manila, Philippines"] = {placetype = {"region", "province"}},
}
-- provinces of the Philippines
export.philippines_group = {
default_container = "Philippines",
default_placetype = "province",
default_divs = {"municipalities", "barangays"},
data = export.philippines_provinces,
}
export.poland_voivodeships = {
["Lower Silesian Voivodeship, Poland"] = {}, -- abbr DS, code 02, capital Wrocław
["Kuyavian-Pomeranian Voivodeship, Poland"] = {}, -- abbr KP, code 04, capital Bydgoszcz (seat of voivode), Toruń (seat of sejmik and marshal)
["Lublin Voivodeship, Poland"] = {}, -- abbr LU, code 06, capital Lublin
["Lubusz Voivodeship, Poland"] = {}, -- abbr LB, code 08, capital Gorzów Wielkopolski (seat of voivode), Zielona Góra (seat of sejmik and marshal)
["Lodz Voivodeship, Poland"] = {wp = "Łódź Voivodeship"}, -- abbr LD, code 10, capital Łódź
["Łódź Voivodeship, Poland"] = {alias_of = "Lodz Voivodeship, Poland", display = true, display_as_full = true},
["Lesser Poland Voivodeship, Poland"] = {}, -- abbr MA, code 12, capital Kraków
["Masovian Voivodeship, Poland"] = {}, -- abbr MZ, code 14, capital Warsaw
["Opole Voivodeship, Poland"] = {}, -- abbr OP, code 16, capital Opole
["Subcarpathian Voivodeship, Poland"] = {}, -- abbr PK, code 18, capital Rzeszów
["Podlaskie Voivodeship, Poland"] = {}, -- abbr PD, code 20, capital Białystok
["Pomeranian Voivodeship, Poland"] = {}, -- abbr PM, code 22, capital Gdańsk
["Silesian Voivodeship, Poland"] = {}, -- abbr SL, code 24, capital Katowice
["Holy Cross Voivodeship, Poland"] = {wp = "Świętokrzyskie Voivodeship"}, -- abbr SK, code 26, capital Kielce
["Świętokrzyskie Voivodeship, Poland"] = {alias_of = "Holy Cross Voivodeship, Poland", display = true, display_as_full = true},
["Warmian-Masurian Voivodeship, Poland"] = {}, -- abbr WN, code 28, capital Olsztyn
["Greater Poland Voivodeship, Poland"] = {}, -- abbr WP, code 30, capital Poznań
["West Pomeranian Voivodeship, Poland"] = {}, -- abbr ZP, code 32, capital Szczecin
}
-- voivodeships of Poland
export.poland_group = {
key_to_placename = make_key_to_placename(", Poland$", " Voivodeship$"),
placename_to_key = make_placename_to_key(", Poland", " Voivodeship"),
default_container = "Poland",
default_placetype = "voivodeship",
default_divs = {
-- "counties", -- not enough of them currently
{type = "Polish colonies", cat_as = {{type = "villages", prep = "in"}}},
},
data = export.poland_voivodeships,
}
export.portugal_districts_and_autonomous_regions = {
["Azores, Portugal"] = {the = true, placetype = {"autonomous region", "region"}},
["Aveiro District, Portugal"] = {},
["Beja District, Portugal"] = {},
["Braga District, Portugal"] = {},
["Bragança District, Portugal"] = {},
["Castelo Branco District, Portugal"] = {},
["Coimbra District, Portugal"] = {},
["Évora District, Portugal"] = {},
["Faro District, Portugal"] = {},
["Guarda District, Portugal"] = {},
["Leiria District, Portugal"] = {},
["Lisbon District, Portugal"] = {},
["Lisboa District, Portugal"] = {alias_of = "Lisbon District, Portugal", display = true},
["Madeira, Portugal"] = {placetype = {"autonomous region", "region"}},
["Portalegre District, Portugal"] = {},
["Porto District, Portugal"] = {},
["Santarém District, Portugal"] = {},
["Setúbal District, Portugal"] = {},
["Viana do Castelo District, Portugal"] = {},
["Vila Real District, Portugal"] = {},
["Viseu District, Portugal"] = {},
}
local function portugal_placename_to_key(placename)
if placename == "Azores" or placename == "Madeira" then
return placename .. ", Portugal"
end
if placename:find(" District$") then
return placename .. ", Portugal"
end
return placename .. " District, Portugal"
end
-- districts and autonomous regions of Portugal
export.portugal_group = {
key_to_placename = make_key_to_placename(", Portugal$", " District$"),
placename_to_key = portugal_placename_to_key,
default_container = "Portugal",
default_placetype = "district",
default_divs = "municipalities",
data = export.portugal_districts_and_autonomous_regions,
}
export.romania_counties = {
["Alba County, Romania"] = {},
["Arad County, Romania"] = {},
["Argeș County, Romania"] = {},
["Bacău County, Romania"] = {},
["Bihor County, Romania"] = {},
["Bistrița-Năsăud County, Romania"] = {},
["Botoșani County, Romania"] = {},
["Brașov County, Romania"] = {},
["Brăila County, Romania"] = {},
-- Bucharest: not in a county
["Buzău County, Romania"] = {},
["Caraș-Severin County, Romania"] = {},
["Cluj County, Romania"] = {},
["Constanța County, Romania"] = {},
["Covasna County, Romania"] = {},
["Călărași County, Romania"] = {},
["Dolj County, Romania"] = {},
["Dâmbovița County, Romania"] = {},
["Galați County, Romania"] = {},
["Giurgiu County, Romania"] = {},
["Gorj County, Romania"] = {},
["Harghita County, Romania"] = {},
["Hunedoara County, Romania"] = {},
["Ialomița County, Romania"] = {},
["Iași County, Romania"] = {},
["Ilfov County, Romania"] = {},
["Maramureș County, Romania"] = {},
["Mehedinți County, Romania"] = {},
["Mureș County, Romania"] = {},
["Neamț County, Romania"] = {},
["Olt County, Romania"] = {},
["Prahova County, Romania"] = {},
["Satu Mare County, Romania"] = {},
["Sibiu County, Romania"] = {},
["Suceava County, Romania"] = {},
["Sălaj County, Romania"] = {},
["Teleorman County, Romania"] = {},
["Timiș County, Romania"] = {},
["Tulcea County, Romania"] = {},
["Vaslui County, Romania"] = {},
["Vrancea County, Romania"] = {},
["Vâlcea County, Romania"] = {},
}
-- counties of Romania
export.romania_group = {
key_to_placename = make_key_to_placename(", Romania$", " County$"),
placename_to_key = make_placename_to_key(", Romania", " County"),
default_container = "Romania",
default_placetype = "county",
default_divs = "communes",
data = export.romania_counties,
}
local function make_russia_federal_subject_spec(spectype, use_the, wp)
return {
placetype = spectype,
the = not not use_the,
bare_category_parent_type = {"federal subjects", spectype .. "s"},
wp = wp,
}
end
local russia_autonomous_okrug_no_the =
{placetype = {"autonomous okrug", "okrug"}, bare_category_parent_type = {"federal subjects", "autonomous okrugs"}}
local russia_autonomous_okrug_the =
{placetype = {"autonomous okrug", "okrug"}, bare_category_parent_type = {"federal subjects", "autonomous okrugs"},
the = true}
local russia_krai = make_russia_federal_subject_spec("krai")
local russia_oblast = make_russia_federal_subject_spec("oblast")
local russia_republic_the = make_russia_federal_subject_spec("republic", "use the")
local russia_republic_no_the = make_russia_federal_subject_spec("republic")
export.russia_federal_subjects = {
-- autonomous oblasts
["Jewish Autonomous Oblast, Russia"] =
{the = true, placetype = {"autonomous oblast", "oblast"},
bare_category_parent_type = {"federal subjects", "autonomous oblasts"}},
-- autonomous okrugs
["Chukotka Autonomous Okrug, Russia"] = russia_autonomous_okrug_the,
["Chukotka, Russia"] = {alias_of = "Chukotka Autonomous Okrug, Russia"},
["Khanty-Mansi Autonomous Okrug, Russia"] = russia_autonomous_okrug_the,
["Khanty-Mansia, Russia"] = {alias_of = "Khanty-Mansi Autonomous Okrug, Russia"},
["Khantia-Mansia, Russia"] = {alias_of = "Khanty-Mansi Autonomous Okrug, Russia"},
["Yugra, Russia"] = {alias_of = "Khanty-Mansi Autonomous Okrug, Russia"},
["Nenets Autonomous Okrug, Russia"] = russia_autonomous_okrug_the,
["Nenetsia, Russia"] = {alias_of = "Nenets Autonomous Okrug, Russia"},
["Yamalo-Nenets Autonomous Okrug, Russia"] = russia_autonomous_okrug_the,
["Yamalia, Russia"] = {alias_of = "Yamalo-Nenets Autonomous Okrug, Russia"},
-- krais
["Altai Krai, Russia"] = russia_krai,
["Kamchatka Krai, Russia"] = russia_krai,
["Khabarovsk Krai, Russia"] = russia_krai,
["Krasnodar Krai, Russia"] = russia_krai,
["Krasnoyarsk Krai, Russia"] = russia_krai,
["Perm Krai, Russia"] = russia_krai,
["Primorsky Krai, Russia"] = russia_krai,
["Stavropol Krai, Russia"] = russia_krai,
["Zabaykalsky Krai, Russia"] = russia_krai,
-- oblasts
["Amur Oblast, Russia"] = russia_oblast,
["Arkhangelsk Oblast, Russia"] = russia_oblast,
["Astrakhan Oblast, Russia"] = russia_oblast,
["Belgorod Oblast, Russia"] = russia_oblast,
["Bryansk Oblast, Russia"] = russia_oblast,
["Chelyabinsk Oblast, Russia"] = russia_oblast,
["Irkutsk Oblast, Russia"] = russia_oblast,
["Ivanovo Oblast, Russia"] = russia_oblast,
["Kaliningrad Oblast, Russia"] = russia_oblast,
["Kaluga Oblast, Russia"] = russia_oblast,
["Kemerovo Oblast, Russia"] = russia_oblast,
["Kirov Oblast, Russia"] = russia_oblast,
["Kostroma Oblast, Russia"] = russia_oblast,
["Kurgan Oblast, Russia"] = russia_oblast,
["Kursk Oblast, Russia"] = russia_oblast,
["Leningrad Oblast, Russia"] = russia_oblast,
["Lipetsk Oblast, Russia"] = russia_oblast,
["Magadan Oblast, Russia"] = russia_oblast,
["Moscow Oblast, Russia"] = russia_oblast,
["Murmansk Oblast, Russia"] = russia_oblast,
["Nizhny Novgorod Oblast, Russia"] = russia_oblast,
["Novgorod Oblast, Russia"] = russia_oblast,
["Novosibirsk Oblast, Russia"] = russia_oblast,
["Omsk Oblast, Russia"] = russia_oblast,
["Orenburg Oblast, Russia"] = russia_oblast,
["Oryol Oblast, Russia"] = russia_oblast,
["Penza Oblast, Russia"] = russia_oblast,
["Pskov Oblast, Russia"] = russia_oblast,
["Rostov Oblast, Russia"] = russia_oblast,
["Ryazan Oblast, Russia"] = russia_oblast,
["Sakhalin Oblast, Russia"] = russia_oblast,
["Samara Oblast, Russia"] = russia_oblast,
["Saratov Oblast, Russia"] = russia_oblast,
["Smolensk Oblast, Russia"] = russia_oblast,
["Sverdlovsk Oblast, Russia"] = russia_oblast,
["Tambov Oblast, Russia"] = russia_oblast,
["Tomsk Oblast, Russia"] = russia_oblast,
["Tula Oblast, Russia"] = russia_oblast,
["Tver Oblast, Russia"] = russia_oblast,
["Tyumen Oblast, Russia"] = russia_oblast,
["Ulyanovsk Oblast, Russia"] = russia_oblast,
["Vladimir Oblast, Russia"] = russia_oblast,
["Volgograd Oblast, Russia"] = russia_oblast,
["Vologda Oblast, Russia"] = russia_oblast,
["Voronezh Oblast, Russia"] = russia_oblast,
["Yaroslavl Oblast, Russia"] = russia_oblast,
-- republics
--
-- We only need to include cases that aren't just shortened versions of the full federal subject name (i.e. where
-- words like "Republic" and "Oblast" are omitted but the name is not otherwise modified; these are handled by
-- key_to_placename). Non-display-canonicalizing aliases are generally due to differences in the presence or absence
-- of "the".
["Adygea, Russia"] = russia_republic_no_the,
["Republic of Adygea, Russia"] = {alias_of = "Adygea, Russia", the = true},
["Bashkortostan, Russia"] = russia_republic_no_the,
["Republic of Bashkortostan, Russia"] = {alias_of = "Bashkortostan, Russia", the = true},
["Bashkiria, Russia"] = {alias_of = "Bashkortostan, Russia"},
["Buryatia, Russia"] = russia_republic_no_the,
["Republic of Buryatia, Russia"] = {alias_of = "Buryatia, Russia", the = true},
["Dagestan, Russia"] = russia_republic_no_the,
["Republic of Dagestan, Russia"] = {alias_of = "Dagestan, Russia", the = true},
["Ingushetia, Russia"] = russia_republic_no_the,
["Republic of Ingushetia, Russia"] = {alias_of = "Ingushetia, Russia", the = true},
["Kalmykia, Russia"] = russia_republic_no_the,
["Republic of Kalmykia, Russia"] = {alias_of = "Kalmykia, Russia", the = true},
["Karelia, Russia"] = make_russia_federal_subject_spec("republic", nil, "Republic of Karelia"),
["Republic of Karelia, Russia"] = {alias_of = "Karelia, Russia", the = true},
["Khakassia, Russia"] = russia_republic_no_the,
["Republic of Khakassia, Russia"] = {alias_of = "Khakassia, Russia", the = true},
["Mordovia, Russia"] = russia_republic_no_the,
["Republic of Mordovia, Russia"] = {alias_of = "Mordovia, Russia", the = true},
["North Ossetia-Alania, Russia"] = make_russia_federal_subject_spec("republic", nil, "North Ossetia–Alania"), -- with en-dash
["Republic of North Ossetia-Alania, Russia"] = {alias_of = "North Ossetia-Alania, Russia", the = true},
["North Ossetia, Russia"] = {alias_of = "North Ossetia-Alania, Russia", display = true},
["Alania, Russia"] = {alias_of = "North Ossetia-Alania, Russia", display = true},
["Tatarstan, Russia"] = russia_republic_no_the,
["Republic of Tatarstan, Russia"] = {alias_of = "Tatarstan, Russia", the = true},
["Altai Republic, Russia"] = russia_republic_the,
["Chechnya, Russia"] = russia_republic_no_the,
["Chechen Republic, Russia"] = {alias_of = "Chechnya, Russia", the = true},
["Chuvashia, Russia"] = russia_republic_no_the,
["Chuvash Republic, Russia"] = {alias_of = "Chuvashia, Russia", the = true},
["Kabardino-Balkaria, Russia"] = russia_republic_no_the,
["Kabardino-Balkariya, Russia"] = {alias_of = "Kabardino-Balkaria, Russia", display = true},
["Kabardino-Balkarian Republic, Russia"] = {alias_of = "Kabardino-Balkaria, Russia", the = true},
["Kabardino-Balkar Republic, Russia"] = {alias_of = "Kabardino-Balkaria, Russia",
display = "Kabardino-Balkarian Republic, Russia", the = true},
["Karachay-Cherkessia, Russia"] = russia_republic_no_the,
["Karachay-Cherkess Republic, Russia"] = {alias_of = "Karachay-Cherkessia, Russia"},
["Komi, Russia"] = make_russia_federal_subject_spec("republic", nil, "Komi Republic"),
["Komi Republic, Russia"] = {alias_of = "Komi, Russia", the = true},
["Mari El, Russia"] = russia_republic_no_the,
["Mari El Republic, Russia"] = {alias_of = "Mari El, Russia", the = true},
["Sakha, Russia"] = make_russia_federal_subject_spec("republic", nil, "Sakha Republic"),
["Sakha Republic, Russia"] = {alias_of = "Sakha, Russia", the = true},
["Yakutia, Russia"] = {alias_of = "Sakha, Russia"},
["Yakutiya, Russia"] = {alias_of = "Sakha, Russia", display = "Yakutia, Russia"},
["Republic of Yakutia (Sakha), Russia"] = {alias_of = "Sakha, Russia", display = "Sakha Republic, Russia",
the = true},
["Tuva, Russia"] = russia_republic_no_the,
["Tyva, Russia"] = {alias_of = "Tuva, Russia", display = true},
["Tuva Republic, Russia"] = {alias_of = "Tuva, Russia", the = true},
["Tyva Republic, Russia"] = {alias_of = "Tuva, Russia", display= "Tuva Republic, Russia", the = true},
["Udmurtia, Russia"] = russia_republic_no_the,
["Udmurt Republic, Russia"] = {alias_of = "Udmurtia, Russia", the = true},
-- Not included due to being unrecognized and only partly controlled:
-- ["Crimea, Russia"] = make_russia_federal_subject_spec("republic", nil, "Republic of Crimea (Russia)")
-- ["Donetsk People's Republic, Russia"] = russia_republic_the,
-- ["Luhansk People's Republic, Russia"] = russia_republic_the,
-- ["Zaporozhye Oblast, Russia"] = make_russia_federal_subject_spec("oblast", nil, "Russian occupation of Zaporizhzhia Oblast"),
-- ["Kherson Oblast, Russia"] = make_russia_federal_subject_spec("oblast", nil, "Russian occupation of Kherson Oblast"),
-- There are also federal cities (not included because they're cities):
-- Moscow, Saint Petersburg; Sevastopol (unrecognized; same status as for "Crimea, Russia" above)
}
local function russia_key_to_placename(key)
key = key:gsub(",.*", "")
local full_placename = key
if key == "Jewish Autonomous Oblast" then
return full_placename, full_placename
end
local elliptical_placename
for _, suffix in ipairs({"Krai", "Oblast"}) do
elliptical_placename = key:match("^(.*) " .. suffix .. "$")
if elliptical_placename then
return full_placename, elliptical_placename
end
end
return full_placename, full_placename
end
local function russia_placename_to_key(placename)
local key = placename .. ", Russia"
if export.russia_federal_subjects[key] then
return key
end
-- We allow the user to say e.g. "obl/Samara" in place of "obl/Samara Oblast".
for _, suffix in ipairs({"Krai", "Oblast"}) do
local suffixed_key = placename .. " " .. suffix .. ", Russia"
if export.russia_federal_subjects[suffixed_key] then
return suffixed_key
end
end
return placename .. ", Russia"
end
local function construct_russia_federal_subject_keydesc(group, key, spec)
local placename = key:gsub(",.*", "")
local linked_placename = export.construct_linked_placename(spec, placename)
local placetype = spec.placetype
if type(placetype) == "table" then
placetype = placetype[1]
end
if placetype == "oblast" then
-- Hack: Oblasts generally don't have entries under "Foo Oblast"
-- but just under "Foo", so fix the linked key appropriately;
-- doesn't apply to the Jewish Autonomous Oblast
linked_placename = linked_placename:gsub(" Oblast%]%]", "%]%] Oblast")
end
return linked_placename .. ", a [[federal subject]] ([[" .. placetype .. "]]) of [[Russia]]"
end
-- federal subjects of Russia
export.russia_group = {
key_to_placename = russia_key_to_placename,
placename_to_key = russia_placename_to_key,
default_container = "Russia",
default_keydesc = construct_russia_federal_subject_keydesc,
default_overriding_bare_label_parents = {"federal subjects of Russia", "+++"},
data = export.russia_federal_subjects,
}
export.saudi_arabia_provinces = {
["Riyadh Province, Saudi Arabia"] = {},
["Mecca Province, Saudi Arabia"] = {},
-- Name is too generic to assume it's in Saudi Arabia if not specified.
["Eastern Province, Saudi Arabia"] = {no_auto_augment_container = true, wp = "%l, %c"},
["Medina Province, Saudi Arabia"] = {wp = "%l (%c)"},
["Aseer Province, Saudi Arabia"] = {wp = "Asir"},
["Asir Province, Saudi Arabia"] = {alias_of = "Aseer Province, Saudi Arabia", display = true},
["Jazan Province, Saudi Arabia"] = {},
["Qassim Province, Saudi Arabia"] = {wp = "Al-Qassim Province"},
["Al-Qassim Province, Saudi Arabia"] = {alias_of = "Qassim Province, Saudi Arabia", display = true},
["Tabuk Province, Saudi Arabia"] = {},
["Hail Province, Saudi Arabia"] = {wp = "Ḥa'il Province"},
["Ha'il Province, Saudi Arabia"] = {alias_of = "Hail Province, Saudi Arabia", display = true},
["Ḥa'il Province, Saudi Arabia"] = {alias_of = "Hail Province, Saudi Arabia", display = true},
["Al-Jouf Province, Saudi Arabia"] = {wp = "Al-Jawf Province"},
["Al-Jawf Province, Saudi Arabia"] = {alias_of = "Al-Jouf Province, Saudi Arabia", display = true},
["Najran Province, Saudi Arabia"] = {},
["Northern Borders Province, Saudi Arabia"] = {},
["Al-Bahah Province, Saudi Arabia"] = {},
}
-- provinces of Saudi Arabia
export.saudi_arabia_group = {
key_to_placename = make_key_to_placename(", Saudi Arabia$", " Province$"),
placename_to_key = make_placename_to_key(", Saudi Arabia", " Province"),
default_container = "Saudi Arabia",
default_placetype = "province",
data = export.saudi_arabia_provinces,
}
export.south_africa_provinces = {
["Eastern Cape, South Africa"] = {the = true},
["Free State, South Africa"] = {the = true, wp = "%l (province)"},
["Gauteng, South Africa"] = {},
["KwaZulu-Natal, South Africa"] = {},
["Limpopo, South Africa"] = {},
["Mpumalanga, South Africa"] = {},
-- per Wikipedia and other sources, `North West` doesn't normally have `the` before it
["North West, South Africa"] = {wp = "%l (South African province)"},
["Northern Cape, South Africa"] = {the = true},
["Western Cape, South Africa"] = {the = true},
}
-- provinces of South Africa
export.south_africa_group = {
default_container = "South Africa",
default_placetype = "province",
default_divs = "municipalities",
data = export.south_africa_provinces,
}
export.south_korea_provinces = {
["North Chungcheong Province, South Korea"] = {},
["South Chungcheong Province, South Korea"] = {},
["Gangwon Province, South Korea"] = {wp = "%l, %c"},
["Gyeonggi Province, South Korea"] = {},
["North Gyeongsang Province, South Korea"] = {},
["South Gyeongsang Province, South Korea"] = {},
["North Jeolla Province, South Korea"] = {},
["South Jeolla Province, South Korea"] = {},
["Jeju Province, South Korea"] = {},
}
-- provinces of South Korea
export.south_korea_group = {
key_to_placename = make_key_to_placename(", South Korea$", " Province$"),
placename_to_key = make_placename_to_key(", South Korea", " Province"),
default_container = "South Korea",
default_placetype = "province",
data = export.south_korea_provinces,
}
export.spain_autonomous_communities = {
["Andalusia, Spain"] = {},
["Aragon, Spain"] = {},
["Asturias, Spain"] = {},
["Balearic Islands, Spain"] = {the = true},
["Basque Country, Spain"] = {the = true, wp = "%l (autonomous community)"},
["Canary Islands, Spain"] = {the = true},
["Cantabria, Spain"] = {},
["Castile and León, Spain"] = {},
["Castilla-La Mancha, Spain"] = {wp = "Castilla–La Mancha"}, -- with en-dash
["Catalonia, Spain"] = {},
["Community of Madrid, Spain"] = {the = true},
["Extremadura, Spain"] = {},
["Galicia, Spain"] = {wp = "%l (Spain)"},
["La Rioja, Spain"] = {},
["Murcia, Spain"] = {wp = "Region of %l"},
["Navarre, Spain"] = {},
["Valencia, Spain"] = {wp = "Valencian Community"},
["Valencian Community, Spain"] = {alias_of = "Valencia, Spain", the = true},
}
-- autonomous communities of Spain
export.spain_group = {
default_container = "Spain",
default_placetype = "autonomous community",
default_divs = {"municipalities", "comarcas"},
data = export.spain_autonomous_communities,
}
export.taiwan_counties = {
["Changhua County, Taiwan"] = {},
["Chiayi County, Taiwan"] = {},
["Hsinchu County, Taiwan"] = {},
["Hualien County, Taiwan"] = {},
["Kinmen County, Taiwan"] = {wp = "Kinmen"},
["Lienchiang County, Taiwan"] = {wp = "Matsu Islands"},
["Miaoli County, Taiwan"] = {},
["Nantou County, Taiwan"] = {},
["Penghu County, Taiwan"] = {wp = "Penghu"},
["Pingtung County, Taiwan"] = {},
["Taitung County, Taiwan"] = {},
["Yilan County, Taiwan"] = {wp = "%l, %c"},
["Yunlin County, Taiwan"] = {},
}
-- counties of Taiwan
export.taiwan_group = {
key_to_placename = make_key_to_placename(", Taiwan$", " County$"),
placename_to_key = make_placename_to_key(", Taiwan", " County"),
default_container = "Taiwan",
default_placetype = "county",
default_divs = {"districts", "townships"},
data = export.taiwan_counties,
}
export.thailand_provinces = {
-- Bangkok (special administrative area)
["Amnat Charoen Province, Thailand"] = {},
["Ang Thong Province, Thailand"] = {},
["Bueng Kan Province, Thailand"] = {},
["Buriram Province, Thailand"] = {},
["Chachoengsao Province, Thailand"] = {},
["Chai Nat Province, Thailand"] = {},
["Chaiyaphum Province, Thailand"] = {},
["Chanthaburi Province, Thailand"] = {},
["Chiang Mai Province, Thailand"] = {},
["Chiang Rai Province, Thailand"] = {},
["Chonburi Province, Thailand"] = {},
["Chumphon Province, Thailand"] = {},
["Kalasin Province, Thailand"] = {},
["Kamphaeng Phet Province, Thailand"] = {},
["Kanchanaburi Province, Thailand"] = {},
["Khon Kaen Province, Thailand"] = {},
["Krabi Province, Thailand"] = {},
["Lampang Province, Thailand"] = {},
["Lamphun Province, Thailand"] = {},
["Loei Province, Thailand"] = {},
["Lopburi Province, Thailand"] = {},
["Mae Hong Son Province, Thailand"] = {},
["Maha Sarakham Province, Thailand"] = {},
["Mukdahan Province, Thailand"] = {},
["Nakhon Nayok Province, Thailand"] = {},
["Nakhon Pathom Province, Thailand"] = {},
["Nakhon Phanom Province, Thailand"] = {},
["Nakhon Ratchasima Province, Thailand"] = {},
["Nakhon Sawon Province, Thailand"] = {},
["Nakhon Si Thammarat Province, Thailand"] = {},
["Nan Province, Thailand"] = {},
["Narathiwat Province, Thailand"] = {},
["Nong Bua Lamphu Province, Thailand"] = {},
["Nong Khai Province, Thailand"] = {},
["Nonthaburi Province, Thailand"] = {},
["Pathum Thani Province, Thailand"] = {},
["Pattani Province, Thailand"] = {},
["Phang Nga Province, Thailand"] = {},
["Phatthalung Province, Thailand"] = {},
["Phayao Province, Thailand"] = {},
["Phetchabun Province, Thailand"] = {},
["Phetchaburi Province, Thailand"] = {},
["Phichit Province, Thailand"] = {},
["Phitsanulok Province, Thailand"] = {},
["Phra Nakhon Si Ayutthaya Province, Thailand"] = {},
["Phrae Province, Thailand"] = {},
["Phuket Province, Thailand"] = {},
["Prachinburi Province, Thailand"] = {},
["Prachuap Khiri Khan Province, Thailand"] = {},
["Ranong Province, Thailand"] = {},
["Ratchaburi Province, Thailand"] = {},
["Rayong Province, Thailand"] = {},
["Roi Et Province, Thailand"] = {},
["Sa Kaeo Province, Thailand"] = {},
["Sakon Nakhon Province, Thailand"] = {},
["Samut Prakan Province, Thailand"] = {},
["Samut Sakhon Province, Thailand"] = {},
["Samut Songkhram Province, Thailand"] = {},
["Saraburi Province, Thailand"] = {},
["Satun Province, Thailand"] = {},
["Sing Buri Province, Thailand"] = {},
["Sisaket Province, Thailand"] = {},
["Songkhla Province, Thailand"] = {},
["Sukhothai Province, Thailand"] = {},
["Suphan Buri Province, Thailand"] = {},
["Surat Thani Province, Thailand"] = {},
["Surin Province, Thailand"] = {},
["Tak Province, Thailand"] = {},
["Trang Province, Thailand"] = {},
["Trat Province, Thailand"] = {},
["Ubon Ratchathani Province, Thailand"] = {},
["Udon Thani Province, Thailand"] = {},
["Uthai Thani Province, Thailand"] = {},
["Uttaradit Province, Thailand"] = {},
["Yala Province, Thailand"] = {},
["Yasothon Province, Thailand"] = {},
}
-- provinces of Thailand
export.thailand_group = {
key_to_placename = make_key_to_placename(", Thailand$", " Province$"),
placename_to_key = make_placename_to_key(", Thailand", " Province"),
default_container = "Thailand",
default_placetype = "province",
default_divs = "districts",
-- For obscure reasons, provinces of Iran, Laos, Thailand and Vietnam use lowercase 'province'
default_wp = "%e province",
data = export.thailand_provinces,
}
export.turkey_provinces = {
["Adana Province, Turkey"] = {}, -- code 01
["Adıyaman Province, Turkey"] = {}, -- code 02
["Afyonkarahisar Province, Turkey"] = {}, -- code 03
["Ağrı Province, Turkey"] = {}, -- code 04
["Amasya Province, Turkey"] = {}, -- code 05
["Ankara Province, Turkey"] = {}, -- code 06
["Antalya Province, Turkey"] = {}, -- code 07
["Artvin Province, Turkey"] = {}, -- code 08
["Aydın Province, Turkey"] = {}, -- code 09
["Balıkesir Province, Turkey"] = {}, -- code 10
["Bilecik Province, Turkey"] = {}, -- code 11
["Bingöl Province, Turkey"] = {}, -- code 12
["Bitlis Province, Turkey"] = {}, -- code 13
["Bolu Province, Turkey"] = {}, -- code 14
["Burdur Province, Turkey"] = {}, -- code 15
["Bursa Province, Turkey"] = {}, -- code 16
["Çanakkale Province, Turkey"] = {}, -- code 17
["Çankırı Province, Turkey"] = {}, -- code 18
["Çorum Province, Turkey"] = {}, -- code 19
["Denizli Province, Turkey"] = {}, -- code 20
["Diyarbakır Province, Turkey"] = {}, -- code 21
["Edirne Province, Turkey"] = {}, -- code 22
["Elazığ Province, Turkey"] = {}, -- code 23
["Elâzığ Province, Turkey"] = {alias_of = "Elazığ Province, Turkey", display = true},
["Erzincan Province, Turkey"] = {}, -- code 24
["Erzurum Province, Turkey"] = {}, -- code 25
["Eskişehir Province, Turkey"] = {}, -- code 26
["Gaziantep Province, Turkey"] = {}, -- code 27
["Giresun Province, Turkey"] = {}, -- code 28
["Gümüşhane Province, Turkey"] = {}, -- code 29
["Hakkâri Province, Turkey"] = {}, -- code 30
["Hakkari Province, Turkey"] = {alias_of = "Hakkâri Province, Turkey", display = true},
["Hatay Province, Turkey"] = {}, -- code 31
["Isparta Province, Turkey"] = {}, -- code 32
["Mersin Province, Turkey"] = {}, -- code 33
-- ["Istanbul Province, Turkey"] = {}, -- code 34; this is coextensive with the city itself
["İzmir Province, Turkey"] = {}, -- code 35
["Izmir Province, Turkey"] = {alias_of = "İzmir Province, Turkey", display = true},
["Kars Province, Turkey"] = {}, -- code 36
["Kastamonu Province, Turkey"] = {}, -- code 37
["Kayseri Province, Turkey"] = {}, -- code 38
["Kırklareli Province, Turkey"] = {}, -- code 39
["Kırşehir Province, Turkey"] = {}, -- code 40
["Kocaeli Province, Turkey"] = {}, -- code 41
["Konya Province, Turkey"] = {}, -- code 42
["Kütahya Province, Turkey"] = {}, -- code 43
["Malatya Province, Turkey"] = {}, -- code 44
["Manisa Province, Turkey"] = {}, -- code 45
["Kahramanmaraş Province, Turkey"] = {}, -- code 46
["Mardin Province, Turkey"] = {}, -- code 47
["Muğla Province, Turkey"] = {}, -- code 48
["Muş Province, Turkey"] = {}, -- code 49
["Nevşehir Province, Turkey"] = {}, -- code 50
["Niğde Province, Turkey"] = {}, -- code 51
["Ordu Province, Turkey"] = {}, -- code 52
["Rize Province, Turkey"] = {}, -- code 53
["Sakarya Province, Turkey"] = {}, -- code 54
["Samsun Province, Turkey"] = {}, -- code 55
["Siirt Province, Turkey"] = {}, -- code 56
["Sinop Province, Turkey"] = {}, -- code 57
["Sivas Province, Turkey"] = {}, -- code 58
["Tekirdağ Province, Turkey"] = {}, -- code 59
["Tokat Province, Turkey"] = {}, -- code 60
["Trabzon Province, Turkey"] = {}, -- code 61
["Tunceli Province, Turkey"] = {}, -- code 62
["Şanlıurfa Province, Turkey"] = {}, -- code 63
["Uşak Province, Turkey"] = {}, -- code 64
["Van Province, Turkey"] = {}, -- code 65
["Yozgat Province, Turkey"] = {}, -- code 66
["Zonguldak Province, Turkey"] = {}, -- code 67
["Aksaray Province, Turkey"] = {}, -- code 68
["Bayburt Province, Turkey"] = {}, -- code 69
["Karaman Province, Turkey"] = {}, -- code 70
["Kırıkkale Province, Turkey"] = {}, -- code 71
["Batman Province, Turkey"] = {}, -- code 72
["Şırnak Province, Turkey"] = {}, -- code 73
["Bartın Province, Turkey"] = {}, -- code 74
["Ardahan Province, Turkey"] = {}, -- code 75
["Iğdır Province, Turkey"] = {}, -- code 76
["Yalova Province, Turkey"] = {}, -- code 77
["Karabük Province, Turkey"] = {}, -- code 78
["Kilis Province, Turkey"] = {}, -- code 79
["Osmaniye Province, Turkey"] = {}, -- code 80
["Düzce Province, Turkey"] = {}, -- code 81
}
-- provinces of Turkey
export.turkey_group = {
key_to_placename = make_key_to_placename(", Turkey$", " Province$"),
placename_to_key = make_placename_to_key(", Turkey", " Province"),
default_container = "Turkey",
default_placetype = "province",
default_divs = "districts",
data = export.turkey_provinces,
}
export.ukraine_oblasts = {
["Cherkasy Oblast, Ukraine"] = {}, -- capital [[Cherkasy]], license plate prefix CA, IA
["Chernihiv Oblast, Ukraine"] = {}, -- capital [[Chernihiv]], license plate prefix CB, IB
["Chernivtsi Oblast, Ukraine"] = {}, -- capital [[Chernivtsi]], license plate prefix CE, IE
-- apparently will be renamed to 'Dnipro Oblast'
["Dnipropetrovsk Oblast, Ukraine"] = {}, -- capital [[Dnipro]], license plate prefix AE, KE
["Donetsk Oblast, Ukraine"] = {}, -- capital ''[[Donetsk]] ([[Kramatorsk]])'', license plate prefix AH, KH
["Ivano-Frankivsk Oblast, Ukraine"] = {}, -- capital [[Ivano-Frankivsk]], license plate prefix AT, KT
["Kharkiv Oblast, Ukraine"] = {}, -- capital [[Kharkiv]], license plate prefix AX, KX
["Kherson Oblast, Ukraine"] = {}, -- capital ''[[Kherson]]'', license plate prefix ''BT, HT''
["Khmelnytskyi Oblast, Ukraine"] = {}, -- capital [[Khmelnytskyi]], license plate prefix BX, HX
-- apparently will be renamed to 'Kropyvnytskyi Oblast'
["Kirovohrad Oblast, Ukraine"] = {}, -- capital [[Kropyvnytskyi]], license plate prefix BA, HA
["Kyiv Oblast, Ukraine"] = {}, -- capital [[Kyiv]], license plate prefix AI, KI
["Kiev Oblast, Ukraine"] = {alias_of = "Kyiv Oblast, Ukraine", display = true},
["Luhansk Oblast, Ukraine"] = {}, -- capital ''[[Luhansk]] ([[Sievierodonetsk]])'', license plate prefix BB, HB
["Lviv Oblast, Ukraine"] = {}, -- capital [[Lviv]], license plate prefix BC, HC
["Mykolaiv Oblast, Ukraine"] = {}, -- capital [[Mykolaiv]], license plate prefix BE, HE
["Odesa Oblast, Ukraine"] = {}, -- capital [[Odesa]], license plate prefix BH, HH
["Odessa Oblast, Ukraine"] = {alias_of = "Odesa Oblast, Ukraine", display = true},
["Poltava Oblast, Ukraine"] = {}, -- capital [[Poltava]], license plate prefix BI, HI
["Rivne Oblast, Ukraine"] = {}, -- capital [[Rivne]], license plate prefix BK, HK
["Sumy Oblast, Ukraine"] = {}, -- capital [[Sumy]], license plate prefix BM, HM
["Ternopil Oblast, Ukraine"] = {}, -- capital [[Ternopil]], license plate prefix BO, HO
["Vinnytsia Oblast, Ukraine"] = {}, -- capital [[Vinnytsia]], license plate prefix AB, KB
["Volyn Oblast, Ukraine"] = {}, -- capital [[Lutsk]], license plate prefix AC, KC
["Zakarpattia Oblast, Ukraine"] = {}, -- capital [[Uzhhorod]], license plate prefix AO, KO
["Zaporizhzhia Oblast, Ukraine"] = {}, -- capital ''[[Zaporizhzhia]]'', license plate prefix AP, KP
["Zaporizhia Oblast, Ukraine"] = {alias_of = "Zaporizhzhia Oblast, Ukraine", display = true},
["Zhytomyr Oblast, Ukraine"] = {}, -- capital [[Zhytomyr]], license plate prefix AM, KM
}
-- oblasts of Ukraine
export.ukraine_group = {
key_to_placename = make_key_to_placename(", Ukraine$", " Oblast$"),
placename_to_key = make_placename_to_key(", Ukraine", " Oblast"),
default_container = "Ukraine",
default_placetype = "oblast",
default_divs = {"raions", "hromadas"},
data = export.ukraine_oblasts,
}
export.united_kingdom_constituent_countries = {
["England"] = {divs = {
"counties",
"districts",
{type = "local government districts", cat_as = "districts"},
{
type = "local government districts with borough status",
cat_as = {"districts", "boroughs"},
},
{type = "boroughs", cat_as = {"districts", "boroughs"}},
{type = "civil parishes", container_parent_type = false},
}},
["Northern Ireland"] = {
placetype = {"constituent country", "province", "country"},
divs = {"counties", "districts"},
},
["Scotland"] = {divs = {
{type = "council areas", container_parent_type = false},
"districts",
}},
["Wales"] = {divs = {
"counties",
{type = "county boroughs", container_parent_type = false},
{type = "communities", container_parent_type = false},
{type = "Welsh communities", cat_as = {{type = "communities", container_parent_type = false}}},
}},
}
-- constituent countries and provinces of the United Kingdom
export.united_kingdom_group = {
placename_to_key = false,
default_container = "United Kingdom",
default_placetype = {"constituent country", "country"},
addl_divs = {
"traditional counties",
{type = "historical counties", cat_as = "traditional counties"},
},
-- Don't create categories like 'Category:en:Towns in the United Kingdom'
-- or 'Category:en:Places in the United Kingdom'.
default_no_container_cat = true,
data = export.united_kingdom_constituent_countries,
}
export.england_counties = {
-- NOTE: We used to have various other "no longer" counties commented out, which seems to refer to counties that
-- existed officially at some point between 1889 and 1974, which I have removed. I have only kept the three
-- ceremonial counties that existed from 1974 (when ceremonial counties were created) to 1996, as well as those
-- still considered "historic counties" per [[w:Historic counties of England]].
-- ["Avon, England"] = {wp = "%l (county)"}, -- no longer (1974 to 1996)
["Bedfordshire, England"] = {},
["Berkshire, England"] = {},
-- ["Brighton and Hove, England"] = {}, -- city
-- ["Bristol, England"] = {}, -- city
["Buckinghamshire, England"] = {},
["Cambridgeshire, England"] = {},
["Cheshire, England"] = {},
-- ["Cleveland, England"] = {wp = "%l (county)"}, -- no longer (1974 to 1996)
["Cornwall, England"] = {},
-- ["Cumberland, England"] = {}, -- no longer (historic county)
["Cumbria, England"] = {},
["Derbyshire, England"] = {},
["Devon, England"] = {},
["Dorset, England"] = {},
["County Durham, England"] = {},
["East Sussex, England"] = {},
["Essex, England"] = {},
["Gloucestershire, England"] = {},
["Greater London, England"] = {},
["Greater Manchester, England"] = {},
["Hampshire, England"] = {},
["Herefordshire, England"] = {},
["Hertfordshire, England"] = {},
-- ["Humberside, England"] = {}, -- no longer (1974 to 1996)
-- ["Huntingdonshire, England"] = {}, -- no longer (historic county)
["Isle of Wight, England"] = {the = true},
["Kent, England"] = {},
["Lancashire, England"] = {},
["Leicestershire, England"] = {},
["Lincolnshire, England"] = {},
["Merseyside, England"] = {},
-- ["Middlesex, England"] = {}, -- no longer (historic county)
["Norfolk, England"] = {},
["Northamptonshire, England"] = {},
["Northumberland, England"] = {},
["North Yorkshire, England"] = {},
["Nottinghamshire, England"] = {},
["Oxfordshire, England"] = {},
["Rutland, England"] = {},
["Shropshire, England"] = {},
["Somerset, England"] = {},
["South Humberside, England"] = {},
["South Yorkshire, England"] = {},
["Staffordshire, England"] = {},
["Suffolk, England"] = {},
["Surrey, England"] = {},
-- ["Sussex, England"] = {}, -- no longer (historic county)
["Tyne and Wear, England"] = {},
["Warwickshire, England"] = {},
["West Midlands, England"] = {the = true, wp = "%l (county)"},
-- ["Westmorland, England"] = {}, -- no longer (historic county)
["West Sussex, England"] = {},
["West Yorkshire, England"] = {},
["Wiltshire, England"] = {},
["Worcestershire, England"] = {},
-- ["Yorkshire, England"] = {}, -- no longer (historic county)
["East Riding of Yorkshire, England"] = {the = true},
}
-- counties of England
export.england_group = {
default_container = {key = "England", placetype = "constituent country"},
default_placetype = "county",
default_divs = {
"districts",
{type = "local government districts", cat_as = "districts"},
{
type = "local government districts with borough status",
cat_as = {"districts", "boroughs"},
},
{type = "boroughs", cat_as = {"districts", "boroughs"}},
"civil parishes",
},
data = export.england_counties,
}
export.northern_ireland_counties = {
["County Antrim, Northern Ireland"] = {},
["County Armagh, Northern Ireland"] = {},
["City of Belfast, Northern Ireland"] = {the = true, is_city = true, wp = "Belfast"},
["County Down, Northern Ireland"] = {},
["County Fermanagh, Northern Ireland"] = {},
["County Londonderry, Northern Ireland"] = {},
["City of Derry, Northern Ireland"] = {the = true, is_city = true, wp = "Derry"},
["County Tyrone, Northern Ireland"] = {},
}
-- counties of Northern Ireland
export.northern_ireland_group = {
key_to_placename = make_irish_type_key_to_placename(", Northern Ireland$"),
placename_to_key = make_irish_type_placename_to_key(", Northern Ireland"),
default_container = {key = "Northern Ireland", placetype = "constituent country"},
default_placetype = "county",
data = export.northern_ireland_counties,
}
export.scotland_council_areas = {
["Aberdeenshire, Scotland"] = {},
["Angus, Scotland"] = {wp = "%l, %c"},
["Argyll and Bute, Scotland"] = {},
["City of Aberdeen, Scotland"] = {the = true, wp = "Aberdeen"},
["Aberdeen"] = {alias_of = "City of Aberdeen, Scotland"},
["Aberdeen City"] = {alias_of = "City of Aberdeen, Scotland"},
["City of Dundee, Scotland"] = {the = true, wp = "Dundee"},
["Dundee"] = {alias_of = "City of Dundee, Scotland"},
["Dundee City"] = {alias_of = "City of Dundee, Scotland"},
["City of Edinburgh, Scotland"] = {the = true, wp = "%l council area"},
["Edinburgh"] = {alias_of = "City of Edinburgh, Scotland"},
["City of Glasgow, Scotland"] = {the = true, wp = "Glasgow"},
["Glasgow"] = {alias_of = "City of Glasgow, Scotland"},
["Clackmannanshire, Scotland"] = {},
["Dumfries and Galloway, Scotland"] = {},
["East Ayrshire, Scotland"] = {},
["East Dunbartonshire, Scotland"] = {},
["East Lothian, Scotland"] = {},
["East Renfrewshire, Scotland"] = {},
["Falkirk, Scotland"] = {wp = "%l council area"},
["Fife, Scotland"] = {},
["Highland, Scotland"] = {wp = "%l council area"},
["Inverclyde, Scotland"] = {},
["Midlothian, Scotland"] = {},
["Moray, Scotland"] = {},
["North Ayrshire, Scotland"] = {},
["North Lanarkshire, Scotland"] = {},
["Orkney Islands, Scotland"] = {the = true},
["Perth and Kinross, Scotland"] = {},
["Renfrewshire, Scotland"] = {},
["Scottish Borders, Scotland"] = {the = true},
["Shetland Islands, Scotland"] = {the = true},
["South Ayrshire, Scotland"] = {},
["South Lanarkshire, Scotland"] = {},
["Stirling, Scotland"] = {wp = "%l council area"},
["West Dunbartonshire, Scotland"] = {},
["West Lothian, Scotland"] = {},
["Western Isles, Scotland"] = {the = true, wp = "Outer Hebrides"},
["Na h-Eileanan Siar, Scotland"] = {alias_of = "Western Isles, Scotland"},
}
-- council areas of Scotland
export.scotland_group = {
default_container = {key = "Scotland", placetype = "constituent country"},
default_placetype = "council area",
data = export.scotland_council_areas,
}
export.wales_principal_areas = {
["Blaenau Gwent, Wales"] = {},
["Bridgend, Wales"] = {wp = "%l County Borough"},
["Caerphilly, Wales"] = {wp = "%l County Borough"},
-- ["Cardiff, Wales"] = {placetype = "city"},
["Carmarthenshire, Wales"] = {placetype = "county"},
["Ceredigion, Wales"] = {placetype = "county"},
["Conwy, Wales"] = {wp = "%l County Borough"},
["Denbighshire, Wales"] = {placetype = "county"},
["Flintshire, Wales"] = {placetype = "county"},
["Gwynedd, Wales"] = {placetype = "county"},
["Isle of Anglesey, Wales"] = {the = true, placetype = "county"},
["Anglesey, Wales"] = {alias_of = "Isle of Anglesey, Wales"}, -- differs in "the"
["Merthyr Tydfil, Wales"] = {wp = "%l County Borough"},
["Monmouthshire, Wales"] = {placetype = "county"},
["Neath Port Talbot, Wales"] = {},
-- ["Newport, Wales"] = {placetype = "city", wp = "%l, %c"},
["Pembrokeshire, Wales"] = {placetype = "county"},
["Powys, Wales"] = {placetype = "county"},
["Rhondda Cynon Taf, Wales"] = {},
-- ["Swansea, Wales"] = {placetype = "city"},
["Torfaen, Wales"] = {},
["Vale of Glamorgan, Wales"] = {the = true},
["Wrexham, Wales"] = {wp = "%l County Borough"},
}
-- principal areas (cities, counties and county boroughs) of Wales
export.wales_group = {
default_container = {key = "Wales", placetype = "constituent country"},
default_placetype = "county borough",
data = export.wales_principal_areas,
}
export.united_states_states = {
["Alabama, USA"] = {},
["Alaska, USA"] = {divs = {
{type = "boroughs", container_parent_type = "counties"},
{type = "borough seats", container_parent_type = "county seats"},
}},
["Arizona, USA"] = {},
["Arkansas, USA"] = {},
["California, USA"] = {},
["Colorado, USA"] = {divs = {"counties", "county seats", "municipalities"}},
["Connecticut, USA"] = {divs = {"counties", "county seats", "municipalities"}},
["Delaware, USA"] = {},
["Florida, USA"] = {},
["Georgia, USA"] = {wp = "%l (U.S. state)"},
["Hawaii, USA"] = {addl_parents = {"Polynesia"}},
["Idaho, USA"] = {},
["Illinois, USA"] = {},
["Indiana, USA"] = {},
["Iowa, USA"] = {},
["Kansas, USA"] = {},
["Kentucky, USA"] = {},
["Louisiana, USA"] = {divs = {
{type = "parishes", container_parent_type = "counties"},
{type = "parish seats", container_parent_type = "county seats"},
}},
["Maine, USA"] = {},
["Maryland, USA"] = {},
["Massachusetts, USA"] = {},
["Michigan, USA"] = {},
["Minnesota, USA"] = {},
["Mississippi, USA"] = {},
["Missouri, USA"] = {},
["Montana, USA"] = {},
["Nebraska, USA"] = {},
["Nevada, USA"] = {},
["New Hampshire, USA"] = {},
["New Jersey, USA"] = {divs = {
"counties", "county seats",
{type = "boroughs", prep = "in"},
}},
["New Mexico, USA"] = {},
["New York, USA"] = {wp = "%l (state)"},
["North Carolina, USA"] = {},
["North Dakota, USA"] = {},
["Ohio, USA"] = {},
["Oklahoma, USA"] = {},
["Oregon, USA"] = {},
["Pennsylvania, USA"] = {divs = {
"counties", "county seats",
{type = "boroughs", prep = "in"},
}},
["Rhode Island, USA"] = {},
["South Carolina, USA"] = {},
["South Dakota, USA"] = {},
["Tennessee, USA"] = {},
["Texas, USA"] = {},
["Utah, USA"] = {},
["Vermont, USA"] = {},
["Virginia, USA"] = {},
["Washington, USA"] = {wp = "%l (state)"},
["West Virginia, USA"] = {},
["Wisconsin, USA"] = {},
["Wyoming, USA"] = {},
}
-- states of the United States
export.united_states_group = {
placename_to_key = make_placename_to_key(", USA"),
default_container = "United States",
default_placetype = "state",
default_divs = {"counties", "county seats"},
addl_divs = {
{type = "census-designated places", prep = "in"},
{type = "unincorporated communities", prep = "in"},
},
data = export.united_states_states,
}
export.vietnam_provinces = {
-- [[Northeast (Vietnam)|Northeast]] region
["Bắc Giang Province, Vietnam"] = {}, -- capital [[Bắc Giang]]
["Bắc Kạn Province, Vietnam"] = {}, -- capital [[Bắc Kạn]]
["Cao Bằng Province, Vietnam"] = {}, -- capital [[Cao Bằng]]
["Hà Giang Province, Vietnam"] = {}, -- capital [[Hà Giang]]
["Lạng Sơn Province, Vietnam"] = {}, -- capital [[Lạng Sơn]]
["Phú Thọ Province, Vietnam"] = {}, -- capital [[Việt Trì]]
["Quảng Ninh Province, Vietnam"] = {}, -- capital [[Hạ Long]]
["Thái Nguyên Province, Vietnam"] = {}, -- capital [[Thái Nguyên]]
["Tuyên Quang Province, Vietnam"] = {}, -- capital [[Tuyên Quang]]
-- [[Northwest (Vietnam)|Northwest]] region
["Lào Cai Province, Vietnam"] = {}, -- capital [[Lào Cai]]
["Yên Bái Province, Vietnam"] = {}, -- capital [[Yên Bái]]
["Điện Biên Province, Vietnam"] = {}, -- capital [[Điện Biên Phủ]]
["Hoà Bình Province, Vietnam"] = {}, -- capital [[Hoà Bình City|Hoà Bình]]
["Hòa Bình Province, Vietnam"] = {alias_of = "Hoà Bình Province, Vietnam", display = true},
["Lai Châu Province, Vietnam"] = {}, -- capital [[Lai Châu]]
["Sơn La Province, Vietnam"] = {}, -- capital [[Sơn La]]
-- [[Red River Delta]] region
["Bắc Ninh Province, Vietnam"] = {}, -- capital [[Bắc Ninh]]
["Hà Nam Province, Vietnam"] = {}, -- capital [[Phủ Lý]]
["Hải Dương Province, Vietnam"] = {}, -- capital [[Hải Dương]]
["Hưng Yên Province, Vietnam"] = {}, -- capital [[Hưng Yên]]
["Nam Định Province, Vietnam"] = {}, -- capital [[Nam Định]]
["Ninh Bình Province, Vietnam"] = {}, -- capital [[Ninh Bình|Hoa Lư]]
["Thái Bình Province, Vietnam"] = {}, -- capital [[Thái Bình]]
["Vĩnh Phúc Province, Vietnam"] = {}, -- capital [[Vĩnh Yên]]
-- ["Hanoi"] = {placetype = {"municipality", "city"}}, -- capital [[Hoàn Kiếm district]]
-- ["Haiphong"] = {placetype = {"municipality", "city"}}, -- capital [[Hồng Bàng district]]
-- [[North Central Coast]] region
["Hà Tĩnh Province, Vietnam"] = {}, -- capital [[Hà Tĩnh]]
["Nghệ An Province, Vietnam"] = {}, -- capital [[Vinh]]
["Quảng Bình Province, Vietnam"] = {}, -- capital [[Đồng Hới]]
["Quảng Trị Province, Vietnam"] = {}, -- capital [[Đông Hà]]
["Thanh Hoá Province, Vietnam"] = {}, -- capital [[Thanh Hoá]]
["Thanh Hóa Province, Vietnam"] = {alias_of = "Thanh Hoá Province, Vietnam", display = true},
-- ["Hue"] = {placetype = {"municipality", "city"}, wp = "Huế"}, -- capital [[Thuận Hoá district]]
-- [[Central Highlands (Vietnam)|Central Highlands]] region
["Đắk Lắk Province, Vietnam"] = {}, -- capital [[Buôn Ma Thuột]]
["Đăk Nông Province, Vietnam"] = {}, -- capital [[Gia Nghĩa]]
["Gia Lai Province, Vietnam"] = {}, -- capital [[Pleiku]]
["Kon Tum Province, Vietnam"] = {}, -- capital [[Kon Tum]]
["Lâm Đồng Province, Vietnam"] = {}, -- capital [[Đà Lạt]]
-- [[South Central Coast]] region
["Bình Định Province, Vietnam"] = {}, -- capital [[Quy Nhon]]
["Bình Thuận Province, Vietnam"] = {}, -- capital [[Phan Thiết]]
["Khánh Hoà Province, Vietnam"] = {}, -- capital [[Nha Trang]]
["Khánh Hòa Province, Vietnam"] = {alias_of = "Khánh Hoà Province, Vietnam", display = true},
["Ninh Thuận Province, Vietnam"] = {}, -- capital [[Phan Rang–Tháp Chàm]]
["Phú Yên Province, Vietnam"] = {}, -- capital [[Tuy Hoà]]
["Quảng Nam Province, Vietnam"] = {}, -- capital [[Tam Kỳ]]
["Quảng Ngãi Province, Vietnam"] = {}, -- capital [[Quảng Ngãi]]
-- ["Da Nang"] = {placetype = {"municipality", "city"}}, -- capital [[Hải Châu district]]
-- [[Southeast (Vietnam)|Southeast]] region
["Bà Rịa–Vũng Tàu Province, Vietnam"] = {}, -- capital [[Bà Rịa]]
["Bình Dương Province, Vietnam"] = {}, -- capital [[Thủ Dầu Một]]
["Bình Phước Province, Vietnam"] = {}, -- capital [[Đồng Xoài]]
["Đồng Nai Province, Vietnam"] = {}, -- capital [[Biên Hoà]]
["Tây Ninh Province, Vietnam"] = {}, -- capital [[Tây Ninh]]
-- ["Ho Chi Minh City"] = {placetype = {"municipality", "city"}}, -- capital [[District 1, Ho Chi Minh City|'''District 1''']]
-- [[Mekong Delta]] region
["An Giang Province, Vietnam"] = {}, -- capital [[Long Xuyên]]
["Bạc Liêu Province, Vietnam"] = {}, -- capital [[Bạc Liêu]]
["Bến Tre Province, Vietnam"] = {}, -- capital [[Bến Tre]]
["Cà Mau Province, Vietnam"] = {}, -- capital [[Cà Mau]]
["Đồng Tháp Province, Vietnam"] = {}, -- capital [[Cao Lãnh City|Cao Lãnh]]
["Hậu Giang Province, Vietnam"] = {}, -- capital [[Vị Thanh]]
["Kiên Giang Province, Vietnam"] = {}, -- capital [[Rạch Giá]]
["Long An Province, Vietnam"] = {}, -- capital [[Tân An]]
["Sóc Trăng Province, Vietnam"] = {}, -- capital [[Sóc Trăng]]
["Tiền Giang Province, Vietnam"] = {}, -- capital [[Mỹ Tho]]
["Trà Vinh Province, Vietnam"] = {}, -- capital [[Trà Vinh]]
["Vĩnh Long Province, Vietnam"] = {}, -- capital [[Vĩnh Long]]
-- ["Can Tho"] = {placetype = {"municipality", "city"}, wp = "Cần Thơ"}, -- capital [[Ninh Kiều district]]
}
-- provinces of Vietnam
export.vietnam_group = {
key_to_placename = make_key_to_placename(", Vietnam$", " Province$"),
placename_to_key = make_placename_to_key(", Vietnam", " Province"),
default_container = "Vietnam",
default_placetype = "province",
-- There may not be enough districts to subcategorize like this.
-- default_divs = "districts",
-- For obscure reasons, provinces of Iran, Laos, Thailand and Vietnam use lowercase 'province'
default_wp = "%e province",
data = export.vietnam_provinces,
}
-----------------------------------------------------------------------------------
-- City data --
-----------------------------------------------------------------------------------
export.australia_cities = {
["Adelaide"] = {container = "South Australia"}, -- 1,450,000 (Agglomeration)
["Brisbane"] = {container = "Queensland"}, -- 3,450,000 (Conglomeration; including the Gold Coast [750,997 2024 estiamte])
["Canberra"] = {container = {key = "Australian Capital Territory, Australia", placetype = "territory"}}, -- 510,641 (2024 estimate)
["Melbourne"] = {container = "Victoria"}, -- 5,200,000 (Agglomeration)
["Newcastle, New South Wales"] = {container = "New South Wales", wp = "%l, %c"}, -- 534,033 (2024 estimate)
["Newcastle"] = {alias_of = "Newcastle, New South Wales"},
["Perth"] = {container = "Western Australia"}, -- 2,350,000 (Agglomeration)
["Sydney"] = {container = "New South Wales"}, -- 5,100,000 (Agglomeration)
}
export.australia_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(", Australia", "state"),
default_placetype = "city",
data = export.australia_cities,
}
export.brazil_cities = {
-- Figures from citypopulation.de; retrieved 2025-04-27; reference date 2025-01-01.
["São Paulo"] = {container = "São Paulo"}, -- 22,600,000 (Consolidated Urban Area; including Guarulhos)
["Sao Paulo"] = {alias_of = "São Paulo", display = true},
["Rio de Janeiro"] = {container = "Rio de Janeiro"}, -- 13,600,000 (Consolidated Urban Area)
["Belo Horizonte"] = {container = "Minas Gerais"}, -- 5,300,000
["Recife"] = {container = "Pernambuco"}, -- 4,100,000
["Porto Alegre"] = {container = "Rio Grande do Sul"}, -- 3,950,000 (Consolidated Urban Area)
["Brasília"] = {container = "Distrito Federal"}, -- 3,850,000
["Brasilia"] = {alias_of = "Brasília", display = true},
["Fortaleza"] = {container = "Ceará"}, -- 3,825,000
["Salvador"] = {container = "Bahia", wp = "%l, %c", commonscat = "%l (%c)"}, -- 3,400,000
["Curitiba"] = {container = "Paraná"}, -- 3,375,000
["Campinas"] = {container = "São Paulo"}, -- 3,250,000
["Goiânia"] = {container = "Goiás"}, -- 2,525,000
["Goiania"] = {alias_of = "Goiânia", display = true},
["Manaus"] = {container = "Amazonas"}, -- 2,275,000
["Belém"] = {container = "Pará"}, -- 2,200,000
["Belem"] = {alias_of = "Belém", display = true},
["Vitória"] = {container = "Espírito Santo", wp = "%l, %c"}, -- 1,870,000
["Vitoria"] = {alias_of = "Vitória", display = true},
["Santos"] = {container = "São Paulo", wp = "%l, %c"}, -- 1,760,000
["São Luís"] = {container = "Maranhão", wp = "%l, %c"}, -- 1,530,000
["Sao Luis"] = {alias_of = "São Luís", display = true},
["Natal"] = {container = "Rio Grande do Norte", wp = "%l, %c"}, -- 1,360,000
["Florianópolis"] = {container = "Santa Catarina"}, -- 1,260,000
["Florianopolis"] = {alias_of = "Florianópolis", display = true},
["Maceió"] = {container = "Alagoas"}, -- 1,220,000
["Maceio"] = {alias_of = "Maceió", display = true},
["João Pessoa"] = {container = "Paraíba", wp = "%l, %c"}, -- 1,210,000
["Joao Pessoa"] = {alias_of = "João Pessoa", display = true},
["São José dos Campos"] = {container = "São Paulo"}, -- 1,090,000
["Sao Jose dos Campos"] = {alias_of = "São José dos Campos", display = true},
["Londrina"] = {container = "Paraná"}, -- 1,050,000
["Teresina"] = {container = "Piauí"}, -- 1,040,000
}
export.brazil_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(", Brazil", "state"),
default_placetype = "city",
data = export.brazil_cities,
}
export.canada_cities = {
-- Figures from citypopulation.de; retrieved 2025-04-27; reference date 2025-01-01.
["Toronto"] = {container = "Ontario"}, -- 7,850,000 (Consolidated Urban Area; including Hamilton)
["Montreal"] = {container = "Quebec"}, -- 4,500,000 (Consolidated Urban Area)
["Vancouver"] = {container = "British Columbia"}, -- 3,175,000 (Consolidated Urban Area)
["Calgary"] = {container = "Alberta"}, -- 1,510,000 (Consolidated Urban Area)
["Edmonton"] = {container = "Alberta"}, -- 1,460,000 (Consolidated Urban Area)
["Ottawa"] = {container = "Ontario"}, -- 1,390,000 (Consolidated Urban Area)
["Quebec City"] = {container = "Quebec"}, -- 839,311 metro per Wikipedia (2021 census)
["Winnipeg"] = {container = "Manitoba"}, -- 834,678 metro per Wikipedia (2021 census)
["Hamilton"] = {container = "Ontario", wp = "%l, %c"}, -- 785,184 metro per Wikipedia (2021 census)
["Kitchener"] = {container = "Ontario", wp = "%l, %c"}, -- 575,847 metro per Wikipedia (2021 census)
}
export.canada_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(", Canada", "province"),
default_placetype = "city",
data = export.canada_cities,
}
export.france_cities = {
-- Figures from citypopulation.de unless otherwise indicated; retrieved 2025-04-26; reference date 2025-01-01.
["Paris"] = {container = "Île-de-France"}, -- 11,500,000 (Conglomeration)
["Lyon"] = {container = "Auvergne-Rhône-Alpes"}, -- 2,050,000 (Conglomeration)
["Lyons"] = {alias_of = "Lyon", display = true},
["Marseille"] = {container = "Provence-Alpes-Côte d'Azur"}, -- 1,710,000 (Conglomeration)
["Marseilles"] = {alias_of = "Marseille", display = true},
["Lille"] = {container = "Hauts-de-France"}, -- 1,320,000 (Conglomeration)
["Bordeaux"] = {container = "Nouvelle-Aquitaine"}, -- 1,160,000 (Conglomeration)
["Toulouse"] = {container = "Occitania"}, -- 1,150,000 (Conglomeration)
["Nice"] = {container = "Provence-Alpes-Côte d'Azur"},
["Nantes"] = {container = "Pays de la Loire"},
["Strasbourg"] = {container = "Grand Est"},
["Rennes"] = {container = "Brittany"},
}
export.france_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(", France", "region"),
default_placetype = "city",
data = export.france_cities,
}
export.germany_cities = {
-- Figures from citypopulation.de unless otherwise indicated; retrieved 2025-04-26; reference date 2025-01-01.
-- listed under Rhein-Ruhr Area, total population 10,900,000 (Consolidated Urban Area)
["Cologne"] = {container = "North Rhine-Westphalia"},
["Köln"] = {alias_of = "Cologne", display = true},
["Düsseldorf"] = {container = "North Rhine-Westphalia"},
["Dusseldorf"] = {alias_of = "Düsseldorf", display = true},
["Dortmund"] = {container = "North Rhine-Westphalia"},
["Essen"] = {container = "North Rhine-Westphalia"},
["Duisberg"] = {container = "North Rhine-Westphalia"},
["Berlin"] = {}, -- 4,700,000
["Frankfurt"] = {container = "Hesse"}, -- 3,225,000
["Frankfurt am Main"] = {alias_of = "Frankfurt"}, -- not a display alias as it's longer
["Hamburg"] = {}, -- 2,900,000
["Munich"] = {container = "Bavaria"}, -- 2,300,000
["Stuttgart"] = {container = "Baden-Württemberg"}, -- 2,300,000
["Mannheim"] = {container = "Baden-Württemberg"}, -- 1,550,000
["Nuremberg"] = {container = "Bavaria"}, -- 1,120,000
["Hanover"] = {"Lower Saxony"}, -- 1,090,000
["Bielefeld"] = {container = "North Rhine-Westphalia"}, -- 1,080,000
["Leipzig"] = {container = "Saxony"}, -- 1,080,000
["Aachen"] = {container = "North Rhine-Westphalia"}, -- 1,000,000
["Aix-la-Chapelle"] = {alias_of = "Aachen"}, -- historical; not a display alias
["Bremen"] = {},
}
export.germany_cities_group = {
default_container = "Germany",
canonicalize_key_container = make_canonicalize_key_container(", Germany", "state"),
default_placetype = "city",
data = export.germany_cities,
}
export.india_cities = {
-- This lists the 65 metro areas per Demographia's 2023 estimates, as found in
-- [[w:List_of_million-plus_urban_agglomerations_in_India]]. The last census in India (as of April 2025) was
-- conducted in 2011, and the results are not accurate any more.
["Delhi"] = {container = {key = "Delhi, India", placetype = "union territory"}}, -- 31,190,000
["Mumbai"] = {container = "Maharashtra"}, -- 25,189,000
["Kolkata"] = {container = "West Bengal"}, -- 21,747,000
["Bangalore"] = {container = "Karnataka", wp = "Bengaluru"}, -- 15,257,000
["Bengaluru"] = {alias_of = "Bangalore"},
["Chennai"] = {container = "Tamil Nadu"}, -- 11,570,000
["Hyderabad"] = {container = "Telangana"}, -- 9,797,000
["Ahmedabad"] = {container = "Gujarat"}, -- 8,006,000
["Pune"] = {container = "Maharashtra"}, -- 6,819,000
["Surat"] = {container = "Gujarat"}, -- 6,601,000
["Lucknow"] = {container = "Uttar Pradesh"}, -- 4,661,000
["Jaipur"] = {container = "Rajasthan"}, -- 4,360,000
["Kanpur"] = {container = "Uttar Pradesh"}, -- 4,350,000
["Indore"] = {container = "Madhya Pradesh"}, -- 3,765,000
["Nagpur"] = {container = "Maharashtra"}, -- 3,493,000
["Patna"] = {container = "Bihar"}, -- 3,331,000
["Varanasi"] = {container = "Uttar Pradesh"}, -- 3,229,000
["Kozhikode"] = {container = "Kerala"}, -- 3,049,000
["Thiruvananthapuram"] = {container = "Kerala"}, -- 2,851,000
["Agra"] = {container = "Uttar Pradesh"}, -- 2,737,000
["Bhopal"] = {container = "Madhya Pradesh"}, -- 2,562,000
["Coimbatore"] = {container = "Tamil Nadu"}, -- 2,551,000
["Allahabad"] = {container = "Uttar Pradesh", wp = "Prayagraj"}, -- 2,438,000
["Prayagraj"] = {alias_of = "Allahabad"},
["Kochi"] = {container = "Kerala"}, -- 2,381,000
["Ludhiana"] = {container = "Punjab"}, -- 2,205,000
["Vadodara"] = {container = "Gujarat"}, -- 2,182,000
["Chandigarh"] = {container = {key = "Chandigarh, India", placetype = "union territory"}}, -- 2,168,000
["Madurai"] = {container = "Tamil Nadu"}, -- 2,048,000
["Meerut"] = {container = "Uttar Pradesh"}, -- 2,011,000
["Visakhapatnam"] = {container = "Andhra Pradesh"}, -- 2,005,000
["Jamshedpur"] = {container = "Jharkhand"}, -- 1,925,000
["Malappuram"] = {container = "Kerala"}, -- 1,868,000
["Nashik"] = {container = "Maharashtra"}, -- 1,810,000
["Asansol"] = {container = "West Bengal"}, -- 1,720,000
["Aligarh"] = {container = "Uttar Pradesh"}, -- 1,660,000
["Ranchi"] = {container = "Jharkhand"}, -- 1,638,000
["Thrissur"] = {container = "Kerala"}, -- 1,578,000
["Kollam"] = {container = "Kerala"}, -- 1,576,000
["Jabalpur"] = {container = "Madhya Pradesh"}, -- 1,533,000
["Dhanbad"] = {container = "Jharkhand"}, -- 1,503,000
["Jodhpur"] = {container = "Rajasthan"}, -- 1,497,000
["Aurangabad"] = {container = "Maharashtra"}, -- 1,490,000
["Chhatrapati Sambhajinagar"] = {alias_of = "Aurangabad"},
["Rajkot"] = {container = "Gujarat"}, -- 1,487,000
["Gwalior"] = {container = "Madhya Pradesh"}, -- 1,477,000
["Raipur"] = {container = "Chhattisgarh"}, -- 1,429,000
["Gorakhpur"] = {container = "Uttar Pradesh"}, -- 1,410,000
["Kannur"] = {container = "Kerala"}, -- 1,360,000
["Bareilly"] = {container = "Uttar Pradesh"}, -- 1,355,000
["Guwahati"] = {container = "Assam"}, -- 1,355,000
["Moradabad"] = {container = "Uttar Pradesh"}, -- 1,345,000
["Amritsar"] = {container = "Punjab"}, -- 1,313,000
["Mysore"] = {container = "Karnataka"}, -- 1,296,000
["Bhilai"] = {container = "Chhattisgarh"}, -- 1,293,000
["Durg-Bhilainagar"] = {alias_of = "Bhilai"},
["Durg-Bhilai"] = {alias_of = "Bhilai"},
["Durg"] = {alias_of = "Bhilai"},
["Bhilainagar"] = {alias_of = "Bhilai"},
["Vijayawada"] = {container = "Andhra Pradesh"}, -- 1,232,000
["Srinagar"] = {container = {key = "Jammu and Kashmir, India", placetype = "union territory"}}, -- 1,212,000
["Salem"] = {container = "Tamil Nadu", wp = "%l, %c"}, -- 1,189,000
["Kota"] = {container = "Rajasthan"}, -- 1,172,000
["Jalandhar"] = {container = "Punjab"}, -- 1,165,000
["Saharanpur"] = {container = "Uttar Pradesh"}, -- 1,152,000
["Dehradun"] = {container = "Uttarakhand"}, -- 1,136,000
["Tiruchirappalli"] = {container = "Tamil Nadu"}, -- 1,131,000
["Bhubaneswar"] = {container = "Odisha"}, -- 1,112,000
["Jammu"] = {container = {key = "Jammu and Kashmir, India", placetype = "union territory"}}, -- 1,103,000
["Solapur"] = {container = "Maharashtra"}, -- 1,082,000
["Hubli-Dharwad"] = {container = "Karnataka", wp = "Hubli–Dharwad"}, -- 1,062,000; wp with en dash
["Hubli"] = {alias_of = "Hubli-Dharwad"},
["Dharwad"] = {alias_of = "Hubli-Dharwad"},
["Puducherry"] = {container = {key = "Puducherry, India", placetype = "union territory"}}, -- 1,024,000
["Pondicherry"] = {alias_of = "Puducherry", display = true},
-- satellite/secondary cities of metro area (none in citypopulation.de)
["Ghaziabad"] = {container = "Uttar Pradesh"}, -- 1,729,000 city, 2,358,525 urban agglomeration per 2011 census; 3,406,061 2025 estimate from official website; part of Delhi metro area
["Faridabad"] = {container = "Haryana"}, -- 1,414,050 city per 2011 census; part of Delhi metro area
["Thane"] = {container = "Maharashtra"}, -- 1,841,488 city per 2011 census; part of Mumbai metro area
["Kalyan-Dombivli"] = {container = "Maharashtra"}, -- 1,246,381 city per 2011 census; part of Mumbai metro area
["Kalyan-Dombivali"] = {alias_of = "Kalyan-Dombivli", display = true},
["Kalyan"] = {alias_of = "Kalyan-Dombivli"},
["Dombivli"] = {alias_of = "Kalyan-Dombivli"},
["Dombivali"] = {alias_of = "Kalyan-Dombivli"},
["Vasai-Virar"] = {container = "Maharashtra"}, -- 1,221,233 city per 2011 census; part of Mumbai metro area
["Vasai"] = {alias_of = "Vasai-Virar"},
["Virar"] = {alias_of = "Vasai-Virar"},
["Navi Mumbai"] = {container = "Maharashtra"}, -- 1,120,547 city per 2011 census; part of Mumbai metro area
["Howrah"] = {container = "West Bengal"}, -- 1,077,075 city ("metropolis"), 2,811,344 "metro" per 2011 census; part of Kolkata metro area
["Pimpri-Chinchwad"] = {container = "Maharashtra"}, -- 1,727,692 per 2011 census; part of Pune metro area
["Pimpri Chinchwad"] = {alias_of = "Pimpri-Chinchwad", display = true},
}
export.india_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(", India", "state"),
default_placetype = "city",
data = export.india_cities,
}
export.indonesia_cities = {
-- cities where the city proper has more than 1,000,000 people as of mid-2023 estimate
["Jakarta"] = {container = "Special Capital Region of Jakarta", divs = {
{type = "subdistricts", container_parent_type = false},
}},
["Surabaya"] = {container = "East Java"},
["Bekasi"] = {container = "West Java"}, -- part of Jakarta metro area
["Bandung"] = {container = "West Java"},
["Medan"] = {container = "North Sumatra"},
["Depok"] = {container = "West Java"}, -- part of Jakarta metro area
["Tangerang"] = {container = "Banten"}, -- part of Jakarta metro area
["Palembang"] = {container = "South Sumatra"},
["Semarang"] = {container = "Central Java"},
["Makassar"] = {container = "South Sulawesi"},
["South Tangerang"] = {container = "Banten"}, -- part of Jakarta metro area
["Batam"] = {container = "Riau Islands"},
["Bogor"] = {container = "West Java"}, -- part of Jakarta metro area
["Pekanbaru"] = {container = "Riau"},
["Bandar Lampung"] = {container = "Lampung"},
-- other metro areas over 1,000,000 people
["Padang"] = {container = "West Sumatra"},
["Samarinda"] = {container = "East Kalimantan"},
["Malang"] = {container = "East Java"},
["Yogyakarta"] = {container = "Special Region of Yogyakarta"},
["Denpasar"] = {container = "Bali"},
["Cirebon"] = {container = "West Java"},
["Surakarta"] = {container = "Central Java"},
["Banjarmasin"] = {container = "South Kalimantan"},
["Tasikmalaya"] = {container = "West Java"},
}
export.indonesia_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(", Indonesia", "province"),
default_placetype = "city",
data = export.indonesia_cities,
}
export.italy_cities = {
-- Data per [[w:List_of_metropolitan_areas_of_Italy]]. There are several lists given; the most recent one, used
-- here, only gives estimates as of Jan 1, 2014.
["Milan"] = {container = "Lombardy"}, -- 6,623,798
["Naples"] = {container = "Campania"}, -- 5,294,546
["Rome"] = {container = "Lazio"}, -- 4,447,881
["Turin"] = {container = "Piedmont"}, -- 1,865,284
["Venice"] = {container = "Veneto"}, -- 1,645,900
["Florence"] = {container = "Tuscany"}, -- 1,485,030
["Bari"] = {container = "Apulia"}, -- 1,257,459
["Palermo"] = {container = "Sicily"}, -- 1,183,084
-- include a few just below 1,000,000 metro area that may be above it by now (depending on the definition).
["Catania"] = {container = "Sicily"}, -- 988,240
["Brescia"] = {container = "Lombardy"}, -- 924,090
["Genoa"] = {container = "Liguria"}, -- 861,318
}
export.italy_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(", Italy", "region"),
default_placetype = "city",
data = export.italy_cities,
}
export.japan_cities = {
-- Population figures from [[w:List of cities in Japan]]. Metro areas from
-- [[w:List of metropolitan areas in Japan]].
["Tokyo"] = {keydesc = "[[Tokyo]] Metropolis, the [[capital city]] and a [[prefecture]] of [[Japan]] (which is a country in [[Asia]])",
placetype = {"city", "prefecture"},
divs = {
{type = "special wards", container_parent_type = false},
{type = "cities", prep = "in"},
},
},
["Yokohama"] = {container = "Kanagawa"}, -- 3,697,894
["Osaka"] = {container = "Osaka"}, -- 2,668,586
["Nagoya"] = {container = "Aichi"}, -- 2,283,289
-- FIXME, Hokkaido is handled specially.
["Sapporo"] = {container = "Hokkaido"}, -- 1,918,096
["Fukuoka"] = {container = "Fukuoka"}, -- 1,581,527
["Kobe"] = {container = "Hyōgo"}, -- 1,530,847
["Kyoto"] = {container = "Kyoto"}, -- 1,474,570
["Kawasaki"] = {container = "Kanagawa", wp = "%l, Kanagawa"}, -- 1,373,630
["Saitama"] = {container = "Saitama", wp = "%l (city)", commonscat = "%l, %c"}, -- 1,192,418
["Hiroshima"] = {container = "Hiroshima"}, -- 1,163,806
["Sendai"] = {container = "Miyagi"}, -- 1,029,552
-- the remaining cities are considered "central cities" in a 1,000,000+ metro area
-- (sometimes there is more than one central city in the area).
["Kitakyushu"] = {container = "Fukuoka"}, -- 986,998
["Chiba"] = {container = "Chiba", wp = "%l (city)", commonscat = "%l, %c"}, -- 938,695
["Sakai"] = {container = "Osaka"}, -- 835,333
["Niigata"] = {container = "Niigata", wp = "%l (city)", commonscat = "%l, %c"}, -- 813,053
["Hamamatsu"] = {container = "Shizuoka"}, -- 811,431
["Shizuoka"] = {container = "Shizuoka", wp = "%l (city)", commonscat = "%l, %c"}, -- 710,944
["Sagamihara"] = {container = "Kanagawa"}, -- 706,342
["Okayama"] = {container = "Okayama"}, -- 701,293
["Kumamoto"] = {container = "Kumamoto"}, -- 670,348
["Kagoshima"] = {container = "Kagoshima"}, -- 605,196
-- skipped 6 cities (Funabashi, Hachiōji, Kawaguchi, Himeji, Matsuyama, Higashiōsaka)
-- with population in the range 509k - 587k because not central cities in any
-- 1,000,000+ metro area.
["Utsunomiya"] = {container = "Tochigi"}, -- 507,833
}
export.japan_cities_group = {
default_container = "Japan",
canonicalize_key_container = make_canonicalize_key_container(" Prefecture, Japan", "prefecture"),
default_placetype = "city",
data = export.japan_cities,
}
export.mexico_cities = {
["Mexico City"] = {}, -- its own state
["Monterrey"] = {container = "Nuevo León"},
["Guadalajara"] = {container = "Jalisco"},
["Puebla"] = {container = "Puebla", wp = "%l (city)"},
["Toluca"] = {container = "State of Mexico"},
["Tijuana"] = {container = "Baja California"},
-- Include the state in the category for León due to possible confusion with León, Spain.
["León, Guanajuato"] = {container = "Guanajuato", wp = "%l, %c"},
["León"] = {alias_of = "León, Guanajuato"},
["Leon"] = {alias_of = "León, Guanajuato", display = true},
["Querétaro"] = {container = "Querétaro", wp = "%l (city)"},
["Queretaro"] = {alias_of = "Querétaro", display = true},
["Ciudad Juárez"] = {container = "Chihuahua"},
["Juárez"] = {alias_of = "Ciudad Juárez"},
["Juarez"] = {alias_of = "Ciudad Juárez", display = "Juárez"},
["Torreón"] = {container = "Coahuila"},
["Torreon"] = {alias_of = "Torreón", display = true},
-- Include the state in the category for Mérida due to possible confusion with Mérida, Spain or
-- Mérida, Venezuela.
["Mérida, Yucatán"] = {container = "Yucatán", wp = "%l, %c"},
["Mérida"] = {alias_of = "Mérida, Yucatán"},
["Merida"] = {alias_of = "Mérida, Yucatán", display = true},
["San Luis Potosí"] = {container = "San Luis Potosí", wp = "%l (city)"},
["San Luis Potosi"] = {alias_of = "San Luis Potosí", display = true},
["Aguascalientes"] = {container = "Aguascalientes", wp = "%l (city)"},
["Mexicali"] = {container = "Baja California"},
}
export.mexico_cities_group = {
default_container = "Mexico",
canonicalize_key_container = make_canonicalize_key_container(", Mexico", "state"),
default_placetype = "city",
data = export.mexico_cities,
}
export.nigeria_cities = {
-- Figures from citypopulation.de unless otherwise indicated; retrieved 2025-04-26; reference date 2025-01-01.
["Lagos"] = {container = "Lagos"}, -- 21,300,000 (unindicated; population of low reliability)
["Kano"] = {container = "Kano", wp = "%l (city)"}, -- 5,350,000 (unindicated; population of low reliability)
["Ibadan"] = {container = "Oyo"}, -- 3,400,000 (unindicated; population of low reliability)
["Abuja"] = {container = {key = "Federal Capital Territory, Nigeria", placetype = "federal territory"}}, -- 3,050,000 (unindicated; population of low reliability)
["Port Harcourt"] = {container = "Rivers"}, -- 2,250,000 (unindicated; population of low reliability)
["Kaduna"] = {container = "Kaduna"}, -- 1,980,000 (unindicated; population of low reliability)
["Benin City"] = {container = "Edo"}, -- 1,790,000 (unindicated; population of low reliability)
["Aba"] = {container = "Abia", wp = "%l, Nigeria"}, -- 1,280,000 (unindicated; population of low reliability)
["Onitsha"] = {container = "Anambra"}, -- 1,230,000 (unindicated; population of low reliability)
["Maiduguri"] = {container = "Borno"}, -- 1,190,000 (unindicated; population of low reliability)
["Ilorin"] = {container = "Kwara"}, -- 1,160,000 (unindicated; population of low reliability)
["Sokoto"] = {container = "Sokoto", wp = "%l (city)"}, -- 1,140,000 (unindicated; population of low reliability)
["Jos"] = {container = "Plateau"}, -- 1,110,000 (unindicated; population of low reliability)
["Zaria"] = {container = "Kaduna"}, -- 1,050,000 (unindicated; population of low reliability)
["Enugu"] = {container = "Enugu", wp = "%l (city)"}, -- 1,010,000 (unindicated; population of low reliability)
}
export.nigeria_cities_group = {
default_container = "Nigeria",
canonicalize_key_container = make_canonicalize_key_container(" State, Nigeria", "state"),
default_placetype = "city",
data = export.nigeria_cities,
}
export.pakistan_cities = {
-- Figures from citypopulation.de; retrieved 2025-04-26; reference date 2025-01-01.
["Karachi"] = {container = "Sindh"}, -- 21,000,000 (Consolidated Urban Area)
["Lahore"] = {container = "Punjab"}, -- 14,600,000 (Consolidated Urban Area)
["Rawalpindi"] = {container = "Punjab"}, -- 5,600,000 (Consolidated Urban Area; including Islamabad)
["Islamabad"] = {container = {key = "Islamabad Capital Territory, Pakistan", placetype = "federal territory"}}, -- 5,600,000 (Consolidated Urban Area; including Rawalpindi)
["Faisalabad"] = {container = "Punjab"}, -- 4,125,000 (Consolidated Urban Area)
["Gujranwala"] = {container = "Punjab"}, -- 3,450,000 (Consolidated Urban Area)
-- there is also Hyderabad in India (very confusing)
["Hyderabad, Pakistan"] = {container = "Sindh", wp = "%l, %c"}, -- 2,475,000 (Consolidated Urban Area)
["Hyderabad"] = {alias_of = "Hyderabad, Pakistan"},
["Multan"] = {container = "Punjab"}, -- 2,425,000 (Consolidated Urban Area)
["Peshawar"] = {container = "Khyber Pakhtunkhwa"}, -- 2,150,000 (Consolidated Urban Area)
["Quetta"] = {container = "Balochistan"}, -- 1,720,000 (Urban Area)
["Sargodha"] = {container = "Punjab"}, -- 1,080,000 (Urban Area)
["Sialkot"] = {container = "Punjab"}, -- 1,050,000 (Consolidated Urban Area)
}
export.pakistan_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(", Pakistan", "province"),
default_placetype = "city",
data = export.pakistan_cities,
}
export.philippines_cities = {
-- Skipped some cities in Metro Manila (Taguig, Pasig) which don't have districts.
-- Other cities outside Metro Manila skipped as not central city in their urban area.
["Quezon City"] = {container = {key = "Metro Manila, Philippines", placetype = "region"}},
-- Don't display-canonicalize Foo to Foo City as it may make the display weird.
["Quezon"] = {alias_of = "Quezon City"},
["Manila"] = {container = {key = "Metro Manila, Philippines", placetype = "region"}},
["Davao City"] = {container = "Davao del Sur"},
["Davao"] = {alias_of = "Davao City"},
["Caloocan"] = {container = {key = "Metro Manila, Philippines", placetype = "region"}},
["Zamboanga City"] = {container = "Zamboanga del Sur"},
["Zamboanga"] = {alias_of = "Zamboanga City"},
["Cebu City"] = {container = "Cebu"},
["Cebu"] = {alias_of = "Cebu City"},
["Antipolo"] = {container = "Rizal"},
["Cagayan de Oro"] = {container = "Misamis Oriental"},
["Dasmariñas"] = {container = "Cavite"},
["Dasmarinas"] = {alias_of = "Dasmariñas", display = true},
["General Santos"] = {container = "South Cotabato"},
["San Jose del Monte"] = {container = "Bulacan"},
["Bacolod"] = {container = "Negros Occidental"},
["Calamba"] = {container = "Laguna", wp = "%l, %c"},
["Angeles"] = {container = "Pampanga", wp = "Angeles City"},
["Angeles City"] = {alias_of = "Angeles"},
["Iloilo City"] = {container = "Iloilo"},
["Iloilo"] = {alias_of = "Iloilo City"},
}
export.philippines_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(", Philippines", "province"),
default_placetype = "city",
data = export.philippines_cities,
}
export.russia_cities = {
-- Figures from citypopulation.de; retrieved 2025-04-26; reference date 2025-01-01.
["Moscow"] = {}, -- 18,800,000 (Agglomeration)
["Saint Petersburg"] = {}, -- 6,350,000 (Agglomeration)
["Novosibirsk"] = {container = "Novosibirsk Oblast"}, -- 1,820,000 (Agglomeration)
["Yekaterinburg"] = {container = "Sverdlovsk Oblast"}, -- 1,810,000 (Agglomeration)
["Nizhny Novgorod"] = {container = "Nizhny Novgorod Oblast"}, -- 1,620,000 (Agglomeration)
["Kazan"] = {container = {key = "Tatarstan, Russia", placetype = "republic"}}, -- 1,560,000 (Agglomeration)
["Chelyabinsk"] = {container = "Chelyabinsk Oblast"}, -- 1,430,000 (Agglomeration)
["Rostov-on-Don"] = {container = "Rostov Oblast"}, -- 1,390,000 (Agglomeration)
["Rostov-na-Donu"] = {alias_of = "Rostov-on-Don", display = true},
["Krasnodar"] = {container = {key = "Krasnodar Krai, Russia", placetype = "krai"}}, -- 1,370,000 (Agglomeration)
["Samara"] = {container = "Samara Oblast"}, -- 1,350,000 (Agglomeration)
["Krasnoyarsk"] = {container = {key = "Krasnoyarsk Krai, Russia", placetype = "krai"}}, -- 1,270,000 (Agglomeration)
["Ufa"] = {container = {key = "Bashkortostan, Russia", placetype = "republic"}}, -- 1,230,000 (Agglomeration)
["Saratov"] = {container = "Saratov Oblast"}, -- 1,170,000 (Agglomeration)
["Omsk"] = {container = "Omsk Oblast"}, -- 1,140,000 (Agglomeration)
["Voronezh"] = {container = "Voronezh Oblast"}, -- 1,130,000 (Agglomeration)
["Volgograd"] = {container = "Volgograd Oblast"}, -- 1,080,000 (Agglomeration)
["Perm"] = {container = {key = "Perm Krai, Russia", placetype = "krai"}, wp = "%l, Russia"}, -- 1,070,000 (Agglomeration)
}
export.russia_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(", Russia", "oblast"),
default_container = "Russia",
default_placetype = "city",
data = export.russia_cities,
}
export.saudi_arabia_cities = {
-- Figures for the first five from [[w:List of cities and towns in Saudi Arabia]] as of 2022. Unclear if these are
-- metro, urban or city proper figures.
["Riyadh"] = {container = "Riyadh"}, -- 7,000,100; 7,700,000 per citypopulation.de 2025-01-01 (Agglomeration)
["Jeddah"] = {container = "Mecca"}, -- 3,751,917; 3,950,000 per citypopulation.de 2025-01-01 (Agglomeration)
["Jedda"] = {alias_of = "Jeddah", display = true},
["Jiddah"] = {alias_of = "Jeddah", display = true},
["Jidda"] = {alias_of = "Jeddah", display = true},
["Dammam"] = {container = "Eastern"}, -- 2,638,166; 2,925,000 per citypopulation.de 2025-01-01 (Agglomeration)
["Mecca"] = {container = "Mecca"}, -- 2,385,509; 2,675,000 per citypopulation.de 2025-01-01 (Agglomeration)
["Makkah"] = {alias_of = "Mecca", display = true},
["Medina"] = {container = "Medina"}, -- 1,477,023; 1,530,000 per citypopulation.de 2025-01-01 (City)
["Hofuf"] = {container = "Eastern"}, -- 1,060,000 per citypopulation.de 2025-01-01 (Agglomeration)
["Khamis Mushait"] = {container = "Aseer"}, -- 1,030,000 per citypopulation.de 2025-01-01 (Agglomeration)
["Khamis Mushayt"] = {alias_of = "Khamis Mushait", display = true},
}
export.saudi_arabia_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(" Province, Saudi Arabia", "province"),
default_placetype = "city",
data = export.saudi_arabia_cities,
}
export.south_korea_cities = {
-- All cities listed are not associated with any county.
["Seoul"] = {},
["Busan"] = {},
["Incheon"] = {},
["Daegu"] = {},
["Daejeon"] = {},
["Gwangju"] = {},
["Ulsan"] = {},
}
export.south_korea_cities_group = {
default_container = "South Korea",
canonicalize_key_container = make_canonicalize_key_container(" County, South Korea", "province"),
default_placetype = "city",
data = export.south_korea_cities,
}
export.spain_cities = {
["Madrid"] = {container = "Community of Madrid"},
["Barcelona"] = {container = "Catalonia"},
["Valencia"] = {container = "Valencia"},
["Seville"] = {container = "Andalusia"},
["Bilbao"] = {container = "Basque Country"},
}
export.spain_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(", Spain", "autonomous community"),
default_placetype = "city",
data = export.spain_cities,
}
export.taiwan_cities = {
["New Taipei City"] = {},
["New Taipei"] = {alias_of = "New Taipei City", display = true},
["Taichung"] = {},
["Kaohsiung"] = {wp = "%l, Taiwan"},
["Taipei"] = {},
["Taoyuan"] = {},
["Tainan"] = {},
-- these last three are not special municipalities
["Chiayi"] = {placetype = "city"},
["Hsinchu"] = {placetype = "city"},
["Keelung"] = {placetype = "city"},
}
export.taiwan_cities_group = {
placename_to_key = false, -- don't add ", Taiwan" to make the key
canonicalize_key_container = make_canonicalize_key_container(", Taiwan", "county"),
default_container = "Taiwan",
default_placetype = {"special municipality", "municipality", "city"},
default_is_city = true,
default_divs = {"districts"},
data = export.taiwan_cities,
}
-- NOTE: It's OK to mix cities from different constituent countries; as long as the immediate container is correct,
-- everything else will be figured out.
export.united_kingdom_cities = {
["London"] = {container = "Greater London"},
["Manchester"] = {container = "Greater Manchester"},
["Birmingham"] = {container = "West Midlands"},
["Liverpool"] = {container = "Merseyside"},
["Glasgow"] = {container = {key = "City of Glasgow, Scotland", placetype = "council area"}},
["Leeds"] = {container = "West Yorkshire"},
["Newcastle upon Tyne"] = {container = "Tyne and Wear"},
["Newcastle"] = {alias_of = "Newcastle upon Tyne"},
["Bristol"] = {container = {key = "England", placetype = "constituent country"}},
["Cardiff"] = {container = {key = "Wales", placetype = "constituent country"}},
["Portsmouth"] = {container = "Hampshire"},
["Edinburgh"] = {container = {key = "City of Edinburgh, Scotland", placetype = "council area"}},
-- under 1,000,000 people but principal areas of Wales; requested by [[User:Donnanz]]
["Swansea"] = {container = {key = "Wales", placetype = "constituent country"}},
["Newport"] = {container = {key = "Wales", placetype = "constituent country"}, wp = "Newport, Wales"},
}
export.united_kingdom_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(", England", "county"),
default_placetype = "city",
data = export.united_kingdom_cities,
}
export.united_states_cities = {
-- top 50 CSA's by population, with the top and sometimes 2nd or 3rd city listed
["New York City"] = {container = "New York", wp = "%l", divs = {
{type = "boroughs", container_parent_type = false},
}},
-- Don't display-canonicalize as it may make the display weird (e.g. in the context New York, New York).
["New York"] = {alias_of = "New York City"},
["Newark"] = {container = "New Jersey"},
["Los Angeles"] = {container = "California", wp = "%l"},
["Long Beach"] = {container = "California"},
["Riverside"] = {container = "California"},
["Chicago"] = {container = "Illinois", wp = "%l"},
["Washington, D.C."] = {wp = "%l"},
["Washington, DC"] = {alias_of = "Washington, D.C.", display = true},
["Washington D.C."] = {alias_of = "Washington, D.C.", display = true},
["Washington DC"] = {alias_of = "Washington, D.C.", display = true},
-- Don't display-canonicalize as it may make the display weird (e.g. if the holonym is followed by a District of
-- Columbia holonym).
["Washington"] = {alias_of = "Washington, D.C."},
["Baltimore"] = {container = "Maryland", wp = "%l"},
-- to avoid conflict with San Jose in Costa Rica
["San Jose, California"] = {container = "California"},
["San Jose"] = {alias_of = "San Jose, California"},
["San Francisco"] = {container = "California", wp = "%l"},
["Oakland"] = {container = "California"},
["Boston"] = {container = "Massachusetts", wp = "%l"},
["Providence"] = {container = "Rhode Island"},
["Dallas"] = {container = "Texas", wp = "%l", commonscat = "%l, %c"},
["Fort Worth"] = {container = "Texas"},
["Philadelphia"] = {container = "Pennsylvania", wp = "%l"},
["Houston"] = {container = "Texas", wp = "%l"},
["Miami"] = {container = "Florida", wp = "%l", commonscat = "%l, %c"},
["Atlanta"] = {container = "Georgia", wp = "%l"},
["Detroit"] = {container = "Michigan", wp = "%l"},
["Phoenix"] = {container = "Arizona", wp = "%l", commonscat = "%l, %c"},
["Mesa"] = {container = "Arizona"},
["Seattle"] = {container = "Washington", wp = "%l"},
["Orlando"] = {container = "Florida"},
["Minneapolis"] = {container = "Minnesota", wp = "%l"},
["Cleveland"] = {container = "Ohio", wp = "%l", commonscat = "%l, %c"},
["Denver"] = {container = "Colorado", wp = "%l", commonscat = "%l, %c"},
["San Diego"] = {container = "California", wp = "%l", commonscat = "%l, %c"},
["Portland"] = {container = "Oregon"},
["Tampa"] = {container = "Florida"},
["St. Louis"] = {container = "Missouri", wp = "%l", commonscat = "%l, %c"},
["Saint Louis"] = {alias_of = "St. Louis", display = true},
["Charlotte"] = {container = "North Carolina"},
["Sacramento"] = {container = "California"},
["Pittsburgh"] = {container = "Pennsylvania", wp = "%l"},
["Salt Lake City"] = {container = "Utah", wp = "%l"},
["San Antonio"] = {container = "Texas", wp = "%l", commonscat = "%l, %c"},
["Columbus"] = {container = "Ohio"},
["Kansas City"] = {container = "Missouri", wp = "%l metropolitan area", commonscat = "%l, %c"},
["Indianapolis"] = {container = "Indiana", wp = "%l"},
["Las Vegas"] = {container = "Nevada", wp = "%l"},
["Cincinnati"] = {container = "Ohio", wp = "%l", commonscat = "%l, %c"},
["Austin"] = {container = "Texas"},
["Milwaukee"] = {container = "Wisconsin", wp = "%l", commonscat = "%l, %c"},
["Raleigh"] = {container = "North Carolina"},
["Nashville"] = {container = "Tennessee"},
["Virginia Beach"] = {container = "Virginia"},
["Norfolk"] = {container = "Virginia"},
["Greensboro"] = {container = "North Carolina"},
["Winston-Salem"] = {container = "North Carolina"},
["Jacksonville"] = {container = "Florida"},
["New Orleans"] = {container = "Louisiana", wp = "%l"},
["Louisville"] = {container = "Kentucky"},
["Greenville"] = {container = "South Carolina"},
["Hartford"] = {container = "Connecticut"},
["Oklahoma City"] = {container = "Oklahoma", wp = "%l"},
["Grand Rapids"] = {container = "Michigan"},
["Memphis"] = {container = "Tennessee"},
["Birmingham, Alabama"] = {container = "Alabama"},
["Birmingham"] = {alias_of = "Birmingham, Alabama"},
["Fresno"] = {container = "California"},
["Richmond"] = {container = "Virginia"},
["Harrisburg"] = {container = "Pennsylvania"},
-- any major city of top 50 MSA's that's missed by previous
["Buffalo"] = {container = "New York"},
-- any of the top 50 city by city population that's missed by previous
["El Paso"] = {container = "Texas"},
["Albuquerque"] = {container = "New Mexico"},
["Tucson"] = {container = "Arizona"},
["Colorado Springs"] = {container = "Colorado"},
["Omaha"] = {container = "Nebraska"},
["Tulsa"] = {container = "Oklahoma"},
-- skip Arlington, Texas; too obscure and likely to be interpreted as Arlington, Virginia
}
export.united_states_cities_group = {
default_container = "United States",
canonicalize_key_container = make_canonicalize_key_container(", USA", "state"),
default_placetype = "city",
default_wp = "%l, %c",
data = export.united_states_cities,
}
export.new_york_boroughs = {
["Bronx"] = {the = true, wp = "The Bronx"},
["Brooklyn"] = {},
["Manhattan"] = {},
["Queens"] = {},
["Staten Island"] = {},
}
export.new_york_boroughs_group = {
default_container = {key = "New York City", placetype = "city"},
default_placetype = "borough",
default_is_city = true,
data = export.new_york_boroughs,
}
export.vietnam_cities = {
-- Figures from citypopulation.de (retrieved 2025-04-26; reference date 2025-01-01) unless otherwise indicated.
["Ho Chi Minh City"] = {}, -- 14,300,000 (Agglomeration; inclunding Bien Hoa)
["Saigon"] = {alias_of = "Ho Chi Minh City"},
["Hanoi"] = {}, -- 7,350,000 (Agglomeration)
["Da Nang"] = {}, -- 1,500,000 (Agglomeration)
["Danang"] = {alias_of = "Da Nang", display = true},
["Haiphong"] = {}, -- 1,450,000 (Agglomeration)
["Hai Phong"] = {alias_of = "Haiphong", display = true},
-- This is the one entry in this list that is not a province-level municipality; instead it's a "provincial city"
-- meaning it is directly under its province as opposed to being contained in a district.
["Bien Hoa"] = {placetype = "city", container = "Đồng Nai", wp = "Biên Hòa"}, -- 1,272,235 (2022 city population per Wikipedia)
["Biên Hòa"] = {alias_of = "Bien Hoa", display = true},
["Biên Hoà"] = {alias_of = "Bien Hoa", display = true},
-- These two not in citypopulation.de because the urban population may be slightly under 1,000,000, but they are
-- both province-level municipalities and close to the 1,000,000 mark.
["Can Tho"] = {wp = "Cần Thơ"}, -- 1,456,000 municipality (2019 census), 994,704 urban (2022 General Statistics Office of Vietnam estimate); capital [[Ninh Kiều district]]
["Cần Thơ"] = {alias_of = "Can Tho", display = true},
["Hue"] = {wp = "Huế"}, -- 1,257,000 municipality (2019 census), 840,000 urban (2022 General Statistics Office of Vietnam estimate); -- capital [[Thuận Hóa district]]
["Huế"] = {alias_of = "Hue", display = true},
}
export.vietnam_cities_group = {
placename_to_key = false, -- don't add ", Vietnam" to make the key
default_container = "Vietnam",
canonicalize_key_container = make_canonicalize_key_container(" Province, Vietnam", "province"),
-- Most of the cities listed are province-level municipalities in addition, which contain a certain amount of
-- rural territory surrounding the city, but not enough to separate the municipality from the city as distinct
-- known locations.
default_placetype = {"municipality", "city"},
default_is_city = true,
-- There may not be enough districts to subcategorize like this.
-- default_divs = "districts",
data = export.vietnam_cities,
}
export.misc_cities = {
------------------ Africa -------------------
-- Sorted by country and then within the country, by decreasing population; figures from citypopulation.de
-- (retrieved 2025-04-26; reference date 2025-01-01) unless otherwise indicated; combined with data from
-- [[w:List of urban areas in Africa by population]].
["Algiers"] = {container = "Algeria"}, -- 4,325,000 (Consolidated Urban Area)
["Oran"] = {container = "Algeria"}, -- 1,640,000 (Consolidated Urban Area)
["Luanda"] = {container = "Angola"}, -- 9,650,000 (Urban Area)
["Benguela"] = {container = "Angola"}, -- 1,420,000 (Urban Area)
["Cotonou"] = {container = "Benin"}, -- 2,150,000 (Agglomeration)
["Ouagadougou"] = {container = "Burkina Faso"}, -- 3,425,000 (Agglomeration)
["Bobo-Dioulasso"] = {container = "Burkina Faso"}, -- 1,100,000 (Agglomeration)
["Bujumbura"] = {container = "Burundi"}, -- 1,143,202 (Urban Area 2023 per PopulationStat, cited in Wikipedia)
["Yaoundé"] = {container = "Cameroon"}, -- 3,975,000 (City)
["Yaounde"] = {alias_of = "Yaoundé", display = true},
["Douala"] = {container = "Cameroon"}, -- 3,900,000 (City)
["Bangui"] = {container = "Central African Republic"}, -- 1,680,000 (Agglomeration)
["N'Djamena"] = {container = "Chad"}, -- 1,950,000 (City)
["Ndjamena"] = {alias_of = "N'Djamena", display = true},
["Kinshasa"] = {container = "Democratic Republic of the Congo"}, -- 16,300,000 (City; population of low reliability)
["Lubumbashi"] = {container = "Democratic Republic of the Congo"}, -- 2,875,000 (City; population of low reliability)
["Mbuji-Mayi"] = {container = "Democratic Republic of the Congo"}, -- 2,500,000 (City; population of low reliability)
["Kananga"] = {container = "Democratic Republic of the Congo"}, -- 1,370,000 (City; population of low reliability)
["Kisangani"] = {container = "Democratic Republic of the Congo"}, -- 1,300,000 (City; population of low reliability)
["Bukavu"] = {container = "Democratic Republic of the Congo"}, -- 1,100,000 (City; population of low reliability)
["Goma"] = {container = "Democratic Republic of the Congo"}, -- 1,010,000 (City; population of low reliability)
["Tshikapa"] = {container = "Democratic Republic of the Congo"}, -- 1,020,468 (2023 Wikipedia [[w:List of cities with over one million inhabitants]] from populationstat.com; not in citypopulation.de)
["Cairo"] = {container = "Egypt"}, -- 22,800,000 (Agglomeration, including Giza and Subhra El Kheima)
["Alexandria"] = {container = "Egypt"}, -- 6,250,000 (Agglomeration)
["Giza"] = {container = "Egypt"}, -- 4,458,135 (2023 from citypopulation.de)
["Shubra El Kheima"] = {container = "Egypt"}, -- 1,240,239 (2021 from citypopulation.de)
["Asmara"] = {container = "Eritrea"}, -- 1,090,000 (City; population of low reliability)
["Asmera"] = {alias_of = "Asmara", display = true},
["Addis Ababa"] = {container = "Ethiopia"}, -- 4,825,000 (Agglomeration)
["Banjul"] = {container = "Gambia"}, -- 1,170,000 (Agglomeration)
["Accra"] = {container = "Ghana"}, -- 6,800,000 (Agglomeration)
["Kumasi"] = {container = "Ghana"}, -- 2,900,000 (Agglomeration)
["Conakry"] = {container = "Guinea"}, -- 2,975,000 (Consolidated Urban Area)
["Abidjan"] = {container = "Ivory Coast"}, -- 7,050,000 (Agglomeration)
["Nairobi"] = {container = "Kenya"}, -- 6,900,000 (unindicated)
["Mombasa"] = {container = "Kenya"}, -- 1,370,000 (City)
["Monrovia"] = {container = "Liberia"}, -- 1,940,000 (Urban Area)
["Tripoli"] = {container = "Libya", wp = "%l, %c"}, -- 1,870,000 (unindicated)
["Antananarivo"] = {container = "Madagascar"}, -- 3,150,000 (Agglomeration)
["Lilongwe"] = {container = "Malawi"}, -- 1,210,000 (City)
["Bamako"] = {container = "Mali"}, -- 5,700,000 (Agglomeration)
["Nouakchott"] = {container = "Mauritania"}, -- 1,500,000 (City)
["Casablanca"] = {container = {key = "Casablanca-Settat, Morocco", placetype = "region"}}, -- 4,450,000 (Municipality (urban population))
["Rabat"] = {container = {key = "Rabat-Sale-Kenitra, Morocco", placetype = "region"}}, -- 2,125,000 (Municipality (urban population))
["Tangier"] = {container = {key = "Tangier-Tetouan-Al Hoceima, Morocco", placetype = "region"}}, -- 1,410,000 (Municipality (urban population))
["Tanger"] = {alias_of = "Tangier", display = true},
["Tangiers"] = {alias_of = "Tangier", display = true},
["Fez"] = {container = {key = "Fez-Meknes, Morocco", placetype = "region"}, wp = "%l, Morocco"}, -- 1,310,000 (Municipality (urban population))
["Fes"] = {alias_of = "Fez", display = true},
["Fès"] = {alias_of = "Fez", display = true},
["Agadir"] = {container = {key = "Souss-Massa, Morocco", placetype = "region"}}, -- 1,270,000 (Municipality (urban population))
["Marrakesh"] = {container = {key = "Marrakesh-Safi, Morocco", placetype = "region"}}, -- 1,140,000 (Municipality (urban population))
["Marrakech"] = {alias_of = "Marrakesh", display = true},
["Maputo"] = {container = "Mozambique"}, -- 2,575,000 (Agglomeration)
["Niamey"] = {container = "Niger"}, -- 1,530,000 (City)
["Brazzaville"] = {container = "Republic of the Congo"}, -- 2,475,000 (Agglomeration)
["Pointe-Noire"] = {container = "Republic of the Congo"}, -- 1,480,000 (City)
["Kigali"] = {container = "Rwanda"}, -- 1,960,000 (Municipality (urban population))
["Dakar"] = {container = "Senegal"}, -- 4,225,000 (Agglomeration)
["Touba"] = {container = "Senegal"}, -- 1,320,000 (Agglomeration)
["Freetown"] = {container = "Sierra Leone"}, -- 1,420,000 (Agglomeration)
["Mogadishu"] = {container = "Somalia"}, -- 2,250,000 (unindicated; population of low reliability)
["Johannesburg"] = {container = {key = "Gauteng, South Africa", placetype = "province"}}, -- 14,800,000 (Consolidated Urban Area; including Pretoria, Soweto, etc.)
["Cape Town"] = {container = {key = "Western Cape, South Africa", placetype = "province"}}, -- 5,100,000 (Consolidated Urban Area)
["Durban"] = {container = {key = "KwaZulu-Natal, South Africa", placetype = "province"}}, -- 3,900,000 (Consolidated Urban Area)
["Pretoria"] = {container = {key = "Gauteng, South Africa", placetype = "province"}}, -- 2,921,488 (2011 census)
["Port Elizabeth"] = {container = {key = "Eastern Cape, South Africa", placetype = "province"}, wp = "Gqeberha"}, -- 1,200,000 (Consolidated Urban Area)
["Gqeberha"] = {alias_of = "Port Elizabeth"}, -- official name; not a display alias
["Khartoum"] = {container = "Sudan"}, -- 7,200,000 (unindicated; population of low reliability)
["Dar es Salaam"] = {container = "Tanzania"}, -- 6,650,000 (Agglomeration)
["Mwanza"] = {container = "Tanzania"}, -- 1,340,000 (Agglomeration)
["Mwanza City"] = {alias_of = "Mwanza", display = true},
["Arusha"] = {container = "Tanzania"}, -- 1,190,000 (Agglomeration)
["Zanzibar"] = {container = "Tanzania"}, -- 1,030,000 (Agglomeration)
["Lomé"] = {container = "Togo"}, -- 2,625,000 (unindicated)
["Lome"] = {alias_of = "Lomé", display = true},
["Tunis"] = {container = "Tunisia"}, -- 2,725,000 (Municipality (urban population))
["Sousse"] = {container = "Tunisia"}, -- 1,180,000 (Municipality (urban population))
["Soussa"] = {alias_of = "Sousse", display = true},
["Kampala"] = {container = "Uganda"}, -- 4,300,000 (unindicated)
["Lusaka"] = {container = "Zambia"}, -- 3,000,000 (Consolidated Urban Area)
["Harare"] = {container = "Zimbabwe"}, -- 2,675,000 (Agglomeration)
------------------ Asia -------------------
-- sorted by country and then within the country, by decreasing population; figures from citypopulation.de
-- (retrieved 2025-04-26; reference date 2025-01-01) unless otherwise indicated.
["Kabul"] = {container = "Afghanistan"}, -- 5,250,000 (Agglomeration)
["Baku"] = {container = "Azerbaijan"}, -- 3,725,000 (Administrative Area (urban population))
["Manama"] = {container = "Bahrain"}, -- 1,560,000 (unindicated)
["Dhaka"] = {container = {key = "Dhaka Division, Bangladesh", placetype = "division"}}, -- 23,100,000 (Agglomeration)
["Dacca"] = {alias_of = "Dhaka", display = true},
["Chittagong"] = {container = {key = "Chittagong Division, Bangladesh", placetype = "division"}}, -- 5,050,000 (Agglomeration)
["Gazipur"] = {container = {key = "Dhaka Division, Bangladesh", placetype = "division"}}, -- 2,674,697 (City per 2022; countied in citypopulation.de as part of Dhaka metro area)
["Khulna"] = {container = {key = "Khulna Division, Bangladesh", placetype = "division"}}, -- 1,210,000 (Agglomeration)
["Phnom Penh"] = {container = "Cambodia"}, -- 2,925,000 (Agglomeration)
["Tehran"] = {container = {key = "Tehran Province, Iran", placetype = "province"}}, -- 16,800,000 (Agglomeration)
["Teheran"] = {alias_of = "Tehran", display = true},
["Mashhad"] = {container = {key = "Razavi Khorasan Province, Iran", placetype = "province"}}, -- 3,475,000 (Agglomeration)
["Mashad"] = {alias_of = "Mashhad", display = true},
["Meshhed"] = {alias_of = "Mashhad", display = true},
["Meshed"] = {alias_of = "Mashhad", display = true},
["Isfahan"] = {container = {key = "Isfahan Province, Iran", placetype = "province"}}, -- 3,425,000 (Agglomeration)
["Esfahan"] = {alias_of = "Isfahan", display = true},
["Tabriz"] = {container = {key = "East Azerbaijan Province, Iran", placetype = "province"}}, -- 1,970,000 (Agglomeration)
["Shiraz"] = {container = {key = "Fars Province, Iran", placetype = "province"}}, -- 1,950,000 (Agglomeration)
["Ahvaz"] = {container = {key = "Khuzestan Province, Iran", placetype = "province"}}, -- 1,550,000 (Agglomeration)
["Qom"] = {container = {key = "Qom Province, Iran", placetype = "province"}}, -- 1,450,000 (City)
["Kermanshah"] = {container = {key = "Kermanshah Province, Iran", placetype = "province"}}, -- 1,130,000 (City)
["Baghdad"] = {container = "Iraq"}, -- 7,800,000 (Administrative Area (urban population))
["Basra"] = {container = "Iraq"}, -- 1,710,000 (Administrative Area (urban population))
["Mosul"] = {container = "Iraq"}, -- 1,550,000 (Administrative Area (urban population))
["Erbil"] = {container = "Iraq"}, -- 1,220,000 (Administrative Area (urban population))
["Kirkuk"] = {container = "Iraq"}, -- 1,160,000 (Administrative Area (urban population))
["Najaf"] = {container = "Iraq"}, -- 1,050,000 (Administrative Area (urban population))
["Tel Aviv"] = {container = "Israel"}, -- 3,000,000 (Agglomeration)
-- Jerusalem is not recognized internationally as part of either Israel or Palestine, but as a
-- [[w:corpus separatum]], so put the container as "Asia" and list Israel and Palestine as additional parents for
-- categorization purposes.
["Jerusalem"] = {container = {key = "Asia", placetype = "continent"},
addl_parents = {"Israel", "Palestine"}}, -- 1,080,000 (Agglomeration)
["Amman"] = {container = "Jordan"}, -- 6,150,000 (unindicated)
["Irbid"] = {container = "Jordan"}, -- 1,070,000 (unindicated)
["Almaty"] = {container = "Kazakhstan"}, -- 2,700,000 (Agglomeration)
["Alma-Ata"] = {alias_of = "Almaty"}, -- former name, sometimes still used; don't display-canonicalize
["Astana"] = {container = "Kazakhstan"}, -- 1,600,000 (Agglomeration)
["Shymkent"] = {container = "Kazakhstan"}, -- 1,370,000 (Agglomeration)
["Kuwait City"] = {container = "Kuwait"}, -- 5,050,000 (Agglomeration)
["Bishkek"] = {container = "Kyrgyzstan"}, -- 1,540,000 (Agglomeration)
["Beirut"] = {container = "Lebanon"}, -- 1,930,000 (unindicated; population of low reliability)
-- Kuala Lumpur is a federal capital city, not in any state
["Kuala Lumpur"] = {container = "Malaysia"}, -- 9,550,000 (Agglomeration)
-- there are various George Towns and Georgetowns
["George Town, Malaysia"] = {container = {key = "Penang, Malaysia", placetype = "state"}, wp = "%l, %c"}, -- 2,075,000 (Agglomeration)
["George Town"] = {alias_of = "George Town, Malaysia"},
["Ulaanbaatar"] = {container = "Mongolia"}, -- 1,610,000 (City)
["Ulan Bator"] = {alias_of = "Ulaanbaatar", display = true},
["Yangon"] = {container = "Myanmar"}, -- 5,650,000 (Municipality (urban population))
["Rangoon"] = {alias_of = "Yangon", display = true},
["Mandalay"] = {container = "Myanmar"}, -- 1,600,000 (Municipality (urban population))
["Kathmandu"] = {container = "Nepal"}, -- 3,175,000 (Agglomeration)
-- Pyongyang is a directly governed city, not in any province
["Pyongyang"] = {container = "North Korea"}, -- 3,025,000 (Administrative Area (urban population))
["Muscat"] = {container = "Oman"}, -- 1,620,000 (Agglomeration)
["Gaza"] = {container = "Palestine", wp = "Gaza City"}, -- 2,275,000 (unindicated)
["Gaza City"] = {alias_of = "Gaza"},
["Doha"] = {container = "Qatar"}, -- 2,650,000 (Agglomeration)
["Colombo"] = {container = "Sri Lanka"}, -- 4,975,000 (unindicated)
["Damascus"] = {container = "Syria"}, -- 3,975,000 (unindicated; population of low reliability)
["Aleppo"] = {container = "Syria"}, -- 1,980,000 (unindicated; population of low reliability)
["Dushanbe"] = {container = "Tajikistan"}, -- 1,270,000 (City)
["Bangkok"] = {container = "Thailand"}, -- 21,800,000 (Agglomeration)
-- Chiang Mai not in citypopulation.de, but 1,198,000 urban population in 2021 per Wikipedia
-- [[w:List_of_municipalities_in_Thailand#Largest_cities_by_urban_population]]
["Chiang Mai"] = {container = {key = "Chiang Mai Province, Thailand", placetype = "province"}},
["Chonburi"] = {container = {key = "Chonburi Province, Thailand", placetype = "province"}}, -- 1,570,000 (Agglomeration; including Pattaya)
-- metro area population stats from https://www.statista.com/statistics/255483/biggest-cities-in-turkey/ as of 2021;
-- second source is citypopulation.de reference date 2025-01-01.
["Istanbul"] = {placetype = {"city", "province"}, divs = {"districts"}, container = "Turkey"}, -- 15.2 million; 16,000,000 (Agglomeration)
["İstanbul"] = {alias_of = "Istanbul", display = true},
["Ankara"] = {container = {key = "Ankara Province, Turkey", placetype = "province"}}, -- 5.15 million; 5,200,000 (Agglomeration)
["Izmir"] = {container = {key = "İzmir Province, Turkey", placetype = "province"}, wp = "İzmir"}, -- 2.95 million; 3,025,000 (Agglomeration)
["İzmir"] = {alias_of = "Izmir", display = true},
["Bursa"] = {container = {key = "Bursa Province, Turkey", placetype = "province"}}, -- 2.02 million; 2,200,000 (Agglomeration)
["Adana"] = {container = {key = "Adana Province, Turkey", placetype = "province"}}, -- 1.77 million; 1,780,000 (Agglomeration)
["Gaziantep"] = {container = {key = "Gaziantep Province, Turkey", placetype = "province"}}, -- 1.71 million; 1,750,000 (Agglomeration)
["Antalya"] = {container = {key = "Antalya Province, Turkey", placetype = "province"}}, -- 1.3 million; 1,400,000 (Agglomeration)
["Konya"] = {container = {key = "Konya Province, Turkey", placetype = "province"}}, -- 1.35 million; 1,390,000 (Agglomeration)
["Diyarbakır"] = {container = {key = "Diyarbakır Province, Turkey", placetype = "province"}}, -- 1.07 million; 1,100,000 (Agglomeration)
-- Diyarbakır is more common per Ngrams and Google Scholar, but Diyarbakir is the Kurdish form, so we should not
-- display-canonicalize to the Turkish form Diyarbakır.
["Diyarbakir"] = {alias_of = "Diyarbakır"},
["Mersin"] = {container = {key = "Mersin Province, Turkey", placetype = "province"}}, -- 1.03 million; 1,060,000 (Agglomeration)
["Ashgabat"] = {container = "Turkmenistan"}, -- 1,150,000 (Agglomeration)
["Dubai"] = {container = "United Arab Emirates"}, -- 6,050,000 (Agglomeration; including Sharjah)
["Abu Dhabi"] = {container = "United Arab Emirates"}, -- 1,850,000 (City)
["Sharjah"] = {container = "United Arab Emirates"}, -- 1,800,000 (Metro area 2022-2023 per Wikipedia; separate from Dubai)
["Tashkent"] = {container = "Uzbekistan"}, -- 3,850,000 (unindicated)
["Sanaa"] = {container = "Yemen"}, -- 3,275,000 (City; population of low reliability)
["Sana'a"] = {alias_of = "Sanaa", display = true},
["Aden"] = {container = "Yemen"}, -- 1,079,060 (?; 2023 estimate from World Population Review per Wikipedia)
------------------ Europe or Europe-like (Caucasus etc.) ---------------------
["Yerevan"] = {container = "Armenia"}, -- 1,520,000 (Agglomeration)
["Vienna"] = {container = "Austria"}, -- 2,375,000 (Agglomeration)
["Minsk"] = {container = "Belarus"}, -- 2,100,000 (unindicated)
["Brussels"] = {container = "Belgium"}, -- 2,800,000 (Consolidated Urban Area)
["Antwerp"] = {container = "Belgium"}, -- 1,270,000 (Consolidated Urban Area)
["Sofia"] = {container = "Bulgaria"}, -- 1,260,000 (Agglomeration)
["Zagreb"] = {container = "Croatia"},
["Prague"] = {container = "Czech Republic"}, -- 1,470,000 (Agglomeration)
["Brno"] = {container = "Czech Republic"}, -- 729,405 (metro area per Wikipedia as of 2024-01-01 Czech Statistical Office)
["Olomouc"] = {container = "Czech Republic"}, -- 102,293 (city; included only because someone went crazy creating Olomouc-related terms)
["Copenhagen"] = {container = "Denmark"}, -- 1,800,000 (Consolidated Urban Area)
["Helsinki"] = {container = {key = "Uusimaa, Finland", placetype = "region"}}, -- 1,560,000 (Consolidated Urban Area)
["Tbilisi"] = {container = "Georgia"}, -- 1,430,000 (Agglomeration)
["Athens"] = {container = "Greece"},
["Thessaloniki"] = {container = "Greece"},
["Budapest"] = {container = "Hungary"},
-- FIXME, per Wikipedia "County Dublin" is now the "Dublin Region"
["Dublin"] = {container = {key = "County Dublin, Ireland", placetype = "county"}},
["Riga"] = {container = "Latvia"},
["Amsterdam"] = {container = {key = "North Holland, Netherlands", placetype = "province"}},
["Rotterdam"] = {container = {key = "South Holland, Netherlands", placetype = "province"}},
["The Hague"] = {container = {key = "South Holland, Netherlands", placetype = "province"}},
-- Christchurch (metro 546,600) and Wellington (metro 439,800) are too small to make it.
["Auckland"] = {container = {key = "Auckland, New Zealand", placetype = "region"}},
["Oslo"] = {container = {key = "Oslo, Norway", placetype = "county"}},
["Warsaw"] = {container = {key = "Masovian Voivodeship, Poland", placetype = "voivodeship"}},
["Katowice"] = {container = {key = "Silesian Voivodeship, Poland", placetype = "voivodeship"}},
--- Ngrams (up through 2022) and Google Scholar (>= 2024) confirms the common form "Krakow" without accent.
["Krakow"] = {container = {key = "Lesser Poland Voivodeship, Poland", placetype = "voivodeship"}, wp = "Kraków"},
["Kraków"] = {alias_of = "Krakow", display = true},
["Cracow"] = {alias_of = "Krakow", display = true},
--- Ngrams (up through 2022) and Google Scholar (>= 2024) confirm "Gdańsk" and "Poznań" with accent.
["Gdańsk"] = {container = {key = "Pomeranian Voivodeship, Poland", placetype = "voivodeship"}},
["Gdansk"] = {alias_of = "Gdańsk", display = true},
["Poznań"] = {container = {key = "Greater Poland Voivodeship, Poland", placetype = "voivodeship"}},
["Poznan"] = {alias_of = "Poznań", display = true},
--- Ngrams (up through 2022) and Google Scholar (>= 2024) confirms the common form "Lodz" without accents.
["Lodz"] = {container = {key = "Lodz Voivodeship, Poland", placetype = "voivodeship"}, wp = "Łódź"},
["Łódź"] = {alias_of = "Lodz", display = true},
["Lisbon"] = {container = {key = "Lisbon District, Portugal", placetype = "district"}},
["Porto"] = {container = {key = "Porto District, Portugal", placetype = "district"}},
["Oporto"] = {alias_of = "Porto", display = true},
["Bucharest"] = {container = "Romania"},
["Belgrade"] = {container = "Serbia"},
["Stockholm"] = {container = "Sweden"},
["Zurich"] = {container = "Switzerland"},
--- Ngrams (up through 2022) and Google Scholar (>= 2024) confirms the common form "Zurich" without umlaut.
--- Even Wikipedia uses the form without umlaut.
["Zürich"] = {alias_of = "Zurich", display = true},
["Kyiv"] = {container = "Ukraine"}, -- not in Kyiv Oblast
-- Don't display-canonicalize Kiev -> Kyiv because in ancient contexts, Kiev is still more common.
["Kiev"] = {alias_of = "Kyiv"},
["Kharkiv"] = {container = {key = "Kharkiv Oblast, Ukraine", placetype = "oblast"}},
["Odessa"] = {container = {key = "Odesa Oblast, Ukraine", placetype = "oblast"}, wp = "Odesa"},
-- Don't display-canonicalize Odesa -> Odessa because it may be interpreted as a political statement.
["Odesa"] = {alias_of = "Odessa"},
------------------ North America, South America ---------------------
-- Primary figures from citypopulation.de retrieved on 2025-04-26 (reference date 2025-01-01);
-- Wikipedia metropolitan figures from [[w:List of metropolitan areas in the Americas]] based on per-country data;
-- Wikipedia city limits figures from [[w:List of largest cities in the Americas]].
["Buenos Aires"] = {container = "Argentina"}, -- 16,800,000 (Consolidated Urban Area; 13,985,794 metropolitan area per Wikipedia)
["Córdoba, Argentina"] = {container = "Argentina", wp = "%l, %c"}, -- 1,810,000 (Consolidated Urban Area; 1,505,25 city limits per Wikipedia)
-- to avoid confusion with Córdoba in Spain
["Córdoba"] = {alias_of = "Córdoba, Argentina"},
["Cordoba"] = {alias_of = "Córdoba, Argentina", display = "Córdoba"},
["Rosario"] = {container = "Argentina", wp = "%l, Santa Fe"}, -- 1,510,000 (Consolidated Urban Area; 1,348,725 metropolitan area per Wikipedia)
["Mendoza"] = {container = "Argentina", wp = "%l, %c"}, -- 1,180,000 (Consolidated Urban Area)
["San Miguel de Tucumán"] = {container = "Argentina"}, -- 1,110,000 (Consolidated Urban Area)
["Tucumán"] = {alias_of = "San Miguel de Tucumán"},
["Tucuman"] = {alias_of = "San Miguel de Tucumán", display = "Tucumán"},
["Santa Cruz de la Sierra"] = {container = "Bolivia"}, -- 1,960,000 (Consolidated Urban Area); 1,606,671 (city limits per Wikipedia)
["Santa Cruz"] = {alias_of = "Santa Cruz de la Sierra"},
["La Paz"] = {container = "Bolivia"}, -- 1,870,000 (Consolidated Urban Area; composed of El Alto, now slightly larger, and La Paz)
["El Alto"] = {container = "Bolivia"},
["Cochabamba"] = {container = "Bolivia"}, -- 1,280,000 (Consolidated Urban Area)
["Santiago"] = {container = "Chile"}, -- 8,400,000 (Consolidated Urban Area; 6,903,479 city limits? per Wikipedia)
["Valparaíso"] = {container = "Chile"}, -- 1,060,000 (Consolidated Urban Area)
["Valparaiso"] = {alias_of = "Valparaíso"}, -- 1,060,000 (Consolidated Urban Area)
["Bogotá"] = {container = "Colombia"}, -- 10,600,000 (Agglomeration; 12,772,828 metropolitan area per Wikipedia)
["Bogota"] = {alias_of = "Bogotá", display = true},
["Medellín"] = {container = "Colombia"}, -- 4,350,000 (Agglomeration; 4,068,000 metropolitan area per Wikipedia)
["Medellin"] = {alias_of = "Medellín", display = true},
["Cali"] = {container = "Colombia"}, -- 2,975,000 (Agglomeration; 2,837,000 metropolitan area per Wikipedia)
["Barranquilla"] = {container = "Colombia"}, -- 2,375,000 (Agglomeration; 1,341,160 city limits per Wikipedia)
["Bucaramanga"] = {container = "Colombia"}, -- 1,380,000 (Agglomeration)
["Cartagena, Colombia"] = {container = "Colombia", wp = "%l, %c"}, -- 1,250,000 (Agglomeration)
-- to avoid confusion with Cartagena, Spain
["Cartagena"] = {alias_of = "Cartagena, Colombia"},
["Cúcuta"] = {container = "Colombia"}, -- 1,130,000 (Agglomeration)
["Cucuta"] = {alias_of = "Cúcuta", display = true},
-- to avoid conflict with San Jose, California
["San José, Costa Rica"] = {container = "Costa Rica", wp = "%l, %c"}, -- 2,450,000 (Municipality (urban population); 3,160,000 metropolitan area per Wikipedia)
["San José"] = {alias_of = "San José, Costa Rica"},
["San Jose"] = {alias_of = "San José, Costa Rica"}, -- display = "San José"; causes error due to San Jose alias for California city; FIXME
["Havana"] = {container = "Cuba"}, -- 2,150,000 (City; 2,137,847 city limits? per Wikipedia)
["Santo Domingo"] = {container = "Dominican Republic"}, -- 3,900,000 (Municipality (urban population); 4,274,651 ??? per Wikipedia)
["Guayaquil"] = {container = "Ecuador"}, -- 3,350,000 (Agglomeration; 3,092,000 metro area? per Wikipedia)
["Quito"] = {container = "Ecuador"}, -- 2,875,000 (Agglomeration; 2,889,703 metro area? per Wikipedia)
["San Salvador"] = {container = "El Salvador"}, -- 1,580,000 (Municipality (urban population))
["Guatemala City"] = {container = "Guatemala"}, -- 3,375,000 (Municipality (urban population); 3,160,000 metro area? per Wikipedia)
["Port-au-Prince"] = {container = "Haiti"}, -- 3,050,000 (Agglomeration; population of low reliability; 2,915,000 metro area? per Wikipedia)
["San Pedro Sula"] = {container = "Honduras"}, -- 1,330,000 (Consolidated Urban Area)
["Tegucigalpa"] = {container = "Honduras"}, -- 1,220,000 (Urban Area)
["Managua"] = {container = "Nicaragua"}, -- 1,400,000 (Consolidated Urban Area)
["Panama City"] = {container = "Panama"}, -- 1,430,000 (Urban Area)
["Asunción"] = {container = "Paraguay"}, -- 2,350,000 (Municipality (urban population))
["Lima"] = {container = "Peru"}, -- 12,000,000 (Agglomeration; 11,283,787 ??? per Wikipedia)
["Arequipa"] = {container = "Peru"}, -- 1,210,000 (Agglomeration)
["San Juan"] = {container = {key = "Puerto Rico", placetype = "commonwealth"}, wp = "%l, %c"}, -- 1,910,000 (Consolidated Urban Area)
["Montevideo"] = {container = "Uruguay"}, -- 1,810,000 (Agglomeration; 1,302,954 ??? per Wikipedia)
["Caracas"] = {container = "Venezuela"}, -- 3,850,000 (Consolidated Urban Area; 5,243,301 ??? per Wikipedia)
["Maracaibo"] = {container = "Venezuela"}, -- 2,825,000 (Consolidated Urban Area; 5,278,448 ??? per Wikipedia)
-- to avoid confusion with Valencia (city and autonomous community of Spain)
["Valencia, Venezuela"] = {container = "Venezuela", wp = "%l, %c"}, -- 2,100,000 (Consolidated Urban Area)
["Valencia"] = {alias_of = "Valencia, Venezuela"},
["Maracay"] = {container = "Venezuela"}, -- 1,480,000 (Consolidated Urban Area)
["Barquisimeto"] = {container = "Venezuela"}, -- 1,360,000 (Consolidated Urban Area)
}
export.misc_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(nil, "country"),
default_placetype = "city",
data = export.misc_cities,
}
--[==[ var:
List of all known locations, in groups. The first group lists continents and continental regions, followed by three
groups listing top-level locations: countries, "country-like entities" (de-facto/unrecognized/etc. countries and
dependent territories) and former polities (countries, empires, etc.). After that come first-level subpolities
(administrative divisions) of several, mostly large, countries, followed by groups of cities. China and the United
Kingdom include second-level subpolities (in the case of China, only the largest ones as the full list runs in the
hundreds).
]==]
export.locations = {
export.continents_group,
export.countries_group,
export.country_like_entities_group,
export.former_countries_group,
export.australia_group,
export.austria_group,
export.bangladesh_group,
export.brazil_group,
export.bulgaria_group,
export.canada_group,
export.china_group,
export.china_prefecture_level_cities_group,
export.china_prefecture_level_cities_group_2,
export.egypt_group,
export.finland_group,
export.france_group,
export.france_departments_group,
export.germany_group,
export.greece_group,
export.india_group,
export.indonesia_group,
export.iran_group,
export.ireland_group,
export.italy_group,
export.japan_group,
export.laos_group,
export.lebanon_group,
export.malaysia_group,
export.malta_group,
export.mexico_group,
export.moldova_group,
export.morocco_group,
export.netherlands_group,
export.new_zealand_group,
export.nigeria_group,
export.north_korea_group,
export.norway_group,
export.pakistan_group,
export.philippines_group,
export.poland_group,
export.portugal_group,
export.romania_group,
export.russia_group,
export.saudi_arabia_group,
export.south_africa_group,
export.south_korea_group,
export.spain_group,
export.taiwan_group,
export.thailand_group,
export.turkey_group,
export.ukraine_group,
export.united_kingdom_group,
export.united_states_group,
export.england_group,
export.northern_ireland_group,
export.scotland_group,
export.wales_group,
export.vietnam_group,
export.australia_cities_group,
export.brazil_cities_group,
export.canada_cities_group,
export.france_cities_group,
export.germany_cities_group,
export.india_cities_group,
export.indonesia_cities_group,
export.italy_cities_group,
export.japan_cities_group,
export.mexico_cities_group,
export.nigeria_cities_group,
export.pakistan_cities_group,
export.philippines_cities_group,
export.russia_cities_group,
export.saudi_arabia_cities_group,
export.south_korea_cities_group,
export.spain_cities_group,
export.taiwan_cities_group,
export.united_kingdom_cities_group,
export.united_states_cities_group,
export.new_york_boroughs_group,
export.vietnam_cities_group,
export.misc_cities_group,
}
return export
dws9ii25xu6ehccq914gw62b8c7d56n
88791
88790
2026-08-06T18:24:42Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:place/locations]]
88790
Scribunto
text/plain
local export = {}
export.force_cat = false -- set to true to force category generation even on non-mainspace pages
local m_table = require("Module:table")
local string_utilities_module = "Module:string utilities"
local en_utilities_module = "Module:en-utilities"
local insert = table.insert
local concat = table.concat
local dump = mw.dumpObject
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
--[==[ intro:
This module contains data on all known locations, along with some lower-level code to process them (higher-level
known-location code is in [[Module:place/placetypes]]). You must load this module using require(), not using
mw.loadData().
===Location data===
'''NOTE: In order to understand the following better, first read the introductory documentation in [[Module:place]],
especially the section `More about known locations`.'''
The bulk of the code in this module (after some helper functions and placetype tables) describes the known locations
and their relationships. Locations are grouped into ''location groups'' that share some common properties (examples are
states of the United States and cities in Brazil). Each location group is associated with two tables, a ''data table''
that lists the locations and their individual properties, and a ''metadata table'' that lists group-level properties and
defaults for the location properties. Each metadata table points to the associated data table (i.e. contains the data
table as its `data` field), and the global `locations` variable holds a list of all group metadata tables. A given
location is generally described by three values: (a) the group metadata table for the group the location is part of; (b)
the location's canonical ''key'', which is the actual key in the group's data table and is globally unique across all
locations; and (c) the location's ''spec'', which is the initialized object describing the properties of the location
and comes from the value in the data table corresponding to the canonical key, transformed by the `initialize_spec()`
function. These are typically named `group`, `key` and `spec`, respectively and in that order, and are found in the
arguments to many functions.
In a per-group data table, the keys are either ''canonical keys'' describing locations (which, as mentioned above, must
be globally unique) or ''alias keys'' specifying an allowed alias for a given location. There may be multiple aliases
for a given location and the alias keys only need to be unique within a particular group data table, not across all
groups. It is also possible for the same string to serve as an alias key in one group and a canonical key in another
group. (For example, `Newcastle` appears as an alias key in two different groups, referring to two different locations,
canonically known as `Newcastle upon Tyne`, for the city in England, and `Newcastle, New South Wales`, for the city in
New South Wales, Australia; and `Birmingham` appears both as a canonical key in the group of English cities and an alias
key for canonical `Birmingham, Alabama` in the group of US cities.) The corresponding value objects are different for
canonical and alias keys. Corresponding to canonical keys are ''location specs'', describing the properies of the
location that cannot be derived from default properties of the group or global defaults. Corresponding to alias keys
are ''alias specs'', which are highly restricted in the properties they can contain, and whose properties do not have
per-group defaults, but only global defaults.
The canonical key is always the same as the bare category corresponding to the location, which is one of the reasons it
must be globally unique. For example, the country of Georgia uses the canonical key `Georgia` and corresponding bare
category [[:Category:Georgia]], while the US state of Georgia uses the canonical key `Georgia, USA` and corresponding
bare category [[:Category:Georgia, USA]]. The following conventions are followed in naming keys:
* Countries, ''country-like entities'' (which are a mixture of unrecognized de-facto states and dependent territories)
and ''former countries'' (which also includes other types of polities, such as the Roman Empire) use their unqualified
placename as the canonical key. (See the documentation for [[Module:place]] for the distinction between keys and
placenames, which is critical to understand when working with location data.) This also applies to constituent
countries (such as England, Aruba and the Faroe Islands) and constituent parts of grouped dependent territories (such
as the island of Saint Helena, which is administratively part of the British overseas territory of Saint Helena,
Ascension and Tristan da Cunha).
* Cities (including prefecture-level cities in China, which behave in most respects more like non-city administrative
divisions) also normally use their unqualified placename as the canonical key, but if this causes name conflicts or
ambiguities, they use a ''qualified key'' containing either the country name or immediate containing division (if
different) following a comma, such as the case of `Newcastle, New South Wales` and `Birmingham, Alabama` above.
Examples of name conflicts are the two cities just given; examples of ambiguities are the major cities of León and
Mérida in Mexico and city of Cartagena, Colombia, which are given the respective canonical keys of `León, Guanajuato`,
`Mérida, Yucatán` and `Cartagena, Colombia` to avoid ambiguity with the well-known respective cities of the same name
in Spain, even though none of those cities are large enough to be included as known locations in this module. (The
cutoff is generally having a metro area of at least 1,000,000 inhabitants, although there are exceptions.)
* Administrative divisions of countries, other than the exceptions noted above for constituent countries and dependent
territories, use a qualified key that contains the name of the country or constituent country in it, e.g.
`Normandy, France` (a region), `Calvados, France` (a department in the region of Normandy), `Herefordshire, England`
(a ceremonial county), `Northwest Territories, Canada` (a territory), `Central Finland, Finland` (a region),
`Antalya Province, Turkey` (a province), `Cluj County, Romania` (a county), `County Cork, Ireland` (a county) and
`New York, USA` (a state). As shown in these various examples, (a) first and second-level divisions are sometimes both
included (as in France, the United Kingdom and China); (b) the qualifier after the comma is sometimes a constituent
country (England) instead of a country (United Kingdom), and is sometimes abbreviated (USA rather than United States
or Unites States of America); (c) the word `the` is not normally included in the key even if the location is normally
preceded by `the` when following a preposition (there is a property in the location and alias specs to indicate this),
except in a very few cases (most notably `The Hague`); (d) the country is included as a qualifier even if it creates
an apparent redundancy, as with `Central Finland, Finland`; and (e) sometimes the placetype is included in the key, as
with provinces in Turkey and several other countries; states in Nigeria; and counties in Ireland, Romania and several
other countries. Whether the placetype is included, and whether it follows or precedes the placename, depends on
per-country conventions. For example, provinces in Turkey, Iran and several other countries (likewise for states in
Nigeria, oblasts in Russia, etc.) conventionally include the word "Province", "State", "Oblast" etc. in their name
because they are normally named after the largest city in the division, which would otherwise lead to ambiguity; and
counties in Ireland and Northern Ireland (and likewise County Durham, England) normally have the word "County"
preceding rather than following them in their conventional name, so we follow this practice. The Wikipedia article
naming scheme for a given administrative division is a strong clue as to how the division is normally referred to,
and we usually follow this practice. (A minor exception is that the Wikipedia articles for provinces in Iran, Laos and
Thailand include the word `province` with an initial lowercase letter while provinces elsewhere, e.g. North and South
Korea, Saudi Arabia and Turkey, use uppercase `Province`; we normalize to uppercase `Province` in all cases.)
As mentioned above, associated with canonical keys in the group data table are location specs, which are objects
containing properties. It is important here to distinguish ''initialized specs'' from ''uninitialized specs''.
Unininitialized specs are as directly specified in [[Module:place/locations]], containing only those properties that
differ from the per-group or global defaults. Initialized specs result from calling `initialize_spec()` on an
uninitialized spec (it is idempotent in that it will do nothing if encountering an already-initialized spec). This
copies all group-level defaults that are not overridden in the location spec itself from the group-level metadata table
into the location spec, so that in general, no more reference need be made to the group to fetch the correct value of a
given location property. (The initialization process also does more transformations in a few cases, noted below.) Note
that the default value of a given property is stored under a key in the group metadata table that is preceded by the
string `default_`; for example, the default value corresponding to the `placetype` property of a given location is
specified in the `default_placetype` key in the group metadata table.
The following are the properties of the location spec.
* `placetype`: String specifying the placetype of the location (e.g. "country", "state", province"). This can also be a
table of such types; in this case, the first listed type is the canonical type that will be used in descriptions, but
the location will be recognized (e.g. in a holonym, or for categorizing into the bare category) when tagged with any
of the specified types. The placetype '''must''' be either specified on an individual location or defaulted at the
group level, or an error occurs.
* `container`: Either a string, a ''canonicalized container'' structure or a list of either type, specifying the
immediate ''container'' (or containers) of the given location. A container is another location which this location is
considered to be directly part of, either politically or (above the country level) geographically. Some locations
belong to multiple immediate containers; this applies especially to transcontinental countries such as Russia and
Turkey. Containers can themselves have containers, forming a tree (or more correctly, a [[w:directed acyclic graph]])
of locations. The list of immediate container(s), followed by the container(s) of the container(s), etc., is termed
the ''container trail'', and some functions compute and return this trail as part of their operation. When a location
spec is initialized, the given container spec is canonicalized into ''canonical container form'', which consists of a
list of canonicalized container structures, each of which is of the form
`{key = "``container_key``", placetype = "``container_placetype``"}`, where ``container_key`` is a canonical location
key and ``container_placetype`` should be the listed placetype for the location, or the first listed placetype if
there are multiple. (FIXME: Since the key uniquely identifies the container location, we should eliminate the
placetype from the container structure.) The list of canonicalized container structures is stored into the
`.containers` field of the location spec (this happens even if the container value is unset in its uninitialized spec
form, causing it to default to the corresponding group-level value), and the `.container` field is set to {nil}. The
canonicalization process is described in more detail below under [[#Container spec canonicalization]].
* `divs`: List of recognized political divisions; e.g. for the Netherlands, a specification of the form
`divs = {"provinces", "municipalities"}` will allow categories such as [[:Category:de:Provinces of the Netherlands]]
and [[:Category:pt:Municipalities of the Netherlands]] to be created. Any division that appears here must also be
found in `placetype_data`, or an error occurs. The entities appearing in the `divs` list can be structures as well as
just strings; this is explained more below under [[#Location divisions]]. Additional political divisions that apply to
all locations in a group can be specified at the group level using the group-only property `addl_divs`, which has the
same format as `divs`. This is intended to be used in the situation where some division types are shared among all
locations in the group and others differ from location to location. An example where this is used is the United
States, where `census-designated places` is specified in the group-level `addl_divs` so that all 50 states have
census-designated places categorized as e.g. [[:Category:Census-designated places in Arizona, USA]], but `counties`
and `county seats` are specified in the group-level `default_divs` because not all states have counties and county
seats (Alaska has boroughs and borough seats and Louisiana has parishes and parish seats), and some states have
additional divisions (New Jersey and Pennsylvania also have boroughs, while Colorado and Connecticut have
municipalities). Note that under most circumstances (particularly, if `container_parent_type` is not set as a property
associated with the division type), any division type specified on a sub-country-level location must also be specified
on all containers up through the country. For example, since French departments specify `communes` and
`municipalities` in `default_divs`, the same division types must be (and are) specified on French regions and for
France itself.
* `keydesc`: String directly specifying a description of the location, for use in generating the contents of category
pages related to the location. In place of a string, a function of three arguments (`group`, `key`, `spec`, as is
normal for locations) that computes the location description can also be given. This is used, for example, for
Russian federal subjects; see `construct_russia_federal_subject_keydesc`. The special string `+++` contained in the
keydesc is replaced with the default value of the location description, which specifies the location's placename,
placetype, and the corresponding values for each container in the container trail, generally up through (but not
beyond) the country level; see `no_include_container_in_desc` below. The location description is used to construct
the full description of various categories, such as bare location categories, whose description generally reads
`"{{(((}}langname}}} terms related to the people, culture, or territory of ``keydesc``."` where ``keydesc`` is the
specified or auto-constructed location description.
* `fulldesc`: String overriding the full description for the bare location category (but not for any other category).
This is currently used only for the location `Earth`, at the very top of the tree (because the standard
`people, culture or territory of ...` text doesn't make sense here), and for `Antarctica` (because it has no permanent
inhabitants). FIXME: This should be renamed `bare_category_fulldesc`.
* `addl_parents`: Specify additional parents for the bare location category, in addition to the category or categories
generated based on the immediate container(s). For example, `Hawaii, USA` specifies `Polynesia` as an additional
parent category; both `North Korea` and `South Korea` specify `Korea` (which is a specially handled location category)
as an additional parent; and `Earth` specifies `nature` (not a location category, but still a topic category) as an
additional parent (which in this case becomes the first parent, as `Earth` has no container). The only restriction on
the categories in `addl_parents` is that they must be topic categories, because each language-specific version of the
bare location category gets the corresponding language-specific versions of the categories in `addl_parents`. FIXME:
This shoudl be renamed `bare_category_addl_parents`.
* `wp`: Spec describing how to construct the Wikipedia article for the location. Each spec is either `true` (equivalent
to `"%l"`, i.e. use the full location placename directly) or a string containing formatting directives, indicating how
to construct the article name. The allowed formatting directives are `%l` (the full location placename), `%e` (the
elliptical location placename) and `%c` (the full placename of the first immediate container). For example, the
default value of `wp` for the group of United States cities is `"%l, %c"` since the city articles tend to be named
e.g. `Austin, Texas` (but with many exceptions, specified using `wp` fields at the city level). Another example is
Thai provinces, which specify a group-level default of `"%e province"` as the Wikipedia articles have lowercase
`province` in their name but the Thai province keys specified in this module have uppercase `Province`. Here we have
to use `%e` to get the placename without the word `Province` in it. The default is `true`, which simply uses the full
location placename as the article name. Note that the Wikipedia article, along with the Wikipedia and Commons category
pages, are shown in the upper right of bare category pages.
* `wpcat`: Spec describing how to construct the Wikipedia category page for the location (i.e. the page listing articles
and categories relevant to the location). The format is the same as with `wp`, and it defaults to the value of `wp`.
It rarely needs to be specified because the category page and the article page almost always follow the same format.
* `commonscat`: Spec describing how to construct the Commons category page for the location (i.e. the page on the
MediaWiki Commons site listing articles and categories relevant to the location). It has the same format as `wp` and
`wpcat` and defaults to `wpcat`, which is usually (but not always) correct.
* `the`: Boolean specifying whether a location should be preceded by `the` when following a preposition, e.g. in
category names such as [[:Category:Cities in the Northern Territory, Australia]] and in old-style place descriptions
when the location occurs as the first holonym, such as the city [[Darwin]] described using
{{tl|place|city|terr/Northern Territory|c/Australia}}. Note that the global default for this and all Boolean
properties is {nil}, which amounts to the same as {false}.
* `british_spelling`: Boolean indicating whether the location in question uses British spelling. Currently this only
affects whether the spelling `neighborhoods` or `neighbourhoods` is used in categories such as
[[:Category:Neighborhoods of New York City]] and [[:Category:Neighbourhoods of Sydney]]. This usually needs to be set
only at the top level (i.e. country or country-like entity), because lower-level entities look up the container trail
for any container that has `british_spelling = true` set, and if found, assume that British spelling applies. The
general principle used in setting this is that all countries in Europe, all dependent territories of any such country,
all former British colonies, and any dependent territories of these former colonies, are assumed to use British
spelling, while all other countries and associated dependent territories are assumed to use American spelling. This
can potentially be modified on a case-by-case basis.
* `is_city`: Boolean indicating whether the location in question is a city. This is explicitly set to `true` for
city-states (e.g. Monaco and Vatican City), dependent territories that are cities (e.g. Hong Kong, Macau, Bonaire,
Gibraltar, etc.), certain city-level administrative divisions (such as `City of Belfast, Northern Ireland`) and
(through a group-levell setting) New York boroughs. In addition, it is set to `true` in initialize_spec() whenever
the group-level `default_placetype == "city"`, so that all cities get it set without explicitly needing to add a
group-level setting for this. Note that the condition `default_placetype == "city"` intentionally excludes Chinese
prefecture-level cities, which aren't really cities in that (for example) they don't directly contain neighborhoods,
but do contain cities within them. This setting is used in various places: (a) to add cities, rivers, etc. to
categories like [[:Category:Rivers in Osaka Prefecture, Japan]] and [[:Category:Cities in Wuhan]] for holonyms that
are ''not'' cities; (b) to add districts, neighborhoods, and the like to categories like
[[:Category:Neighborhoods of Brooklyn]] and [[:Category:Neighborhoods of Monaco]] for holoynms that ''are'' cities;
(c) generally, to determine which "generic" placetypes (cities, rivers, neighborhoods, etc.) apply to the location.
(Those that can occur with cities have a `generic_before_cities` setting in [[Module:place/placetypes]], and those
that can occur with non-cities have a `generic_before_non_cities` setting.)
* `is_former_place`: Boolean that should be set on former places such as the Soviet Union and the Roman Empire. For such
places, categories such as [[:Category:fr:Rivers in the Soviet Union]] are neither generated nor recognized (more
generally, no "generic" placetypes apply except for `places`), and category descriptions include the word `former`.
* `overriding_bare_label_parents`: Document me!
* `bare_category_parent_type`: Document me!
* `no_container_cat`: Document me!
* `no_container_parent`: Document me!
* `no_generic_place_cat`: Document me!
* `no_check_holonym_mismatch`: Document me!
* `no_auto_augment_container`: Document me!
* `no_include_container_in_desc`: Document me!
====Location divisions====
The `divs` field of a location describes the recognized political division types of that location. Specifying a given
division type will cause places defined as being of the specified division type and with the location as a holonym will
cause the place to be categorized as ` ``placetypes`` in/of ``location`` `; for example, specifying that the United
States has `"states"` as a division will cause anything defined as {{tl|place|fr|state|c/US}} to be categorized under
[[:Category:fr:States of the United States]]. Note that you do not have to explicitly specify division types for
"generic" placetypes (those that have a `generic_before_non_cities` field if the location is not a city, or that have a
`generic_before_cities` field if the location is a city); this includes things like cities, towns, villages,
neighbo(u)rhoods and rivers. A given element in the `divs` list is usually a string naming a plural placetype; the
placetype is automatically converted to the singular for recognizing the placetype in a {{tl|place}} spec, and irregular
plurals such as `kibbutzim` are handled correctly as long as the placetype specifies an appropriate `plural` field
(if the `plural` isn't explicitly given, the default singularization algorithm in [[Module:en-utilities]] is run, which
gets most things correctly but has problems with `passes` and `fortresses`, which are singularized to `passe` and
`fortresse`; for this reason, an explicit plural entry is added to terms in ''-ss''). In place of a string, an object
can be given with the plural placetype in the `type` field; this allows additional properties to be specified along with
the placetype. An example of this is the `divs` list for Canada:
{
["Canada"] = {divs = {
{type = "provinces", cat_as = "provinces and territories"},
{type = "territories", cat_as = "provinces and territories"},
"counties", "districts", "municipalities", "regional municipalities",
"rural municipalities", "parishes",
"Indian reserves",
"census divisions",
{type = "townships", prep = "in"},
}, ...},
}
Here, both provinces and territories are set to categorize as `provinces and territories`, meaning that there is a
single category [[:Category:Provinces and territories of Canada]] rather than separate categories for provinces and
territories. Similar things are done for other countries that have more than one type of first-level administrative
division (e.g. Australia, China, India and Pakistan). Note that any placetype listed under `cat_as` must exist in the
table of placetypes in [[Module:place/placetypes]], and in fact there is a category-only entry there for `provinces and
territories!` (the use of exclamation point following a plural placetype means that the placetype is present only for
use in categories and won't be recognized as the placetype field in a {{tl|place}} description). In addition, townships
are declared to use `in` rather than `of` as the preposition in the category; hence the category name will be
[[:Category:Townships in Canada]] rather than [[:Category:Townships of Canada]]. (The use of `in` vs. `of` is somewhat
related to whether a given placetype is an official administrative or statistical division of the location in question
and comes in a defined list, in which case `of` should be used, or is more ill-defined, in which case `in` should be
used; the default is `of`, and the use of `in` with `townships` is probably by analogy with the use of `in` with cities
and towns.)
Another more complex example is the divisions given for Quebec:
{
["Quebec, Canada"] = {divs = {
"counties",
{type = "regional county municipalities", container_parent_type = "regional municipalities"},
{type = "regions", container_parent_type = false},
{type = "townships", prep = "in"},
{type = "parish municipalities", cat_as = {{type = "parishes", container_parent_type = "counties"}, "municipalities"}},
{type = "township municipalities", cat_as = {{type = "townships", prep = "in"}, "municipalities"}},
{type = "village municipalities", cat_as = {{type = "villages", prep = "in"}, "municipalities"}},
}, ...},
}
Here, `container_parent_type` controls the second parent category of the placetype/location category associated with the
entry. In this case, for example, [[:Category:Counties of Quebec, Canada]] will have [[:Category:Counties of Canada]] as
its second or ''container-level'' parent. However, this doesn't make sense for `regional county municipalities`, which
exist only in Quebec (so the parent category [[:Category:Regional county municipalities of Canada]] would have only one
subcategory); but they are similar to regional municipalities in British Columbia, Nova Scotia and Ontario, so the
`container_parent_type = "regional municipalities"` spec causes the container-level parent of this category to be
[[:Category:Regional municipalities of Canada]]. Likewise, `regions` as administrative divisions (as opposed to mere
geographic regions) exist only in Quebec; they have no equivalent elsewhere, so we disable the container-level parent
using `container_parent_type = false`. The specs for `parish municipalities`, `township municipalities` and
`village municipalities` show both that multiple types can be specified under `cat_as` (here, for example, we categorize
`parish municipalities` as both `parishes` and `municipalities`) and that these types can themselves have properties,
just as for entries directly under `divs`. Specifically, `{type = "parishes", container_parent_type = "counties"}`
means that any place defined as a parish municipality in Quebec will be categorized under both [[:Category:Parishes of
Quebec, Canada]] and [[:Category:Municipalities of Quebec, Canada]], and that the former will have a container-level
parent of [[:Category:Counties of Canada]] (rather than the default of [[:Category:Parishes of Canada]]). Similarly,
`township municipalities` will be categorized under both [[:Category:Townships in Quebec, Canada]] (''not''
[[:Category:Townships of Quebec, Canada]]) and [[:Category:Municipalities of Quebec, Canada]].
====Container spec canonicalization====
A fully canonicalized container spec for a given location consists of a list of ''canonicalized container objects'',
each with a `key` and `placetype` field. The `key` field should name the canonical key of some other location at a
higher level (e.g. French cities are contained in French departments, which are contained in French regions, which are
contained in France, which is contained in Europe, which is contained in Eurasia, which is contained in the Earth). The
`placetype` field should correspond to the first (canonical) placetype listed for the key in question. The process of
initializing a locaion spec converts the container spec in `.container` into a canonicalized spec in `.containers` and
removes the spec from `.container`. It works as follows:
# If the `container` field is missing, and there is a group-level `default_container` field, it is used in its place.
For example, none of the Brazilian states listed in `brazil_states` specifies a container, but the group specifies
`default_container = "Brazil"`.
# A single string or canonicalized container object is allowed and made into a one-element list.
# If a list element is a string that did ''not'' come from `default_container`, and there is a group-level
`canonicalize_key_container` field, it is assumed to be a one-argument function and is called on the string to get
a canonicalized container object.
# Any remaining strings are assumed to be countries and are used directly as the `key`, with `placetype` set to
`"country"`.
====Alias keys====
Aliases can be provided for canonical keys using ''alias keys''. Alias keys have a very different location spec
structure from canonical keys. This structure does not, in general, have defaults at the group level and is not
initialized using `initialize_spec()`, but is used as-is. The following properties are recognized in an alias location
spec:
* `alias_of`: The canonical key of which this key is an alias. Required.
* `the`: If true, this alias key is preceded by `the` following a preposition. Defaults to the group-level `default_the`
but does not pay attention to the value of `the` for the corresponding canonical key.
* `display`: This is a display alias, meaning that holonyms using the placename corresponding to this alias will be
converted to the placename corresponding to the canonical key when formatting the holonym for display. (Otherwise,
the aliasing applies only to categorization.) If the value is true, the display canonicalization is to the placename
of the canonical key; otherwise, the value should be a key whose corresponding placename is used when display
canonicalizing.
* `placetype`: The placetype of the alias. Rarely needs to be specified as it defaults to the canonical key's placetype,
and if that is unspecified, to the group-level default placetype.
====Location group metadata tables====
As mentioned above, associated with each location group is a ''metadata table'' listing group-level properties. The
metadata table contains two types of keys: group-level defaults (named like the corresponding location-level keys but
preceded by `default_`, e.g. `default_placetype` corresponding to the location-level `placetype` key) and group-only
keys, which are mostly functions. The following are the possible group-only keys:
* `data`: This points to the group data table for the group, as described above.
* `key_to_placename`: This is a function of one argument to transform the location's key (whether canonical or alias)
into the full and elliptical placenames. The difference between full and elliptical placenames is described in the
documentation for [[Module:place]], but in essence, it applies for keys that include the placetype in them (e.g.
`Phuket Province, Thailand` or `County Mayo, Ireland`), in which case the full placename includes the placetype and
the elliptical placename does not. For keys that do not include the placetype in them (e.g. `Arizona, USA` or
`Gloucestershire, England`), the full and elliptical placenames are identical. Note that neither the full nor the
elliptical placename includes the container in it; hence, for `Phuket Province, Thailand`, the full placename is
`Phuket Province` and the elliptical placename is just `Phuket`. (Note that the full vs. elliptical placename
distinction is intended only for handling cases where the placetype follows or precedes the raw placename and there
is no difference between the two in whether they are normally preceded by `the`. More complex situations, such as
`State of Mexico` (which normally takes `the`) vs. just `Mexico` (which doesn't), or `Islamabad Capital Territory` vs.
just `Islamabad`, should be handled instead by aliases.) The `key_to_placename` function takes one argument, the key,
and returns two arguments, the full and elliptical placenames, respectively. If left undefined, the default is to
chop off anything starting with a comma and return the result as both full and elliptical placename, and if
specifically set to `false`, the key is used directly as both full and elliptical placename. If it needs to be
defined, it is best to use the helper function `make_key_to_placename`, if possible (or
`make_irish_type_key_to_placename` in the case of Ireland and Northern Ireland, where `County` precedes), rather than
rolling your own. In addition, you should use the global `key_to_placename` function (which takes care of the default
implementation and such) rather than directly calling the function in the `key_to_placename` field.
* `placename_to_key`: This is approximately the inverse of `key_to_placename`, transforming a placename (which can be
either in full or elliptical form) into the corresponding key. As with `key_to_placename`, if you need to define this
(generally, when the full and elliptical placenames are different), prefer using `make_placename_to_key` (or
`make_irish_type_placename_to_key` for Ireland and Northern Ireland) to rolling your own. In addition, similarly to
`key_to_placename`, use the global `placename_to_key` function to convert placenames to keys rather than directly
invoking the function in the `placename_to_key` field. If the field is set to `false`, the placename is used unchanged
as the key. Otherwise, the default algorithm works as follows:
*# If the group-level `default_placetype == "city"`, use the placename unchanged as the key.
*# Otherwise, if the group-level `default_container` exists and is a string, append it to the placename after a comma +
space and use the result as the key.
*# Otherwise, if the group-level `default_container` is a canonical container object (an object with `key` and
`placetype` fields), and the `placetype` field is either `country` or `constituent country`, append the `key` field
to the placename after a comma + space and use the result as the key.
*# Otherwise, use the placename unchanged as the key.
* `canonicalize_key_container`: A function of one argument to convert the specified `container` field, when a string,
to canonical form. Described in more detail above under [[#Container spec canonicalization]]. It is preferable to
construct the function using `make_canonicalize_key_container`, if possible, rather than rolling your own.
* `addl_divs`: Additional political divisions appended, for all locations in the group, to the list of divisions derived
from the location-level `divs` or group-level `default_divs` fields to get the final list of divisions for the
location. See [[#Location divisions]] for more details.
]==]
-----------------------------------------------------------------------------------
-- Helper functions --
-----------------------------------------------------------------------------------
--[==[
Throw an error. `fmt` is a format string and the remaining arguments are passed through `mw.dumpObject` and then used to
format the format string as if `fmt:format(...)` were called. In general, callers should use `internal_error` unless the
error was due to bad user input rather than a logic error (which usually isn't the case in deep back-end code like
this).
]==]
function export.process_error(fmt, ...)
local args = {...}
for i = 1, select("#", ...) do
args[i] = dump(args[i])
end
return error(string.format(fmt, unpack(args)))
end
--[==[
Throw an internal error (a logic error that should never happen unless there is a bug in the code, as opposed to a user
error triggered by bad input or a system error due to something like running out of memory or hitting a time limit).
`fmt` is a format string and the remaining arguments are passed through `mw.dumpObject` and then used to format the
format string as if `fmt:format(...)` were called.
]==]
function export.internal_error(fmt, ...)
export.process_error("Internal error: " .. fmt, ...)
end
local internal_error = export.internal_error
-- Return whether `list_or_element` (a list of strings, or a single string) "contains" `item` (a string). If
-- `list_or_element` is a list, this returns true if `item` is in the list; otherwise it returns true if `item`
-- equals `list_or_element`.
local function list_or_element_contains(list_or_element, item)
if type(list_or_element) == "table" then
return m_table.contains(list_or_element, item) and true or false
end
return list_or_element == item
end
--[==[
Call the location group's `key_to_placename` function if it exists (see the comment at the top of [[Module:place]] for
the distinction between keys and placenames). Two values are returned, the full and elliptical placenames (e.g. full
`"County Durham"` vs. elliptical `"Durham"`). If the group does not define `key_to_placename`, both full and elliptical
placenames are computed by chopping off anything starting with a comma.
]==]
function export.key_to_placename(group, key)
if group.key_to_placename == false then
return key, key
end
if group.key_to_placename then
local full_placename, elliptical_placename = group.key_to_placename(key)
if type(full_placename) ~= "string" then
internal_error("Key %s returned a non-string full placename: %s", key, full_placename)
end
if type(elliptical_placename) ~= "string" then
internal_error("Key %s returned a non-string elliptical placename: %s", key, elliptical_placename)
end
return full_placename, elliptical_placename
end
key = key:gsub(",.*", "")
return key, key
end
--[==[
Call the location group's `placename_to_key` function if it exists (see the comment at the top of [[Module:place]] for
the distinction between keys and placenames) and return the result. If `placename_to_key` exists with the value `false`,
return the placename unchanged. If the group does not define `placename_to_key`, and it defines a `default_container`
whose placetype is either `country` or `constituent country`, the container name is appended to the placename after a
comma and a space. Otherwise the placename is returned unchanged.
]==]
function export.placename_to_key(group, placename)
if group.placename_to_key == false then
return placename
elseif group.placename_to_key then
local key = group.placename_to_key(placename)
if type(key) ~= "string" then
internal_error("Placename %s returned a non-string key: %s", placename, key)
end
return key
elseif group.default_placetype == "city" then
return placename
else
local defcon = group.default_container
if not defcon then
return placename
elseif type(defcon) == "string" then
return placename .. ", " .. defcon
elseif type(defcon) == "table" and (defcon.placetype == "country" or
defcon.placetype == "constituent country") then
return placename .. ", " .. defcon.key
else
return placename
end
end
end
--[==[
Initialize the location spec `spec`, augmenting it with default values taken from `group` if the spec itself doesn't
specify values for the properties. This sets `containers` to a canonicalized list of objects, each with `key` and
`placetype` keys, describing the immediate containers of the location, and erases (sets to nil) the original
non-canonicalized `container` field. (Most locations have only one immediate container but some, e.g. Russia, have more
than one. Containers should be carefully distinguished from category parents. Generally the container is the first
category parent, or the first ``n`` parents if there are ``n`` containers, but there may be additional category parents,
which indicate some sort of relation between the category parent and the location but not necessarily one of
containment.)
This function is idempotent in that nothing happens if called more than once on the same spec.
FIXME: Consider reimplementing this in a more standardly object-oriented way using metatables.
]==]
function export.initialize_spec(group, key, spec)
if spec.initialized then
return
end
local container = spec.container
local containers
local container_from_default
if not container then
container = group.default_container
container_from_default = true
end
if container then
if type(container) == "string" or container.key then
container = {container}
end
containers = {}
for _, cont in ipairs(container) do
if type(cont) == "string" then
if group.canonicalize_key_container and not container_from_default then
cont = group.canonicalize_key_container(cont)
else
cont = {key = cont, placetype = "country"}
end
end
insert(containers, cont)
end
end
spec.containers = containers
spec.container = nil
local function value_with_default(val, default_val)
if val == nil then
return default_val
else
return val
end
end
local function set_or_default(prop)
spec[prop] = value_with_default(spec[prop], group["default_" .. prop])
end
set_or_default("placetype")
if not spec.placetype then
internal_error("No placetype found in key %s for spec %s or in group `default_placetype`", key, spec)
end
set_or_default("divs")
spec.addl_divs = group.addl_divs
for _, prop in ipairs {
"keydesc",
"fulldesc",
"addl_parents",
"overriding_bare_label_parents",
"bare_category_parent_type",
"wp",
"wpcat",
"commonscat",
"british_spelling",
"the",
"no_container_cat",
"no_container_parent",
"no_generic_place_cat",
"no_check_holonym_mismatch",
"no_auto_augment_container",
"no_include_container_in_desc",
"is_city",
"is_former_place",
} do
set_or_default(prop)
end
-- `default_placetype == "city"` is correct; if `default_placetype` has something else like `prefecture-level city`
-- as the canonical placetype but also lists `city` (as Chinese prefecture-level cities do), don't mark as
-- is_city.
spec.is_city = value_with_default(spec.is_city, group.default_placetype == "city")
spec.initialized = true
end
--[=[
Given a location group, key and possible placetypes that the placename must match, check if the key exists in the group
with at least one of the group's key's placetypes matching one of the passed-in placetypes. If so, return two values:
the group key (which potentially could differ from the passed-in key due to aliases) and the corresponding spec object,
which (as with all functions that return spec objects) has been initialized using `initialize_spec()` (i.e. default
property values have been copied from the group into the spec, if the spec doesn't itself specify a value for the
property in question).
`alias_resolution` controls how aliases are resolved. Normally, both display and category aliases are followed, and
the returned key will reflect the canonical location key. However, if `alias_resolution` is {"none"}, no alias following
happens. In that case, if the key specifies an alias, the spec for the alias rather than the spec for the canonical
location is returned, and importantly, it is returned uninitialized, meaning that properties from the group are not
copied into the spec. (If the key specifies a canonical location, its spec is returned initialized, as in the normal
case where `alias_resolution` is unspecified.) The caller needs to check whether the returned spec is an alias by
looking for an `alias_of` property. If `alias_resolution` is {"display"}, the behavior is the same as for {"none"}
except that if the alias contains a setting `display = true`, the returned key will reflect the canonical location key,
and if the alias contains a setting `display = ``string`` `, the returned key will reflect that string.
This is a low-level function meant for internal use; external callers should generally use `get_matching_location` (for
internally-derived locations), `find_matching_holonym_location` (for externally-derived locations) or
`find_canonical_key` (for known-canonical locations where the placetype isn't known).
]=]
local function find_matching_key_in_group(group, placetypes, key, alias_resolution)
if alias_resolution ~= nil and alias_resolution ~= "none" and alias_resolution ~= "display" and
alias_resolution ~= "all" then
internal_error("Bad value for 'alias_resolution': %s", alias_resolution)
end
local spec = group.data[key]
if not spec then
return nil
end
local function check_correct_placetype(placetype)
if type(placetype) == "table" then
for _, pt in ipairs(placetype) do
if list_or_element_contains(placetypes, pt) then
return true
end
end
return false
else
return list_or_element_contains(placetypes, placetype)
end
end
if spec.alias_of then
local resolved_key = spec.alias_of
local resolved_spec = group.data[resolved_key]
if not resolved_spec then
internal_error("Key %s is an alias of %s, which doesn't exist", key, resolved_key)
elseif resolved_spec.alias_of then
internal_error("Key %s is an alias of %s, which is itself an alias; indirect aliasing not allowed",
key, resolved_key)
end
if alias_resolution == "none" or alias_resolution == "display" then
-- We could be working with non-initialized/defaulted spec, since we're pulling it directly from the group.
local placetype = spec.placetype or resolved_spec.placetype or group.default_placetype
if not placetype then
internal_error("No placetype found for key %s in any of spec %s, alias-resolved spec %s or in group " ..
"`default_placetype`", key, spec, resolved_spec)
end
if not check_correct_placetype(placetype) then
return nil
end
if alias_resolution == "display" then
if spec.display == true then
key = resolved_key
elseif spec.display then
key = spec.display
end
end
return key, spec
end
key = resolved_key
spec = resolved_spec
end
-- We could be working with non-initialized/defaulted spec, since we're pulling it directly from the group.
local placetype = spec.placetype or group.default_placetype
if not placetype then
internal_error("No placetype found for key %s in spec %s or group `default_placetype`", key, spec)
end
if not check_correct_placetype(placetype) then
return nil
end
export.initialize_spec(group, key, spec)
return key, spec
end
--[=[
Given a location group, placename and possible placetypes that the placename must match, check if the placename exists
in the group with at least one of the placetypes of the key in the group that corresponds to the placename matching one
of the passed-in placetypes. If so, return two values: the key corrsponding to the passed-in placename and the
corresponding spec object. This is similar to `find_matching_key_in_group()` but works with placenames rather than keys.
`alias_resolution` is as in `find_matching_key_in_group()`.
This is a low-level function meant for internal use; external callers should generally use `get_matching_location` (for
internally-derived locations), `find_matching_holonym_location` (for externally-derived locations) or
`find_canonical_key` (for known-canonical locations where the placetype isn't known).
]=]
local function find_matching_placename_in_group(group, placetypes, placename, alias_resolution)
local key = export.placename_to_key(group, placename)
return find_matching_key_in_group(group, placetypes, key, alias_resolution)
end
--[==[
If `key` is a canonical known location key (i.e. not an alias), return the corresponding group and initialized spec.
If no such key exists, return {nil}. This throws an internal error if two locations with the same key are found.
]==]
function export.find_canonical_key(key)
local found_locations = {}
for _, group in ipairs(export.locations) do
local spec = group.data[key]
if not spec then
-- do nothing
elseif spec.alias_of then
mw.log(("Skipping alias '%s' of canonical '%s'"):format(key, spec.alias_of))
else
insert(found_locations, {group, spec})
end
end
if not found_locations[1] then
return nil
elseif found_locations[2] then
internal_error("Found multiple matching locations for canonical key %s: %s", key, found_locations)
else
local group, spec = unpack(found_locations[1])
export.initialize_spec(group, key, spec)
return group, spec
end
end
--[==[
Iterator that returns all locations matching a given description, where the description consists of either a placename
or a key along with a list of possible placetypes. Usually there will be at most one such location. The iterator
returns three values at each iteration: the location group, canonical key by which the location is known and the spec
object describing the location. `data` contains the following possible fields:
* `placetypes`: A list of possible placetypes, one of which must match one of the location's placetypes; or a string
specifying a placetype, which must match one of the location's placetypes. This must be specified.
* `placename`: The placename of the location. Either this or `key` must be specified.
* `key`: The key of the location. Either this or `placename` must be specified.
* `alias_resolution`: If specified, it behaves the same as for `find_matching_key_in_group`.
The spec is normally initialized using `initialize_spec()` prior to it being returned (but may not be if
`alias_resolution` is given and the specified key or placename is an alias; see the documentation for
`find_matching_key_in_group`).
]==]
function export.iterate_matching_location(data)
local i = 0
local n = #export.locations
return function()
while true do
i = i + 1
if i > n then
break
end
local group = export.locations[i]
local key, spec
if data.placename then
key, spec = find_matching_placename_in_group(group, data.placetypes, data.placename,
data.alias_resolution)
else
if not data.key then
internal_error("'.placename' or '.key' must be defined: %s", data)
end
key, spec = find_matching_key_in_group(group, data.placetypes, data.key, data.alias_resolution)
end
if key then
return group, key, spec
end
end
end
end
--[==[
Return the location matching a given description, where the description consists of either a placename or a key along
with a list of possible placetypes. This is similar to `iterate_matching_location()` but throws an internal error if
there is not exactly one location found; as such, it is for use with internally specified locations (such as the
containers of known locations) rather than externally specified locations, which may not match a known location and in
some cases may match multiple known locations. For finding an externally specified location, consider using
`find_matching_holonym_location`, which returns {nil} rather than throwing an error if the location isn't found, but
also (more importantly) checks to make sure there are no conflicting holonyms among the user-specified holonyms (e.g.
{{tl|place|city|s/Delaware|c/USA|t=Newark}} will not match the known location `Newark` (in New Jersey, not Delaware).
]==]
function export.get_matching_location(data)
local all_found = {}
for group, key, spec in export.iterate_matching_location(data) do
insert(all_found, {group, key, spec})
end
if not all_found[1] then
internal_error("Couldn't find matching location for data %s", data)
elseif all_found[2] then
internal_error("Found multiple matching locations for data %s: %s", data, all_found)
else
return unpack(all_found[1])
end
end
--[==[
Successively iterate over a location's containers, and then the containers of those containers, etc. Keep in mind that
locations may have multiple containers (e.g. Russia has both Europe and Asia as containers, and both Europe and Asia
have Eurasia as their container). A given container will never be returned twice (e.g. in the case where a specific
location A has locations B and C as containers, and B has C as its container, C will not be returned twice). An
internal error happens if a container loop is detected. The return value is a list of location objects, each of which
contains `group`, `key` and `spec` fields.
]==]
function export.iterate_containers(group, key, spec)
local keys_seen = {}
keys_seen[key] = true
local iterations = 0
local last_iteration_containers = {{group = group, key = key, spec = spec}}
return function()
iterations = iterations + 1
if iterations > 10 then
internal_error("Probable loop in containers when processing key %s", key)
end
local next_iteration_containers = {}
for _, location in ipairs(last_iteration_containers) do
local containers = location.spec.containers
if containers then
for _, container in ipairs(containers) do
local container_group, container_key, container_spec = export.get_matching_location {
placetypes = container.placetype,
key = container.key,
}
if not keys_seen[container_key] then
insert(next_iteration_containers, {
group = container_group, key = container_key, spec = container_spec
})
keys_seen[container_key] = true
end
end
end
end
if not next_iteration_containers[1] then
return nil
end
last_iteration_containers = next_iteration_containers
return next_iteration_containers
end
end
--[==[
Given a placename, convert it into a link (two-part if `display_form` is given and differs from `placename`) and add
`"the "` to the beginning if called for in `spec`.
]==]
function export.construct_linked_placename(spec, placename, display_form)
local linked_placename = display_form and placename ~= display_form and ("[[%s|%s]]"):format(placename,
display_form) or ("[[%s]]"):format(placename)
if spec.the then
linked_placename = "the " .. linked_placename
end
return linked_placename
end
--[=[
This is typically used to define `key_to_placename`. It generates a function that chops off parts of a string (a
location key), typically at the end, in order to get the full and elliptical versions of a placename. (See the
documentation above for `key_to_placename` under "Location group tables" for the difference between full and elliptical
placenames.) `container_patterns` is a Lua pattern or a list of possible patterns matching the container at the end of
the key, which will be used to remove that container. If multiple patterns are specified, each one is tried until one
matches. If `container_patterns` is omitted, this part of the process is skipped. The reulting string becomes the full
placename. If `divtype_patterns` is specified, it is likewise either a Lua pattern or list of possible patterns to match
and remove the political division affixed onto the end (or possibly the beginning) of the key in the keys of certain
countries (such as South Korean and North Korean counties, which include the word "County" in the key). The resulting
chopped string becomes the elliptical placename. If `divtype_patterns` is omitted, this part of the process is skipped
and the full and elliptical placenames are the same.
Typical usage is as follows:
```
key_to_placename = make_key_to_placename(", England$"),
```
or (when the political division is part of the key)
```
key_to_placename = make_key_to_placename(", South Korea$", " County$")
```
]=]
local function make_key_to_placename(container_patterns, divtype_patterns)
if type(container_patterns) == "string" then
container_patterns = {container_patterns}
end
if type(divtype_patterns) == "string" then
divtype_patterns = {divtype_patterns}
end
return function(key)
local full_placename = key
if container_patterns then
for _, container_pattern in ipairs(container_patterns) do
local nsubs
full_placename, nsubs = full_placename:gsub(container_pattern, "")
if nsubs > 0 then
break
end
end
end
local elliptical_placename = full_placename
if divtype_patterns then
for _, divtype_pattern in ipairs(divtype_patterns) do
local nsubs
elliptical_placename, nsubs = elliptical_placename:gsub(divtype_pattern, "")
if nsubs > 0 then
break
end
end
end
return full_placename, elliptical_placename
end
end
--[=[
This is typically used to define `placename_to_key`. It generates a function that appends a string to the end of a given
placename to get the key (see the definition of `placename_to_key` above in the documentation under "Location group
tables"). Optional `divtype_suffix` is a raw string (which should not contain hyphens or other characters that have
special meaning in Lua patterns) to be appended first to the placename; if already present at the end, it is not
appended. `container_suffix` is then added in the same fashion if given. Typical usage is like this:
```
placename_to_key = make_placename_to_key(", England")
```
(which will convert e.g. `"Hampshire"` into `"Hampshire, England"`)
or
```
placename_to_key = make_placename_to_key(", South Korea", " County")
```
(which will convert e.g. `"Gangwon"` or `"Gangwon County"` into `"Gangwon County, South Korea"`).
]=]
local function make_placename_to_key(container_suffix, divtype_suffix)
return function(placename)
local key = placename
if divtype_suffix then
if not key:find(divtype_suffix .. "$") then
key = key .. divtype_suffix
end
end
if container_suffix then
key = key .. container_suffix
end
return key
end
end
--[=[
This is typically used to define `canonicalize_key_container`, which converts a container as specified in the location
data into the canonical form containing both the full container key and its placetype. It generates a function to do
the canonicalization of a given container. If the container is a string, `suffix` is appended onto the string (use {nil}
or {""} if there is no suffix to append), and the placetype is set to `placetype`. Otherwise the container is left
as-is. Typical usage is like this:
```
canonicalize_key_container = make_canonicalize_key_container(", Canada", "province")
```
which will convert e.g. `"Ontario"` into `{key = "Ontario, Canada", placetype = "province"}`.
]=]
local function make_canonicalize_key_container(suffix, placetype)
return function(container)
if type(container) == "string" then
return {key = container .. (suffix or ""), placetype = placetype}
else
return container
end
end
end
-----------------------------------------------------------------------------------
-- Top-level tables --
-----------------------------------------------------------------------------------
export.continents = {
["Earth"] = {the = true, placetype = "planet", addl_parents = {"nature"},
fulldesc = "=the planet [[Earth]] and the features found on it"},
["Africa"] = {placetype = "continent", container = {key = "Earth", placetype = "planet"}},
["America"] = {placetype = {"supercontinent", "continent"}, container = {key = "Earth", placetype = "planet"},
keydesc = "[[America]], in the sense of [[North America]] and [[South America]] combined",
wp = "Americas"},
["Americas"] = {alias_of = "America", the = true},
["North America"] = {placetype = "continent", container = {key = "America", placetype = "supercontinent"}},
["Caribbean"] = {the = true, placetype = {"continental region", "region"}, container = {key = "North America", placetype = "continent"}},
["Central America"] = {placetype = {"continental region", "region"}, container = {key = "North America", placetype = "continent"}},
["South America"] = {placetype = "continent", container = {key = "America", placetype = "supercontinent"}},
["Antarctica"] = {placetype = "continent", container = {key = "Earth", placetype = "planet"},
fulldesc = "=the territory of [[Antarctica]]"},
["Eurasia"] = {placetype = {"supercontinent", "continent"}, container = {key = "Earth", placetype = "planet"},
keydesc = "[[Eurasia]], i.e. [[Europe]] and [[Asia]] together"},
["Asia"] = {placetype = "continent", container = {key = "Eurasia", placetype = "supercontinent"}},
["Europe"] = {placetype = "continent", container = {key = "Eurasia", placetype = "supercontinent"}},
["Oceania"] = {placetype = "continent", container = {key = "Earth", placetype = "planet"}},
["Melanesia"] = {placetype = {"continental region", "region"}, container = {key = "Oceania", placetype = "continent"}},
["Micronesia"] = {placetype = {"continental region", "region"}, container = {key = "Oceania", placetype = "continent"}},
["Polynesia"] = {placetype = {"continental region", "region"}, container = {key = "Oceania", placetype = "continent"}},
}
export.continents_group = {
default_overriding_bare_label_parents = {}, -- container parents should be used
default_divs = {{type = "countries", prep = "in"}},
-- It's enough to mention the first-level continent or continent group. It seems excessive to write e.g.
-- "El Salvador, a country in Central America, a continental region in North America, a continent in America, ...".
default_no_include_container_in_desc = true,
default_no_container_cat = true,
default_no_container_parent = true,
default_no_auto_augment_container = true,
default_no_generic_place_cat = true,
-- French Guyana is in France but not in Europe, which should not be an issue, so don't check holonym mismatches at
-- this level. We also run into problems with supercontinents, which have "continent" as the fallback and cause
-- mismatches.
default_no_check_holonym_mismatch = true,
data = export.continents,
}
-- Countries: including those with partial recognition that are normally considered countries (e.g. Kosovo, Taiwan).
export.countries = {
["Afghanistan"] = {container = "Asia", divs = {"provinces", "districts"}},
["Albania"] = {container = "Europe", divs = {"counties", "municipalities", "communes",
{type = "administrative units", cat_as = "communes"},
}, british_spelling = true},
["Algeria"] = {container = "Africa", divs = {"provinces", "communes", "districts", "municipalities"}},
["Andorra"] = {container = "Europe", divs = {"parishes"}, british_spelling = true},
["Angola"] = {container = "Africa", divs = {"provinces", "municipalities"}},
["Antigua and Barbuda"] = {container = "Caribbean", divs = {"provinces"}, british_spelling = true},
["Argentina"] = {container = "South America", divs = {"provinces", "departments", "municipalities"}},
["Armenia"] = {container = {"Europe", "Asia"}, divs = {"provinces", "districts", "municipalities"},
british_spelling = true},
["Republic of Armenia"] = {alias_of = "Armenia", the = true}, -- differs in "the"
-- Both a country and continent
["Australia"] = {container = "Oceania", divs = {
{type = "states", cat_as = "states and territories"},
{type = "territories", cat_as = "states and territories"},
{type = "ABBREVIATION_OF states", cat_as = "abbreviations of states and territories"},
{type = "ABBREVIATION_OF territories", cat_as = "abbreviations of states and territories"},
"local government areas", "dependent territories",
}, british_spelling = true},
["Austria"] = {container = "Europe", divs = {"states", "districts", "municipalities"}, british_spelling = true},
["Azerbaijan"] = {container = {"Europe", "Asia"}, divs = {"districts", "municipalities"}, british_spelling = true},
["Bahamas"] = {the = true, container = "Caribbean", divs = {"districts"}, british_spelling = true, wp = "The %l"},
["Bahrain"] = {container = "Asia", divs = {"governorates"}},
["Bangladesh"] = {container = "Asia", divs = {"divisions", "districts", "municipalities"}, british_spelling = true},
["Barbados"] = {container = "Caribbean", divs = {"parishes"}, british_spelling = true},
["Belarus"] = {container = "Europe", divs = {"regions", "districts"}, british_spelling = true},
["Belgium"] = {container = "Europe", divs = {"regions", "provinces", "municipalities"}, british_spelling = true},
["Belize"] = {container = "Central America", divs = {"districts"}, british_spelling = true},
["Benin"] = {container = "Africa", divs = {"departments", "communes"}},
["Bhutan"] = {container = "Asia", divs = {"districts", "gewogs", "chiwogs"}},
["Bolivia"] = {container = "South America", divs = {"provinces", "departments", "municipalities"}},
["Bosnia and Herzegovina"] = {container = "Europe", divs = {"entities", "cantons", "municipalities"}, british_spelling = true},
["Bosnia and Hercegovina"] = {alias_of = "Bosnia and Herzegovina", display = true},
["Bosnia-Herzegovina"] = {alias_of = "Bosnia and Herzegovina", display = true},
["Bosnia-Hercegovina"] = {alias_of = "Bosnia and Herzegovina", display = true},
["Bosnia"] = {alias_of = "Bosnia and Herzegovina", display = true},
["Botswana"] = {container = "Africa", divs = {"districts", "subdistricts"}, british_spelling = true},
["Brazil"] = {container = "South America", divs = {
"states", "municipalities", "macroregions",
{type = "ABBREVIATION_OF states", cat_as = "abbreviations of states"},
}},
["Brunei"] = {container = "Asia", divs = {"districts", "mukims"}, british_spelling = true},
["Bulgaria"] = {container = "Europe", divs = {"provinces", "municipalities"}, british_spelling = true},
["Burkina Faso"] = {container = "Africa", divs = {"regions", "departments", "provinces"}},
["Burundi"] = {container = "Africa", divs = {"provinces", "communes"}},
["Cambodia"] = {container = "Asia", divs = {"provinces", "districts"}},
["Cameroon"] = {container = "Africa", divs = {"regions", "departments"}},
["Canada"] = {container = "North America", divs = {
{type = "provinces", cat_as = "provinces and territories"},
{type = "territories", cat_as = "provinces and territories"},
{type = "ABBREVIATION_OF provinces", cat_as = "abbreviations of provinces and territories"},
{type = "ABBREVIATION_OF territories", cat_as = "abbreviations of provinces and territories"},
"counties", "districts", "municipalities", "regional municipalities",
"rural municipalities", "parishes",
-- Don't change the following to something more politically correct (e.g. "First Nations reserves") until/unless
-- the Canadian government makes a similar switch (and note that as of Apr 18 2025, the Wikipedia article is
-- still at [[w:Indian reserves]]).
"Indian reserves",
"census divisions",
{type = "townships", prep = "in"},
},
british_spelling = true},
["Cape Verde"] = {container = "Africa", divs = {"municipalities", "parishes"}},
["Cabo Verde"] = {alias_of = "Cape Verde", display = true},
["Central African Republic"] = {the = true, container = "Africa", divs = {"prefectures", "subprefectures"}},
["CAR"] = {alias_of = "Central African Republic", display = true, the = true},
["C.A.R"] = {alias_of = "Central African Republic", display = true, the = true},
["Chad"] = {container = "Africa", divs = {"regions", "departments"}},
["Chile"] = {container = "South America", divs = {"regions", "provinces", "communes"}},
["China"] = {container = "Asia", divs = {
{type = "provinces", cat_as = "provinces and autonomous regions"},
{type = "autonomous regions", cat_as = "provinces and autonomous regions"},
{type = "FORMER provinces", cat_as = "former provinces"},
"special administrative regions",
"prefectures",
{type = "FORMER prefectures", cat_as = "former prefectures"},
"prefecture-level cities",
{type = "counties", cat_as = "counties and county-level cities"},
{type = "county-level cities", cat_as = "counties and county-level cities"},
{type = "FORMER counties", cat_as = "former counties and county-level cities"},
{type = "FORMER county-level cities", cat_as = "former counties and county-level cities"},
-- "towns" (but not "townships") are automatically added as they are specified as generic_before_non_cities.
"districts",
{type = "FORMER districts", cat_as = "former districts"},
"subdistricts",
"townships",
"municipalities",
{type = "direct-administered municipalities", cat_as = "municipalities"},
}},
["People's Republic of China"] = {alias_of = "China", the = true}, -- differs in "the"
["Colombia"] = {container = "South America", divs = {"departments", "municipalities"}},
["Comoros"] = {the = true, container = "Africa", divs = {"autonomous islands"}},
["Costa Rica"] = {container = "Central America", divs = {"provinces", "cantons"}},
["Croatia"] = {container = "Europe", divs = {"counties", "municipalities"}, british_spelling = true},
["Cuba"] = {container = "Caribbean", divs = {"provinces", "municipalities"}},
["Cyprus"] = {container = {"Europe", "Asia"}, divs = {"districts"}, british_spelling = true},
["Czech Republic"] = {the = true, container = "Europe", divs = {"regions", "districts", "municipalities"}, british_spelling = true},
["Czechia"] = {alias_of = "Czech Republic"}, -- differs in "the"
["Democratic Republic of the Congo"] = {the = true, container = "Africa", divs = {"provinces", "territories"}},
["Congo"] = {alias_of = "Democratic Republic of the Congo", display = true, the = true},
["DRC"] = {alias_of = "Democratic Republic of the Congo", display = true, the = true},
["D.R.C"] = {alias_of = "Democratic Republic of the Congo", display = true, the = true},
["Denmark"] = {container = "Europe", divs = {"regions", "municipalities", "dependent territories"},
british_spelling = true,
-- Wikipedia separates [[w:Denmark]] (constituent country) from [[w:Danish Realm]] (country)
},
["Djibouti"] = {container = "Africa", divs = {"regions", "districts"}},
["Dominica"] = {container = "Caribbean", divs = {"parishes"}, british_spelling = true},
["Dominican Republic"] = {the = true, container = "Caribbean", divs = {"provinces", "municipalities"},
keydesc = "the [[Dominican Republic]], the country that shares the [[Caribbean]] island of [[Hispaniola]] with [[Haiti]]"},
["East Timor"] = {container = "Asia", divs = {"municipalities"}, wp = "Timor-Leste"},
["Timor-Leste"] = {alias_of = "East Timor", display = true},
["Ecuador"] = {container = "South America", divs = {"provinces", "cantons"}},
["Egypt"] = {container = "Africa", divs = {"governorates", "regions"}, british_spelling = true},
["El Salvador"] = {container = "Central America", divs = {"departments", "municipalities"}},
["Equatorial Guinea"] = {container = "Africa", divs = {"provinces"}},
["Eritrea"] = {container = "Africa", divs = {"regions", "subregions"}},
["Estonia"] = {container = "Europe", divs = {"counties", "municipalities"}, british_spelling = true},
["Eswatini"] = {container = "Africa", british_spelling = true},
["Swaziland"] = {alias_of = "Eswatini", display = true},
["Ethiopia"] = {container = "Africa", divs = {"regions", "zones"}},
["Federated States of Micronesia"] = {the = true, container = "Micronesia", divs = {"states"}},
["Micronesia"] = {alias_of = "Federated States of Micronesia"},
["Fiji"] = {container = "Melanesia", divs = {"divisions", "provinces"}, british_spelling = true},
["Finland"] = {container = "Europe", divs = {"regions", "municipalities"}, british_spelling = true},
["France"] = {container = "Europe", divs = {"regions", "cantons", "collectivities",
"communes",
{type = "municipalities", cat_as = "communes"},
"departments",
{type = "prefectures", cat_as = {"prefectures", "departmental capitals"}},
{type = "French prefectures", cat_as = {"prefectures", "departmental capitals"}},
"dependent territories", "territories", "provinces",
}, british_spelling = true},
["Gabon"] = {container = "Africa", divs = {"provinces", "departments"}},
["Gambia"] = {the = true, container = "Africa", divs = {"divisions", "districts"}, british_spelling = true, wp = "The %l"},
["Georgia"] = {container = {"Europe", "Asia"}, divs = {"regions", "districts"},
keydesc = "the country of [[Georgia]], in [[Eurasia]]", british_spelling = true, wp = "%l (country)"},
["Germany"] = {container = "Europe", divs = {
"states",
-- Bavaria, Baden-Württemberg, Hesse and North Rhine-Westphalia have administrative regions as divisions, but
-- there aren't really enough of them to categorize per state.
"regions",
"municipalities", "districts"}, british_spelling = true},
["Ghana"] = {container = "Africa", divs = {"regions", "districts"}, british_spelling = true},
["Greece"] = {container = "Europe", divs = {"regions", "regional units", "municipalities",
{type = "peripheries", cat_as = {"regions"}},
}, british_spelling = true},
["Grenada"] = {container = "Caribbean", divs = {"parishes"}, british_spelling = true},
["Guatemala"] = {container = "Central America", divs = {"departments", "municipalities"}},
["Guinea"] = {container = "Africa", divs = {"regions", "prefectures"}},
["Guinea-Bissau"] = {container = "Africa", divs = {"regions"}},
["Guyana"] = {container = "South America", divs = {"regions"}, british_spelling = true},
["Haiti"] = {container = "Caribbean", divs = {"departments", "arrondissements"}},
["Honduras"] = {container = "Central America", divs = {"departments", "municipalities"}},
["Hungary"] = {container = "Europe", divs = {"counties", "districts"}, british_spelling = true},
["Iceland"] = {container = "Europe", divs = {"regions", "municipalities", "counties"}, british_spelling = true},
["India"] = {container = "Asia", divs = {
{type = "states", cat_as = "states and union territories"},
{type = "union territories", cat_as = "states and union territories"},
{type = "ABBREVIATION_OF states", cat_as = "abbreviations of states and union territories"},
{type = "ABBREVIATION_OF union territories", cat_as = "abbreviations of states and union territories"},
"divisions", "districts", "municipalities",
}, british_spelling = true},
["Indonesia"] = {container = "Asia", divs = {"regencies", "provinces",
{type = "ABBREVIATION_OF provinces", cat_as = "abbreviations of provinces"},
}},
["Iran"] = {container = "Asia", divs = {"provinces", "counties"}},
["Iraq"] = {container = "Asia", divs = {"governorates", "districts"}},
["Ireland"] = {container = "Europe", addl_parents = {"British Isles"},
divs = {"counties", "districts", "provinces"}, british_spelling = true, wp = "Republic of %l"},
["Republic of Ireland"] = {alias_of = "Ireland", the = true}, -- differs in "the"
["Israel"] = {container = "Asia", divs = {"districts"}},
["Italy"] = {container = "Europe", divs = {
"regions", "provinces", "metropolitan cities", "municipalities",
{type = "autonomous regions", cat_as = "regions"},
}, british_spelling = true},
["Ivory Coast"] = {container = "Africa", divs = {"districts", "regions"}},
-- We should really be using Ivory Coast (common name) but there are political ramifications to the use of
-- Côte d'Ivoire so don't make it a display alias.
["Côte d'Ivoire"] = {alias_of = "Ivory Coast"},
["Jamaica"] = {container = "Caribbean", divs = {"parishes"}, british_spelling = true},
["Japan"] = {container = "Asia", divs = {"prefectures", "subprefectures", "municipalities"}},
["Jordan"] = {container = "Asia", divs = {"governorates"}},
["Kazakhstan"] = {container = {"Asia", "Europe"}, divs = {"regions", "districts"}},
["Kenya"] = {container = "Africa", divs = {"counties"}, british_spelling = true},
["Kiribati"] = {container = "Micronesia", british_spelling = true},
["Kosovo"] = {container = "Europe", divs = {"districts", "municipalities"}, british_spelling = true},
["Kuwait"] = {container = "Asia", divs = {"governorates", "areas"}},
["Kyrgyzstan"] = {container = "Asia", divs = {"regions", "districts"}},
["Laos"] = {container = "Asia", divs = {"provinces", "districts"}},
["Latvia"] = {container = "Europe", divs = {"municipalities"}, british_spelling = true},
["Lebanon"] = {container = "Asia", divs = {"governorates", "districts"}},
["Lesotho"] = {container = "Africa", divs = {"districts"}, british_spelling = true},
["Liberia"] = {container = "Africa", divs = {"counties", "districts"}},
["Libya"] = {container = "Africa", divs = {"districts", "municipalities"}},
["Liechtenstein"] = {container = "Europe", divs = {"municipalities"}, british_spelling = true},
["Lithuania"] = {container = "Europe", divs = {"counties", "municipalities"}, british_spelling = true},
["Luxembourg"] = {container = "Europe", divs = {"cantons", "districts"}, british_spelling = true},
["Madagascar"] = {container = "Africa", divs = {"regions", "districts"}},
["Malawi"] = {container = "Africa", divs = {"regions", "districts"}, british_spelling = true},
["Malaysia"] = {container = "Asia", divs = {"states", "federal territories", "districts"}, british_spelling = true},
["Maldives"] = {the = true, container = "Asia", divs = {"provinces", "administrative atolls"}, british_spelling = true},
["Mali"] = {container = "Africa", divs = {"regions", "cercles"}},
["Malta"] = {container = "Europe", divs = {"regions", "local councils"}, british_spelling = true},
["Marshall Islands"] = {the = true, container = "Micronesia", divs = {"municipalities"}},
["Mauritania"] = {container = "Africa", divs = {"regions", "departments"}},
["Mauritius"] = {container = "Africa", divs = {"districts"}, british_spelling = true},
["Mexico"] = {container = "North America", addl_parents = {"Central America"}, divs = {
"states", "municipalities",
{type = "ABBREVIATION_OF states", cat_as = "abbreviations of states"},
}},
["Moldova"] = {container = "Europe", divs = {
{type = "districts", cat_as = "districts and autonomous territorial units"},
{type = "autonomous territorial units", cat_as = "districts and autonomous territorial units"},
"communes", "municipalities",
}, british_spelling = true},
["Monaco"] = {placetype = {"city-state", "country"}, container = "Europe",
-- We want the first placetype to be 'city-state' so the description of Monaco says it's a city-state, but we
-- want its parent to be "countries in Europe".
bare_category_parent_type = {type = "countries", prep = "in"},
is_city = true, british_spelling = true},
["Mongolia"] = {container = "Asia", divs = {"provinces", "districts"}},
["Montenegro"] = {container = "Europe", divs = {"municipalities"}},
["Morocco"] = {container = "Africa", divs = {"regions", "prefectures", "provinces"}},
["Mozambique"] = {container = "Africa", divs = {"provinces", "districts"}},
["Myanmar"] = {container = "Asia",
divs = {"regions", "states", "union territories",
{type = "self-administered zones", cat_as = "self-administered areas"},
{type = "self-administered divisions", cat_as = "self-administered areas"},
"districts"}},
["Burma"] = {alias_of = "Myanmar"}, -- not display-canonicalizing; has political connotations
["Namibia"] = {container = "Africa", divs = {"regions", "constituencies"}, british_spelling = true},
["Nauru"] = {container = "Micronesia", divs = {"districts"}, british_spelling = true},
["Nepal"] = {container = "Asia", divs = {"provinces", "districts"}},
["Netherlands"] = {the = true, placetype = {"country", "constituent country"}, container = "Europe",
divs = {"provinces", "municipalities",
{type = "FORMER municipalities", cat_as = "former municipalities"},
"dependent territories", "constituent countries"}, british_spelling = true,
-- Wikipedia separates [[w:Netherlands]] (constituent country) from [[w:Kingdom of the Netherlands]]
-- (country)
},
["New Zealand"] = {container = "Polynesia", divs = {
"regions", "dependent territories", "territorial authorities",
{type = "districts", cat_as = "territorial authorities"},
},
british_spelling = true},
["Nicaragua"] = {container = "Central America", divs = {"departments", "municipalities"}},
["Niger"] = {container = "Africa", divs = {"regions", "departments"}},
["Nigeria"] = {container = "Africa", divs = {
"states",
-- Categorize the Federal Capital Territory as a state because there's only one of it; we could categorize
-- everything under 'states and territories' but that seems a bit pointless.
{type = "federal territories", cat_as = "states"},
"local government areas",
}, british_spelling = true},
["North Korea"] = {container = "Asia", addl_parents = {"Korea"}, divs = {"provinces", "counties"}},
["North Macedonia"] = {container = "Europe", divs = {"regions", "municipalities"}, british_spelling = true},
["Macedonia"] = {alias_of = "North Macedonia", display = true},
["Republic of North Macedonia"] = {alias_of = "North Macedonia", the = true}, -- differs in "the"
["Republic of Macedonia"] = {alias_of = "North Macedonia", the = true}, -- differs in "the"
["Norway"] = {container = "Europe",
divs = {"counties", "municipalities", "dependent territories", "districts", "unincorporated areas"},
british_spelling = true},
["Oman"] = {container = "Asia", divs = {"governorates", "provinces"}},
["Pakistan"] = {container = "Asia", divs = {
{type = "provinces", cat_as = "provinces and territories"},
{type = "administrative territories", cat_as = "provinces and territories"},
{type = "federal territories", cat_as = "provinces and territories"},
{type = "territories", cat_as = "provinces and territories"},
"divisions", "districts",
}, british_spelling = true},
["Palau"] = {container = "Micronesia", divs = {"states"}},
["Palestine"] = {container = "Asia", divs = {"governorates"}},
["State of Palestine"] = {alias_of = "Palestine", the = true}, -- differs in "the"
["Panama"] = {container = "Central America", divs = {"provinces", "districts"}},
["Papua New Guinea"] = {container = "Melanesia", divs = {"provinces", "districts"}, british_spelling = true},
["Paraguay"] = {container = "South America", divs = {"departments", "districts"}},
["Peru"] = {container = "South America", divs = {"regions", "provinces", "districts"}},
["Philippines"] = {the = true, container = "Asia", divs = {"regions", "provinces", "districts", "municipalities", "barangays"}},
["Poland"] = {divs = {"voivodeships", "counties",
{type = "Polish colonies", cat_as = {{type = "villages", prep = "in"}}},
}, container = "Europe", british_spelling = true},
["Portugal"] = {container = "Europe", divs = {
{type = "autonomous regions", cat_as = "districts and autonomous regions"},
{type = "districts", cat_as = "districts and autonomous regions"},
"provinces", "municipalities"}, british_spelling = true},
["Qatar"] = {container = "Asia", divs = {"municipalities", "zones"}},
["Republic of the Congo"] = {the = true, container = "Africa", divs = {"departments", "districts"}},
["Congo Republic"] = {alias_of = "Republic of the Congo", display = true, the = true},
["Romania"] = {container = "Europe", divs = {
"regions", "counties", "communes",
{type = "ABBREVIATION_OF counties", cat_as = "abbreviations of counties"},
}, british_spelling = true},
["Russia"] = {container = {"Europe", "Asia"}, divs = {
"federal subjects", "republics", "autonomous oblasts", "autonomous okrugs", "oblasts", "krais", "federal cities",
"districts", "federal districts"},
british_spelling = true},
["Rwanda"] = {container = "Africa", divs = {"provinces", "districts"}},
["Saint Kitts and Nevis"] = {container = "Caribbean", divs = {"parishes"}, british_spelling = true},
["Saint Kitts"] = {alias_of = "Saint Kitts and Nevis", display = true},
["Saint Lucia"] = {container = "Caribbean", divs = {"districts"}, british_spelling = true},
["Saint Vincent and the Grenadines"] = {container = "Caribbean", divs = {"parishes"}, british_spelling = true},
["Saint Vincent"] = {alias_of = "Saint Vincent and the Grenadines", display = true},
["SVG"] = {alias_of = "Saint Vincent and the Grenadines", display = true},
["S.V.G"] = {alias_of = "Saint Vincent and the Grenadines", display = true},
["Samoa"] = {container = "Polynesia", divs = {"districts"}, british_spelling = true},
["San Marino"] = {container = "Europe", divs = {"municipalities"}, british_spelling = true},
["São Tomé and Príncipe"] = {container = "Africa", divs = {"districts"}},
["São Tome and Principe"] = {alias_of = "São Tomé and Príncipe", display = true},
["São Tomé"] = {alias_of = "São Tomé and Príncipe", display = true},
["São Tome"] = {alias_of = "São Tomé and Príncipe", display = true},
["Saudi Arabia"] = {container = "Asia", divs = {"provinces", "governorates"}},
["Senegal"] = {container = "Africa", divs = {"regions", "departments"}},
["Serbia"] = {container = "Europe", divs = {"districts", "municipalities", "autonomous provinces"}},
["Seychelles"] = {container = "Africa", divs = {"districts"}, british_spelling = true},
["Sierra Leone"] = {container = "Africa", divs = {"provinces", "districts"}, british_spelling = true},
["Singapore"] = {container = "Asia", divs = {"districts", "regions"}, british_spelling = true},
["Slovakia"] = {container = "Europe", divs = {"regions", "districts"}, british_spelling = true},
["Slovenia"] = {container = "Europe", divs = {"statistical regions", "municipalities"}, british_spelling = true},
-- Note: While the official name does not include "the" at the beginning,
-- it sounds strange in English to leave it out and it's commonly included.
["Solomon Islands"] = {the = true, container = "Melanesia", divs = {"provinces"}, british_spelling = true},
["Somalia"] = {container = "Africa", divs = {"regions", "districts"}},
["South Africa"] = {container = "Africa", divs = {
"provinces",
"districts",
{type = "district municipalities", cat_as = "districts"},
{type = "metropolitan municipalities", cat_as = "districts"},
"municipalities",
}, british_spelling = true},
["South Korea"] = {container = "Asia", addl_parents = {"Korea"}, divs = {"provinces", "counties", "districts"}},
["South Sudan"] = {container = "Africa", divs = {"regions", "states", "counties"}, british_spelling = true},
["Spain"] = {container = "Europe", divs = {"autonomous communities", "provinces", "municipalities",
"comarcas", "autonomous cities"},
british_spelling = true},
["Sri Lanka"] = {container = "Asia", divs = {"provinces", "districts"}, british_spelling = true},
["Sudan"] = {container = "Africa", divs = {"states", "districts"}, british_spelling = true},
["Suriname"] = {container = "South America", divs = {"districts"}},
["Sweden"] = {container = "Europe", divs = {"provinces", "counties", "municipalities"}, british_spelling = true},
["Switzerland"] = {container = "Europe", divs = {"cantons", "municipalities", "districts"}, british_spelling = true},
["Syria"] = {container = "Asia", divs = {"governorates", "districts"}},
["Taiwan"] = {container = "Asia", divs = {"counties", "districts", "townships", "special municipalities"}},
["Republic of China"] = {alias_of = "Taiwan", the = true}, -- differs in "the", different political connotations
["Tajikistan"] = {container = "Asia", divs = {"regions", "districts"}},
["Tanzania"] = {container = "Africa", divs = {"regions", "districts"}, british_spelling = true},
["Thailand"] = {container = "Asia", divs = {"provinces", "districts", "subdistricts"}},
["Togo"] = {container = "Africa", divs = {"provinces", "prefectures"}},
["Tonga"] = {container = "Polynesia", divs = {"divisions"}, british_spelling = true},
["Trinidad and Tobago"] = {container = "Caribbean", divs = {"regions", "municipalities"}, british_spelling = true},
["Tunisia"] = {container = "Africa", divs = {"governorates", "delegations"}},
["Turkey"] = {container = {"Europe", "Asia"}, divs = {"provinces", "districts"}},
-- Foreign names generally get display-canonicalized.
["Türkiye"] = {alias_of = "Turkey", display = true},
["Turkmenistan"] = {container = "Asia", divs = {
-- The 5 regions are often also called provinces
"regions", {type = "provinces", cat_as = "regions"}, "districts"},
},
["Tuvalu"] = {container = "Polynesia", divs = {"atolls"}, british_spelling = true},
["Uganda"] = {container = "Africa", divs = {"districts", "counties"}, british_spelling = true},
["Ukraine"] = {container = "Europe", divs = {
{type = "oblasts", cat_as = "oblasts and autonomous republics"},
{type = "autonomous republics", cat_as = "oblasts and autonomous republics"},
"raions", "hromadas",
}, british_spelling = true},
["United Arab Emirates"] = {the = true, container = "Asia", divs = {"emirates"}},
-- Abbreviations get display-canonicalized.
["UAE"] = {alias_of = "United Arab Emirates", display = true, the = true},
["U.A.E."] = {alias_of = "United Arab Emirates", display = true, the = true},
["United Kingdom"] = {the = true, container = "Europe", addl_parents = {"British Isles"},
divs = {"constituent countries", "counties", "districts", "boroughs", "territories", "dependent territories",
"traditional counties"},
keydesc = "the [[United Kingdom]] of Great Britain and Northern Ireland", british_spelling = true},
-- Abbreviations get display-canonicalized.
["UK"] = {alias_of = "United Kingdom", display = true, the = true},
["U.K."] = {alias_of = "United Kingdom", display = true, the = true},
["United States"] = {the = true, container = "North America",
divs = {"counties", "county seats", "states", "territories", "dependent territories",
{type = "ABBREVIATION_OF states", cat_as = "abbreviations of states"},
{type = "DEROGATORY_NAME_FOR states", cat_as = "derogatory names for states"},
{type = "NICKNAME_FOR states", cat_as = "nicknames for states"},
{type = "OFFICIAL_NICKNAME_FOR states", cat_as = "official nicknames for states"},
{type = "boroughs", prep = "in"}, -- exist in Pennsylvania and New Jersey
"municipalities", -- these exist politically at least in Colorado and Connecticut
{type = "census-designated places", prep = "in"},
{type = "unincorporated communities", prep = "in"},
-- Don't change the following to something more politically correct until/unless the US government makes a
-- similar switch (and note that as of Apr 18 2025, the Wikipedia article is still at
-- [[w:Indian reservations]]).
"Indian reservations",
}},
-- Abbreviations and long forms (when possible) get display-canonicalized.
["US"] = {alias_of = "United States", display = true, the = true},
["U.S."] = {alias_of = "United States", display = true, the = true},
["USA"] = {alias_of = "United States", display = true, the = true},
["U.S.A."] = {alias_of = "United States", display = true, the = true},
["United States of America"] = {alias_of = "United States", display = true, the = true},
["Uruguay"] = {container = "South America", divs = {"departments", "municipalities"}},
["Uzbekistan"] = {container = "Asia", divs = {"regions", "districts"}},
["Vanuatu"] = {container = "Melanesia", divs = {"provinces"}, british_spelling = true},
["Vatican City"] = {placetype = {"city-state", "country"}, container = "Europe",
-- First placetype should be 'city-state' for to shown up in its description,
-- Its parent should still be "countries in Europe".
bare_category_parent_type = {type = "countries", prep = "in"},
addl_parents = {"Rome"}, is_city = true, british_spelling = true},
["Vatican"] = {alias_of = "Vatican City", the = true}, -- differs in "the"
["Venezuela"] = {container = "South America", divs = {"states", "municipalities"}},
["Vietnam"] = {container = "Asia", divs = {"provinces", "districts", "municipalities"}},
["Western Sahara"] = {placetype = {"territory", "country"}, container = "Africa",
bare_category_parent_type = {type = "countries", prep = "in"},
},
-- Not display-canonicalizable both due to differences in 'the' and the sovereignty dispute over Western Sahara
["Sahrawi Arab Democratic Republic"] = {alias_of = "Western Sahara", the = true},
["SADR"] = {alias_of = "Sahrawi Arab Democratic Republic", display = true, the = true},
["Yemen"] = {container = "Asia", divs = {"governorates", "districts"}},
["Zambia"] = {container = "Africa", divs = {"provinces", "districts"}, british_spelling = true},
["Zimbabwe"] = {container = "Africa", divs = {"provinces", "districts"}, british_spelling = true},
}
local function canonicalize_continent_container(key)
if type(key) ~= "string" then
return key
end
if export.continents[key] then
return {key = key, placetype = export.continents[key].placetype}
end
internal_error("Unrecognized key %s in `canonicalize_continent_like`", key)
end
export.countries_group = {
canonicalize_key_container = canonicalize_continent_container,
default_overriding_bare_label_parents = {"+++", "countries"},
default_placetype = "country",
default_no_container_cat = true,
default_no_container_parent = true,
-- No need to augment country holonyms with continents; not needed for disambiguation.
default_no_auto_augment_container = true,
data = export.countries,
}
-- Country-like entities: typically overseas territories or de-facto independent countries, which in both cases
-- are not internationally recognized as sovereign nations but which we treat similarly to countries.
export.country_like_entities = {
-- British Overseas Territory
["Akrotiri and Dhekelia"] = {
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"Cyprus", "Europe", "Asia"},
british_spelling = true,
},
-- Åland: Listed as a region of Finland. Wikipedia lists this under "dependent territories" in
-- [[w:List of sovereign states and dependent territories by continent]].
-- unincorporated territory of the United States
["American Samoa"] = {
placetype = {"unincorporated territory", "overseas territory", "territory"},
container = "United States",
addl_parents = {"Polynesia"},
},
-- British Overseas Territory
["Anguilla"] = {
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"Caribbean"},
british_spelling = true,
},
-- de-facto independent state, internationally recognized as part of Georgia
["Abkhazia"] = {
placetype = {"unrecognized country", "country"},
addl_parents = {"Georgia", "Europe", "Asia"},
divs = {"districts"},
keydesc = "the de-facto independent state of [[Abkhazia]], internationally recognized as part of the country of [[Georgia]]",
british_spelling = true,
},
-- Australian external territory
["Ashmore and Cartier Islands"] = {
the = true,
placetype = {"external territory", "territory"},
container = "Australia",
addl_parents = {"Asia"},
},
-- constituent country of the Netherlands
["Aruba"] = {
placetype = {"constituent country", "country"},
container = "Netherlands",
addl_parents = {"Caribbean"},
british_spelling = true,
},
-- British Overseas Territory
["Bermuda"] = {
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"North America"},
british_spelling = true,
},
-- special municipality of the Netherlands
["Bonaire"] = {
placetype = {"special municipality", "municipality", "overseas territory", "territory"},
container = "Netherlands",
addl_parents = {"Caribbean"},
is_city = true,
british_spelling = true,
},
-- British Overseas Territory
["British Indian Ocean Territory"] = {
the = true,
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"Asia"},
british_spelling = true,
},
-- British Overseas Territory
["British Virgin Islands"] = {
the = true,
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"Caribbean"},
british_spelling = true,
},
-- Norwegian dependent territory
["Bouvet Island"] = {
placetype = {"dependent territory", "territory"},
container = "Norway",
addl_parents = {"Africa"},
british_spelling = true,
},
-- British Overseas Territory
["Cayman Islands"] = {
the = true,
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"Caribbean"},
british_spelling = true,
},
-- Australian external territory
["Christmas Island"] = {
placetype = {"external territory", "territory"},
container = "Australia",
addl_parents = {"Asia"},
british_spelling = true,
},
-- Sui generis French "state private property" per Wikipedia; classify as overseas territory like the
-- French Southern and Antarctic Lands.
["Clipperton Island"] = {
placetype = {"overseas territory", "territory"},
container = "France",
addl_parents = {"North America"},
},
-- Australian external territory; also called the Keeling Islands or (officially) the Cocos (Keeling) Islands
["Cocos Islands"] = {
the = true,
placetype = {"external territory", "territory"},
container = "Australia",
addl_parents = {"Asia"},
wp = "Cocos (Keeling) Islands",
british_spelling = true,
},
["Cocos (Keeling) Islands"] = {alias_of = "Cocos Islands", display = true, the = true},
["Keeling Islands"] = {alias_of = "Cocos Islands", display = true, the = true},
-- self-governing but in free association with New Zealand
["Cook Islands"] = {
the = true,
placetype = {"country"},
container = "New Zealand",
addl_parents = {"Polynesia"},
british_spelling = true,
},
-- constituent country of the Netherlands
["Curaçao"] = {
placetype = {"constituent country", "country"},
container = "Netherlands",
addl_parents = {"Caribbean"},
british_spelling = true,
},
-- special territory of Chile
["Easter Island"] = {
placetype = {"special territory", "territory"},
container = "Chile",
addl_parents = {"Polynesia"},
},
-- British Overseas Territory
["Falkland Islands"] = {
the = true,
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"South America"},
british_spelling = true,
},
-- autonomous territory of Denmark
["Faroe Islands"] = {
the = true,
placetype = {"autonomous territory", "territory"},
container = "Denmark",
addl_parents = {"Europe"},
british_spelling = true,
},
-- overseas department and region of France
["French Guiana"] = {
placetype = {"overseas department", "department", "administrative region", "region"},
container = "France",
divs = {"communes"},
addl_parents = {"South America"},
british_spelling = true,
},
-- overseas collectivity of France
["French Polynesia"] = {
placetype = {"overseas collectivity", "collectivity"},
container = "France",
addl_parents = {"Polynesia"},
british_spelling = true,
},
-- French overseas territory
["French Southern and Antarctic Lands"] = {
the = true,
placetype = {"overseas territory", "territory"},
container = "France",
addl_parents = {"Africa"},
},
-- British Overseas Territory
["Gibraltar"] = {
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"Europe"},
is_city = true,
british_spelling = true,
},
-- autonomous territory of Denmark
["Greenland"] = {
placetype = {"autonomous territory", "territory"},
container = "Denmark",
addl_parents = {"North America"},
divs = {"municipalities"},
british_spelling = true,
},
-- overseas department and region of France
["Guadeloupe"] = {
placetype = {"overseas department", "department", "administrative region", "region"},
container = "France",
addl_parents = {"Caribbean"},
divs = {"communes"},
british_spelling = true,
},
-- unincorporated territory of the United States
["Guam"] = {
placetype = {"unincorporated territory", "overseas territory", "territory"},
container = "United States",
addl_parents = {"Micronesia"},
},
-- self-governing British Crown dependency; technically called the Bailiwick of Guernsey
["Guernsey"] = {
placetype = {"crown dependency", "dependency", "dependent territory", "bailiwick", "territory"},
container = "United Kingdom",
addl_parents = {"British Isles", "Europe"},
british_spelling = true,
wp = "Bailiwick of %l",
},
["Bailiwick of Guernsey"] = {alias_of = "Guernsey", the = true},
-- Australian external territory
["Heard Island and McDonald Islands"] = {
the = true,
placetype = {"external territory", "territory"},
container = "Australia",
addl_parents = {"Africa"},
},
-- special administrative region of China
["Hong Kong"] = {
placetype = {"special administrative region", "city"},
container = "China",
is_city = true,
british_spelling = true,
},
-- self-governing British Crown dependency
["Isle of Man"] = {
the = true,
placetype = {"crown dependency", "dependency", "dependent territory", "territory"},
container = "United Kingdom",
addl_parents = {"British Isles", "Europe"},
british_spelling = true,
},
-- Norwegian unincorporated area
["Jan Mayen"] = {
placetype = {"unincorporated area", "dependent territory", "territory", "island"},
container = "Norway",
addl_parents = {"Europe"},
british_spelling = true,
},
-- self-governing British Crown dependency; technically called the Bailiwick of Jersey
["Jersey"] = {
placetype = {"crown dependency", "dependency", "dependent territory", "bailiwick", "territory"},
container = "United Kingdom",
addl_parents = {"British Isles", "Europe"},
british_spelling = true,
},
["Bailiwick of Jersey"] = {alias_of = "Jersey", the = true},
-- special administrative region of China
["Macau"] = {
placetype = {"special administrative region", "city"},
container = "China",
is_city = true,
british_spelling = true,
},
-- overseas department and region of France
["Martinique"] = {
placetype = {"overseas department", "department", "administrative region", "region"},
container = "France",
divs = {"communes"},
addl_parents = {"Caribbean"},
british_spelling = true,
},
-- overseas department and region of France
["Mayotte"] = {
placetype = {"overseas department", "department", "administrative region", "region"},
container = "France",
divs = {"communes"},
addl_parents = {"Africa"},
british_spelling = true,
},
-- British Overseas Territory
["Montserrat"] = {
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"Caribbean"},
british_spelling = true,
},
-- special collectivity of France
["New Caledonia"] = {
placetype = {"special collectivity", "collectivity"},
container = "France",
addl_parents = {"Melanesia"},
british_spelling = true,
},
-- dependent territory of New Zealand
["New Zealand Subantarctic Islands"] = {
the = true,
placetype = {"dependent territory", "territory"},
container = "New Zealand",
addl_parents = {"Antarctica"},
british_spelling = true,
},
-- self-governing but in free association with New Zealand
["Niue"] = {
placetype = {"country"},
container = "New Zealand",
addl_parents = {"Polynesia"},
british_spelling = true,
},
-- Australian external territory
["Norfolk Island"] = {
placetype = {"external territory", "territory"},
container = "Australia",
addl_parents = {"Polynesia"},
british_spelling = true,
},
-- de-facto independent state, internationally recognized as part of Cyprus
["Northern Cyprus"] = {
placetype = {"unrecognized country", "country"},
addl_parents = {"Cyprus", "Turkey", "Europe", "Asia"},
divs = {"districts"},
keydesc = "the de-facto independent state of [[Northern Cyprus]], internationally recognized as part of the country of [[Cyprus]]",
british_spelling = true,
},
-- commonwealth, unincorporated territory of the United States
["Northern Mariana Islands"] = {
the = true,
placetype = {"commonwealth", "unincorporated territory", "overseas territory", "territory"},
container = "United States",
addl_parents = {"Micronesia"},
},
-- British Overseas Territory
["Pitcairn Islands"] = {
the = true,
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"Polynesia"},
british_spelling = true,
},
-- commonwealth of the United States
["Puerto Rico"] = {
placetype = {"commonwealth", "overseas territory", "territory"},
container = "United States",
addl_parents = {"Caribbean"},
divs = {"municipalities"},
},
-- overseas department and region of France
["Réunion"] = {
placetype = {"overseas department", "department", "administrative region", "region"},
container = "France",
divs = {"communes"},
addl_parents = {"Africa"},
british_spelling = true,
},
-- special municipality of the Netherlands
["Saba"] = {
placetype = {"special municipality", "municipality", "overseas territory", "territory"},
container = "Netherlands",
addl_parents = {"Caribbean"},
is_city = true,
british_spelling = true,
},
-- overseas collectivity of France
["Saint Barthélemy"] = {
placetype = {"overseas collectivity", "collectivity"},
container = "France",
addl_parents = {"Caribbean"},
british_spelling = true,
},
-- British Overseas Territory
["Saint Helena, Ascension and Tristan da Cunha"] = {
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
divs = {{type = "constituent parts", container_parent_type = false}},
addl_parents = {"Atlantic Ocean", "Africa"},
british_spelling = true,
},
-- constituent parts of the combined oveseas territory
["Ascension Island"] = {
placetype = {"constituent part", "territory", "island"},
container = {key = "Saint Helena, Ascension and Tristan da Cunha", placetype = "overseas territory"},
addl_parents = {"Atlantic Ocean"},
overriding_bare_label_parents = {},
no_container_cat = false,
no_container_parent = false,
no_auto_augment_container = false,
},
["Saint Helena"] = {
placetype = {"constituent part", "territory", "island"},
container = {key = "Saint Helena, Ascension and Tristan da Cunha", placetype = "overseas territory"},
addl_parents = {"Atlantic Ocean"},
overriding_bare_label_parents = {},
no_container_cat = false,
no_container_parent = false,
no_auto_augment_container = false,
},
["Tristan da Cunha"] = {
placetype = {"constituent part", "territory", "archipelago"},
container = {key = "Saint Helena, Ascension and Tristan da Cunha", placetype = "overseas territory"},
addl_parents = {"Atlantic Ocean"},
overriding_bare_label_parents = {},
no_container_cat = false,
no_container_parent = false,
no_auto_augment_container = false,
},
-- overseas collectivity of France
["Saint Martin"] = {
placetype = {"overseas collectivity", "collectivity"},
container = "France",
addl_parents = {"Caribbean"},
british_spelling = true,
},
-- overseas collectivity of France
["Saint Pierre and Miquelon"] = {
placetype = {"overseas collectivity", "collectivity"},
container = "France",
divs = {"communes"},
addl_parents = {"North America"},
british_spelling = true,
},
-- special municipality of the Netherlands
["Sint Eustatius"] = {
placetype = {"special municipality", "municipality", "overseas territory", "territory"},
container = "Netherlands",
addl_parents = {"Caribbean"},
is_city = true,
british_spelling = true,
},
-- constituent country of the Netherlands
["Sint Maarten"] = {
placetype = {"constituent country", "country"},
container = "Netherlands",
addl_parents = {"Caribbean"},
british_spelling = true,
},
-- de-facto independent state, internationally recognized as part of Somalia
["Somaliland"] = {
placetype = {"unrecognized country", "country"},
addl_parents = {"Somalia", "Africa"},
keydesc = "the de-facto independent state of [[Somaliland]], internationally recognized as part of the country of [[Somalia]]",
british_spelling = true,
},
-- British Overseas Territory
-- FIXME: We should form the group "South Georgia and the South Sandwich Islands" like we did for
-- "Saint Helena, Ascension and Tristan da Cunha".
["South Georgia"] = {
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"Atlantic Ocean"},
british_spelling = true,
},
-- de-facto independent state, internationally recognized as part of Georgia
["South Ossetia"] = {
placetype = {"unrecognized country", "country"},
addl_parents = {"Georgia", "Europe", "Asia"},
keydesc = "the de-facto independent state of [[South Ossetia]], internationally recognized as part of the country of [[Georgia]]",
british_spelling = true,
},
-- British Overseas Territory
["South Sandwich Islands"] = {
the = true,
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"Atlantic Ocean"},
wp = true,
wpcat = "South Georgia and the South Sandwich Islands",
british_spelling = true,
},
-- Norwegian unincorporated area
["Svalbard"] = {
placetype = {"unincorporated area", "dependent territory", "territory", "archipelago"},
container = "Norway",
addl_parents = {"Europe"},
british_spelling = true,
},
-- dependent territory of New Zealand
["Tokelau"] = {
placetype = {"dependent territory", "territory"},
container = "New Zealand",
addl_parents = {"Polynesia"},
british_spelling = true,
},
-- de-facto independent state, internationally recognized as part of Moldova
["Transnistria"] = {
placetype = {"unrecognized country", "country"},
addl_parents = {"Moldova", "Europe"},
keydesc = "the de-facto independent state of [[Transnistria]], internationally recognized as part of [[Moldova]]",
british_spelling = true,
},
-- British Overseas Territory
["Turks and Caicos Islands"] = {
the = true,
placetype = {"overseas territory", "territory"},
container = "United Kingdom",
addl_parents = {"Caribbean"},
british_spelling = true,
},
-- unincorporated territory of the United States
["United States Minor Outlying Islands"] = {
the = true,
placetype = {"unincorporated territory", "overseas territory", "territory"},
container = "United States",
addl_parents = {"Islands", "Micronesia", "Polynesia", "Caribbean"},
},
-- FIXME: We should add entries for the other minor outlying islands.
-- Baker Island (Oceania)
-- Howland Island (Oceania)
-- Jarvis Island (Oceania)
-- Johnston Atoll (Oceania)
-- Kingman Reef (Oceania)
-- Midway Atoll (Oceania)
-- Navassa Island (Caribbean)
-- Palmyra Atoll (Oceania)
-- Wake Island (Oceania)
["Wake Island"] = {
placetype = {"unincorporated territory", "overseas territory", "territory"},
container = "United States",
addl_parents = {"Micronesia"},
},
-- unincorporated territory of the United States
["United States Virgin Islands"] = {
the = true,
placetype = {"unincorporated territory", "overseas territory", "territory"},
container = "United States",
addl_parents = {"Caribbean"},
},
["U.S. Virgin Islands"] = {alias_of = "United States Virgin Islands", display = true, the = true},
["US Virgin Islands"] = {alias_of = "United States Virgin Islands", display = true, the = true},
-- overseas collectivity of France
["Wallis and Futuna"] = {
placetype = {"overseas collectivity", "collectivity"},
container = "France",
addl_parents = {"Polynesia"},
british_spelling = true,
},
}
export.country_like_entities_group = {
-- don't do any transformations between key and placename; in particular, don't chop off anything from
-- "Saint Helena, Ascension and Tristan da Cunha".
key_to_placename = false,
placename_to_key = false,
canonicalize_key_container = make_canonicalize_key_container(nil, "country"),
default_overriding_bare_label_parents = {"country-like entities"},
default_no_container_cat = true,
default_no_container_parent = true,
-- These entities often aren't really part of their container; a village in Wallis and Futuna (an overseas
-- collectivity of France in Polynesia), for example, shouldn't be treated as a village in France, nor as a village
-- in Europe.
default_no_auto_augment_container = true,
data = export.country_like_entities,
}
-- Former countries and such; we don't create "Cities in ..." categories because they don't exist anymore
export.former_countries = {
-- de-facto independent state of Armenian ethnicity, internationally recognized as part of Azerbaijan
-- (also known as Nagorno-Karabakh)
-- NOTE: Formerly listed Armenia as a parent; this seems politically non-neutral so I've taken it out.
["Artsakh"] = {
placetype = {"unrecognized country", "country"},
addl_parents = {"Azerbaijan", "Europe", "Asia"},
keydesc = "the former de-facto independent state of [[Artsakh]], internationally recognized as part of [[Azerbaijan]]",
british_spelling = true,
},
["Nagorno-Karabakh"] = {alias_of = "Artsakh"},
["Czechoslovakia"] = {container = "Europe", british_spelling = true},
["East Germany"] = {container = "Europe", addl_parents = {"Germany"}, british_spelling = true},
["North Vietnam"] = {container = "Asia", addl_parents = {"Vietnam"}},
["Persia"] = {placetype = {"empire", "country"}, container = "Asia", divs = {"provinces"}},
["Byzantine Empire"] = {
the = true, placetype = {"empire", "country"}, container = {"Europe", "Africa", "Asia"},
addl_parents = {"Ancient Europe", "Ancient Near East"},
divs = {
"provinces", "themes",
}},
["Roman Empire"] = {
the = true, placetype = {"empire", "country"}, container = {"Europe", "Africa", "Asia"}, addl_parents = {"Rome"},
divs = {
"provinces",
{type = "FORMER provinces", cat_as = "provinces"},
}},
["South Vietnam"] = {container = "Asia", addl_parents = {"Vietnam"}},
["Soviet Union"] = {
the = true, container = {"Europe", "Asia"}, divs = {"republics", "autonomous republics"},
british_spelling = true},
["West Germany"] = {container = "Europe", addl_parents = {"Germany"}, british_spelling = true},
["Yugoslavia"] = {container = "Europe", divs = {"districts"},
keydesc = "the former [[Kingdom of Yugoslavia]] (1918–1943) or the former [[Socialist Federal Republic of Yugoslavia]] (1943–1992)", british_spelling = true},
}
export.former_countries_group = {
canonicalize_key_container = canonicalize_continent_container,
default_overriding_bare_label_parents = {"former countries and country-like entities"},
default_is_former_place = true,
default_placetype = "country",
default_no_container_cat = true,
default_no_container_parent = true,
-- No need to augment country holonyms with continents; not needed for disambiguation.
default_no_auto_augment_container = true,
data = export.former_countries,
}
-----------------------------------------------------------------------------------
-- Subpolity tables --
-----------------------------------------------------------------------------------
export.australia_states_and_territories = {
["Australian Capital Territory, Australia"] = {the = true, placetype = "territory"},
["Jervis Bay Territory, Australia"] = {the = true, placetype = "territory"},
["New South Wales, Australia"] = {},
["Northern Territory, Australia"] = {the = true, placetype = "territory"},
["Queensland, Australia"] = {},
["South Australia, Australia"] = {},
["Tasmania, Australia"] = {},
["Victoria, Australia"] = {},
["Western Australia, Australia"] = {},
}
-- states and territories of Australia
export.australia_group = {
default_container = "Australia",
default_placetype = "state",
default_divs = "local government areas",
data = export.australia_states_and_territories,
}
export.austria_states = {
["Vienna, Austria"] = {},
["Lower Austria, Austria"] = {},
["Upper Austria, Austria"] = {},
["Styria, Austria"] = {},
["Tyrol, Austria"] = {wp = "Tyrol (state)"},
["Carinthia, Austria"] = {},
["Salzburg, Austria"] = {wp = "Salzburg (state)"},
["Vorarlberg, Austria"] = {},
["Burgenland, Austria"] = {},
}
-- states of Austria
export.austria_group = {
default_container = "Austria",
default_placetype = "state",
default_divs = "municipalities",
data = export.austria_states,
}
export.bangladesh_divisions = {
["Barisal Division, Bangladesh"] = {},
["Chittagong Division, Bangladesh"] = {},
["Dhaka Division, Bangladesh"] = {},
["Khulna Division, Bangladesh"] = {},
["Mymensingh Division, Bangladesh"] = {},
["Rajshahi Division, Bangladesh"] = {},
["Rangpur Division, Bangladesh"] = {},
["Sylhet Division, Bangladesh"] = {},
}
-- divisions of Bangladesh
export.bangladesh_group = {
key_to_placename = make_key_to_placename(", Bangladesh$", " Division$"),
placename_to_key = make_placename_to_key(", Bangladesh", " Division"),
default_container = "Bangladesh",
default_placetype = "division",
default_divs = "districts",
data = export.bangladesh_divisions,
}
export.brazil_states = {
["Acre, Brazil"] = {wp = "%l (state)"},
["Alagoas, Brazil"] = {},
["Amapá, Brazil"] = {},
["Amazonas, Brazil"] = {wp = "%l (Brazilian state)"},
["Bahia, Brazil"] = {},
["Ceará, Brazil"] = {},
["Distrito Federal, Brazil"] = {wp = "Federal District (Brazil)"},
["Espírito Santo, Brazil"] = {},
["Goiás, Brazil"] = {},
["Maranhão, Brazil"] = {},
["Mato Grosso, Brazil"] = {},
["Mato Grosso do Sul, Brazil"] = {},
["Minas Gerais, Brazil"] = {},
["Pará, Brazil"] = {},
["Paraíba, Brazil"] = {},
["Paraná, Brazil"] = {wp = "%l (state)"},
["Pernambuco, Brazil"] = {},
["Piauí, Brazil"] = {},
["Rio de Janeiro, Brazil"] = {wp = "%l (state)"},
["Rio Grande do Norte, Brazil"] = {},
["Rio Grande do Sul, Brazil"] = {},
["Rondônia, Brazil"] = {},
["Roraima, Brazil"] = {},
["Santa Catarina, Brazil"] = {wp = "%l (state)"},
["São Paulo, Brazil"] = {wp = "%l (state)"},
["Sergipe, Brazil"] = {},
["Tocantins, Brazil"] = {},
}
-- states of Brazil
export.brazil_group = {
default_container = "Brazil",
default_placetype = "state",
default_divs = "municipalities",
data = export.brazil_states,
}
-- provinces (a.k.a. oblasts) of Bulgaria
export.bulgaria_provinces = {
["Blagoevgrad Province, Bulgaria"] = {},
["Burgas Province, Bulgaria"] = {},
["Dobrich Province, Bulgaria"] = {},
["Gabrovo Province, Bulgaria"] = {},
["Haskovo Province, Bulgaria"] = {},
["Kardzhali Province, Bulgaria"] = {},
["Kyustendil Province, Bulgaria"] = {},
["Lovech Province, Bulgaria"] = {},
["Montana Province, Bulgaria"] = {},
["Pazardzhik Province, Bulgaria"] = {},
["Pernik Province, Bulgaria"] = {},
["Pleven Province, Bulgaria"] = {},
["Plovdiv Province, Bulgaria"] = {},
["Razgrad Province, Bulgaria"] = {},
["Ruse Province, Bulgaria"] = {},
["Shumen Province, Bulgaria"] = {},
["Silistra Province, Bulgaria"] = {},
["Sliven Province, Bulgaria"] = {},
["Smolyan Province, Bulgaria"] = {},
["Sofia City Province, Bulgaria"] = {},
["Sofia Province, Bulgaria"] = {},
["Stara Zagora Province, Bulgaria"] = {},
["Targovishte Province, Bulgaria"] = {},
["Varna Province, Bulgaria"] = {},
["Veliko Tarnovo Province, Bulgaria"] = {},
["Vidin Province, Bulgaria"] = {},
["Vratsa Province, Bulgaria"] = {},
["Yambol Province, Bulgaria"] = {},
}
export.bulgaria_group = {
key_to_placename = make_key_to_placename(", Bulgaria$", " Province$"),
placename_to_key = make_placename_to_key(", Bulgaria", " Province"),
default_container = "Bulgaria",
--== source: https://en.wikipedia.org/wiki/NUTS_statistical_regions_of_Bulgaria ==
divs = {"regions", "planning regions", "provinces", "municipalities", "settlements"},
default_placetype = "province",
data = export.bulgaria_provinces,
}
export.canada_provinces_and_territories = {
["Alberta, Canada"] = {divs = {
{type = "municipal districts", container_parent_type = "rural municipalities"},
}},
["British Columbia, Canada"] = {divs =
{type = "regional districts", container_parent_type = false},
"regional municipalities",
},
["Manitoba, Canada"] = {divs = {"rural municipalities"}},
["New Brunswick, Canada"] = {divs = {"counties", "parishes", {type = "civil parishes", cat_as = "parishes"}}},
["Newfoundland and Labrador, Canada"] = {},
["Northwest Territories, Canada"] = {the = true, placetype = "territory"},
["Nova Scotia, Canada"] = {divs = {"counties", "regional municipalities"}},
["Nunavut, Canada"] = {placetype = "territory"},
["Ontario, Canada"] = {divs = {"counties", "regional municipalities", {type = "townships", prep = "in"}}},
["Prince Edward Island, Canada"] = {divs = {"counties", "parishes", "rural municipalities"}},
["Saskatchewan, Canada"] = {divs = {"rural municipalities"}},
["Quebec, Canada"] = {divs = {
"counties",
{type = "regional county municipalities", container_parent_type = "regional municipalities"},
-- administrative regions have an official (but non-governmental) function but there don't appear to be any
-- equivalent regions elsewhere in Canada, so disable the [[Category:Regions of Canada]] grouping
{type = "regions", container_parent_type = false},
{type = "townships", prep = "in"},
{type = "parish municipalities", cat_as = {{type = "parishes", container_parent_type = "counties"}, "municipalities"}},
{type = "township municipalities", cat_as = {{type = "townships", prep = "in"}, "municipalities"}},
{type = "village municipalities", cat_as = {{type = "villages", prep = "in"}, "municipalities"}},
}},
["Yukon, Canada"] = {placetype = "territory"},
["Yukon Territory, Canada"] = {alias_of = "Yukon, Canada", the = true},
}
-- provinces and territories of Canada
export.canada_group = {
default_container = "Canada",
default_placetype = "province",
data = export.canada_provinces_and_territories,
}
export.china_provinces_and_autonomous_regions = {
-- direct-administered municipalities are not here but below under prefecture-level cities
["Anhui, China"] = {},
["Fujian, China"] = {},
["Fuchien, China"] = {alias_of = "Fujian, China", display = true},
["Gansu, China"] = {},
["Guangdong, China"] = {},
["Guangxi, China"] = {placetype = "autonomous region"},
["Guizhou, China"] = {},
["Hainan, China"] = {},
["Hebei, China"] = {},
["Heilongjiang, China"] = {},
["Henan, China"] = {},
["Hubei, China"] = {},
["Hunan, China"] = {},
["Inner Mongolia, China"] = {placetype = "autonomous region"},
["Jiangsu, China"] = {},
["Jiangxi, China"] = {},
["Jilin, China"] = {},
["Liaoning, China"] = {},
["Ningxia, China"] = {placetype = "autonomous region"},
["Qinghai, China"] = {},
["Shaanxi, China"] = {},
["Shandong, China"] = {},
["Shanxi, China"] = {},
["Sichuan, China"] = {},
["Tibet, China"] = {placetype = "autonomous region", wp = "Tibet Autonomous Region"},
["Xinjiang, China"] = {placetype = "autonomous region"},
["Yunnan, China"] = {},
["Zhejiang, China"] = {},
}
-- provinces and autonomous regions of China
export.china_group = {
default_container = "China",
default_placetype = "province",
default_divs = {
"prefectures", "prefecture-level cities",
"districts", "subdistricts", "townships",
{type = "counties", cat_as = "counties and county-level cities"},
{type = "county-level cities", cat_as = "counties and county-level cities"},
},
data = export.china_provinces_and_autonomous_regions,
}
export.china_prefecture_level_cities = {
-- In China, a "prefecture-level city" is not a city in any real sense. It is rather a prefecture, which is an
-- administrative unit smaller than a province but bigger than a county, which is administratively controlled by
-- the chief city of the prefecture (which bears the same name as the prefecture), in a unified government. Prior
-- to the mid-1980's, in fact, prefecture-level cities *were* prefectures, and a few of them (especially in the
-- western portion of China) have not yet been converted. Generally a given province is entirely tiled by
-- prefecture-level cities, another indication that they should be treated as prefectures and not cities per se.
-- Yet another indication is that prefecture-level cities can contain counties and county-level cities (which, much
-- like prefecture-level cities, are effectively counties surrounding a chief city of the county, again which bears
-- the same name as the county-level city).
--
-- For this reason, we treat prefecture-level cities as non-city political divisions, and separately enumerate the
-- most populous so we can separately categorize districts and counties under them instead of lumping them at the
-- province level.
--
-- Note also that China separately distinguishes "urban area" from "metro area". Sometimes the two figures are
-- identical but sometimes the metro area is larger (and very occasionally smaller, which I assume is an error). I'm
-- guessing that the "urban area" is the contiguous urban area over a certain density while the metro area includes
-- all urban areas above a certain density; when the latter is greater, it's because of satellite cities in the
-- metro area separated by suburban/exurban or rural land.
-- At first I chose all prefecture/province-level cities with a total prefecture/province-level population of at
-- least 6,000,000 per the 2020 census with data taken from https://www.citypopulation.de/en/china/admin/ (a total
-- of 67, including the four direct-administered municipalities), and also chose all prefecture/province-level
-- cities whose "urban population" was at least 2,000,000 per the 2020 census with data taken from Wikipedia
-- [[w:List of cities in China by population#Cities and towns by population]] (a total of 61 cities; if we cut off
-- at 1.5 million we'd have 84 cities, and if we cut off at 1 million we'd have 105 cities). Merging them produces
-- 87 cities. Note that this leaves off a few well-known cities (Guilin, Qiqihar, Kashgar, Lhasa, ...) but includes
-- a lot of obscure cities.
--
-- At a later date I added all cities from citypopulation.de whose "urban" population per the 2020 China census was
-- >= 1 million, and then finally added all urban agglomerations from citypopulation.de whose 2025-01-01 estimate
-- was >= 1 million. These are sorted below by the urban agglomeration value (which is generally of the "adm-urb" =
-- "administrative area (urban population)" type) and sometimes groups nearby cities into a single agglomeration
-- (most notably in the case of the Pearl River Delta, grouped under Guangzhou with an agglomeration population of
-- 72,700,000 but including a large number of nearby large cities in the agglomeration (although for some reason not
-- Hong Kong, maybe due to the administrative issues involved). In addition, citypopulation.de includes divisions
-- under a prefecture-level city if they are city-like and have an agglomeration population of at least 1 million;
-- this includes several county-level cities, one county and one district (Wanzhou, a "district" of Chongqing
-- despite being 142 miles away). None of the county-level cities or counties have districts under them, only
-- subdistricts, towns and townships.
["Guangzhou"] = {container = "Guangdong"}, -- 18.7 prefectural, 18.8 urban; sub-provincial city; 16.097 urban (72.700 adm-urb including Dongguan, Foshan, Huizhou, Jiangmen, Shenzhen, Zhongshan) per citypopulation.de
["Dongguan"] = {container = "Guangdong"}, -- 10.5 prefectural, 10.5 urban; 9.645 per citypopulation.de; included by citypopulation.de in Guangzhou agglomeration
["Foshan"] = {container = "Guangdong"}, -- 9.5 prefectural, 9.5 urban; 9.043 per citypopulation.de; included by citypopulation.de in Guangzhou agglomeration
["Huizhou"] = {container = "Guangdong"}, -- 6.0 prefectural, 2.5 urban; 2.900 per citypopulation.de; included by citypopulation.de in Guangzhou agglomeration
["Jiangmen"] = {container = "Guangdong"}, -- 4.798 prefectural, 2.7 urban; 1.795 per citypopulation.de; included by citypopulation.de in Guangzhou agglomeration
["Shenzhen"] = {container = "Guangdong"}, -- 17.5 prefectural, 14.7 urban; sub-provincial city; 17.445 per citypopulation.de; included by citypopulation.de in Guangzhou agglomeration
["Zhongshan"] = {container = "Guangdong"}, -- 4.418 prefectural, 4.4 urban; 3.842 per citypopulation.de; included by citypopulation.de in Guangzhou agglomeration
["Shanghai"] = {placetype = {"direct-administered municipality", "municipality", "city"}}, -- 24.9 prefectural, 29.9 urban; 21.910 urban (41.600 adm-urb including Changshu, Changzhou, Suzhou, Wuxi) per citypopulation.de
["Changshu"] = {container = "Jiangsu"}, -- 1.231 urban per citypopulation.de; included by citypopulation.de in Shanghai agglomeration
-- NOTE: Not to be confused with Cangzhou in Hebei
["Changzhou"] = {container = "Jiangsu"}, -- 5.278 prefectural, 3.6 urban; 3.187 urban per citypopulation.de; included by citypopulation.de in Shanghai agglomeration
-- NOTE: There is also a prefecture-level city Suzhou in Anhui with 5.3 million prefectural inhabitants
["Suzhou"] = {container = "Jiangsu"}, -- 12.8 prefectural, 4.3 urban; 5.893 urban per citypopulation.de; included by citypopulation.de in Shanghai agglomeration
["Wuxi"] = {container = "Jiangsu"}, -- 7.5 prefectural, 3.3 urban; 3.957 per citypopulation.de; included by citypopulation.de in Shanghai agglomeration
["Beijing"] = {placetype = {"direct-administered municipality", "municipality", "city"}}, -- 21.9 prefectural, 21.9 urban; 18.961 urban (21.500 adm-urb) per citypopulation.de
["Chengdu"] = {container = "Sichuan"}, -- 20.9 prefectural, 16.9 urban; sub-provincial city; 13.568 urban (18.100 adm-urb) per citypopulation.de
["Xiamen"] = {container = "Fujian"}, -- 5.163 prefectural, 5.2 urban; sub-provincial city; 4.617 urban (15.400 adm-urb including Jinjiang, Quanzhou, Putian) per citypopulation.de
["Jinjiang"] = {container = "Fujian"}, -- 1.416 urban per citypopulation.de; included by citypopulation.de in Xiamen agglomeration
["Quanzhou"] = {container = "Fujian"}, -- 8.8 prefectural, 1.7 urban (6.7 metro); 1.469 urban per citypopulation.de; included by citypopulation.de in Xiamen agglomeration
["Putian"] = {container = "Fujian"}, -- 3.210 prefectural, 2.0 urban; 1.539 urban per citypopulation.de; included by citypopulation.de in Xiamen agglomeration
["Hangzhou"] = {container = "Zhejiang"}, -- 11.9 prefectural, 10.7 urban; sub-provincial city; 9.236 urban (14.600 adm-urb including Shaoxing) per citypopulation.de
["Shaoxing"] = {container = "Zhejiang"}, -- 5.270 prefectural, 2.5 urban; 2.333 urban per citypopulation.de; included by citypopulation.de in Hangzhou agglomeration
["Xi'an"] = {container = "Shaanxi"}, -- 12.1 prefectural, 11.9 urban; sub-provincial city; 9.393 urban (13.400 adm-urb including Xianyang) per citypopulation.de
["Xianyang"] = {container = "Shaanxi"}, -- 1.193 urban per citypopulation.de; included by citypopulation.de in Xi'an agglomeration
["Chongqing"] = {placetype = {"direct-administered municipality", "municipality", "city"}}, -- 32.1 prefectural, 16.9 urban; 9.581 urban (12.900 adm-urb) per citypopulation.de
["Wuhan"] = {container = "Hubei"}, -- 12.4 prefectural, 12.3 urban; sub-provincial city; 10.495 urban (12.600 adm-urb) per citypopulation.de
["Tianjin"] = {placetype = {"direct-administered municipality", "municipality", "city"}}, -- 13.9 prefectural, 13.9 urban; 11.052 urban (11.700 adm-urb) per citypopulation.de
["Changsha"] = {container = "Hunan"}, -- 10.0 prefectural, 6.0 urban; 5.630 urban (11.500 adm-urb including Xiangtan, Zhuzhou) per citypopulation.de
-- Changsha County -- 1.024 urban per citypopulation.de
["Zhuzhou"] = {container = "Hunan"}, -- 1.510 urban per citypopulation.de; included by citypopulation.de in Changsha agglomeration
["Zhengzhou"] = {container = "Henan"}, -- 12.6 prefectural, 6.7 urban; 6.461 urban (10.300 adm-urb) per citypopulation.de
["Nanjing"] = {container = "Jiangsu"}, -- 9.3 prefectural, 9.3 urban; sub-provincial city; 7.520 urban (9.500 adm-urb including Ma'anshan) per citypopulation.de
["Shenyang"] = {container = "Liaoning"}, -- 9.1 prefectural, 7.9 urban; sub-provincial city; 7.026 urban (8.800 adm-urb including Fushun) per citypopulation.de
["Fushun"] = {container = "Liaoning"}, -- 1.229 urban per citypopulation.de; included by citypopulation.de in Shenyang agglomeration
["Hefei"] = {container = "Anhui"}, -- 9.4 prefectural, 4.2 urban; 5.056 urban (8.200 adm-urb) per citypopulation.de
["Shantou"] = {container = "Guangdong"}, -- 5.502 prefectural, 4.3 urban; 3.839 urban (8.050 adm-urb including Chaozhou, Jieyang, Puning) per citypopulation.de
["Chaozhou"] = {container = "Guangdong"}, -- 1.254 urban per citypopulation.de; included by citypopulation.de in Shantou agglomeration
["Jieyang"] = {container = "Guangdong"}, -- 1.243 urban per citypopulation.de; included by citypopulation.de in Shantou agglomeration
["Qingdao"] = {container = "Shandong"}, -- 10.1 prefectural, 7.1 urban; sub-provincial city; 6.165 urban (7.700 adm-urb) per citypopulation.de
["Ningbo"] = {container = "Zhejiang"}, -- 9.4 prefectural, 5.1 urban; sub-provincial city; 3.731 urban (7.600 adm-urb including Cixi, Yuyao) per citypopulation.de
["Cixi"] = {container = "Zhejiang"}, -- 1.458 urban per citypopulation.de; included by citypopulation.de in Ningbo agglomeration
["Yuyao"] = {container = "Zhejiang"}, -- 1.014 urban per citypopulation.de; included by citypopulation.de in Ningbo agglomeration
-- Hong Kong 7.500 agglomeration per citypopulation.de 2025-01-01 estimate including Kowloon, Victoria
["Wenzhou"] = {container = "Zhejiang"}, -- 9.6 prefectural, 3.6 urban; 2.582 urban (7.000 adm-urb including Rui'an, Cangnan, Pingyang) per citypopulation.de
-- Rui'an is a "county-level city" of the "prefecture-level city" of Wenzhou but in fact is 19 miles away from Wenzhou city proper (urban core to urban core).
["Rui'an"] = {placetype = "county-level city", container = {key = "Wenzhou", placetype = "prefecture-level city"}, divs = {"subdistricts", "townships"}}, -- 1.013 urban per citypopulation.de; included by citypopulation.de in Wenzhou agglomeration
["Kunming"] = {container = "Yunnan"}, -- 8.5 prefectural, 6.0 urban; 5.273 urban (6.800 adm-urb) per citypopulation.de
-- includes Láiwú city
["Jinan"] = {container = "Shandong", wp = "%l, %c"}, -- 9.2 prefectural, 8.4 urban; sub-provincial city; 5.648 urban (6.750 adm-urb) per citypopulation.de
-- includes Xīnjí city
["Shijiazhuang"] = {container = "Hebei"}, -- 11.2 prefectural, 4.1 urban; 5.090 urban (6.450 adm-urb) per citypopulation.de
["Taiyuan"] = {container = "Shanxi"}, -- 5.304 prefectural, 4.5 urban; 4.304 urban (6.150 adm-urb) per citypopulation.de
["Harbin"] = {container = "Heilongjiang"}, -- 10.0 prefectural, 7.0 urban; sub-provincial city; 5.243 urban (5.550 adm-urb) per citypopulation.de
["Nanning"] = {container = {key = "Guangxi, China", placetype = "autonomous region"}}, -- 8.7 prefectural, 3.8 urban; 4.583 urban (5.550 adm-urb) per citypopulation.de
["Dalian"] = {container = "Liaoning"}, -- 7.5 prefectural, 5.7 urban; sub-provincial city; 4.914 urban (5.400 adm-urb) per citypopulation.de
["Guiyang"] = {container = "Guizhou"}, -- 5.987 prefectural, 3.5 urban; 4.021 urban (5.300 adm-urb) per citypopulation.de
["Changchun"] = {container = "Jilin"}, -- 9.1 prefectural, 5.7 urban; sub-provincial city; 4.557 urban (5.200 adm-urb) per citypopulation.de
["Nanchang"] = {container = "Jiangxi"}, -- 6.3 prefectural, 3.6 (3.9?) urban, 5.3 metro; 3.519 urban (5.150 adm-urb) per citypopulation.de
["Ürümqi"] = {container = {key = "Xinjiang, China", placetype = "autonomous region"}}, -- 4.054 prefectural, 4.3 urban; 3.843 urban (5.000 adm-urb) per citypopulation.de
["Urumqi"] = {alias_of = "Ürümqi", display = true},
["Fuzhou"] = {container = "Fujian"}, -- 8.3 prefectural, 4.1 urban; 3.723 urban (4.775 adm-urb) per citypopulation.de
["Linyi"] = {container = "Shandong"}, -- 11.0 prefectural, 2.3 urban; 2.744 urban (4.650 adm-urb) per citypopulation.de
["Zibo"] = {container = "Shandong"}, -- 4.704 prefectural, 2.6 urban; 2.750 urban (3.975 adm-urb) per citypopulation.de
["Luoyang"] = {container = "Henan"}, -- 7.1 prefectural, 2.4 urban; 2.231 urban (3.750 adm-urb) per citypopulation.de
["Lanzhou"] = {container = "Gansu"}, -- 4.359 prefectural, 3.1 urban; 3.013 urban (3.575 adm-urb) per citypopulation.de
["Nantong"] = {container = "Jiangsu"}, -- 7.7 prefectural, 2.3 urban; 2.988 urban (3.475 adm-urb) citypopulation.de
["Weifang"] = {container = "Shandong"}, -- 9.4 prefectural, 2.7 urban; 1.998 urban (3.325 adm-urb) per citypopulation.de
["Jiangyin"] = {container = "Jiangsu"}, -- 1.331 urban (3.200 adm-urb including Zhangjiagang) per citypopulation.de
["Zhangjiagang"] = {container = "Jiangsu"}, -- 1.056 urban per citypopulation.de; included in Jiangyin figures
["Xuzhou"] = {container = "Jiangsu"}, -- 9.1 prefectural, 2.6 urban; 2.846 urban (3.150 adm-urb) per citypopulation.de
["Handan"] = {container = "Hebei"}, -- 9.4 prefectural, 2.8 urban; 2.095 urban (2.925 adm-urb) per citypopulation.de
["Hohhot"] = {container = {key = "Inner Mongolia, China", placetype = "autonomous region"}}, -- 3.446 prefectural, 2.7 urban; 2.373 urban (2.850 adm-urb) per citypopulation.de
["Haikou"] = {container = "Hainan"}, -- 2.873 prefectural, 2.3 urban; 2.349 urban (2.800 adm-urb) per citypopulation.de
["Tangshan"] = {container = "Hebei"}, -- 7.7 prefectural, 3.4 urban; 2.550 urban (2.750 adm-urb) per citypopulation.de
["Xinxiang"] = {container = "Henan"}, -- 6.3 prefectural, 1.2 urban, 2.7 metro; 1.271 urban (2.700 adm-urb) per citypopulation.de
["Yiwu"] = {container = "Zhejiang"}, -- 1.481 urban (2.700 adm-urb) per citypopulation.de
["Zhuhai"] = {container = "Guangdong"}, -- 2.439 prefectural, 2.4 urban; 2.207 urban (2.675 adm-urb) per citypopulation.de
["Taizhou, Zhejiang"] = {container = "Zhejiang"}, -- 6.6 prefectural, 1.6 urban; 1.486 urban (2.625 adm-urb) per citypopulation.de
["Taizhou"] = {alias_of = "Taizhou, Zhejiang"},
["Yantai"] = {container = "Shandong"}, -- 7.1 prefectural, 2.5 urban; 2.312 urban (2.550 adm-urb) per citypopulation.de
["Yinchuan"] = {container = {key = "Ningxia, China", placetype = "autonomous region"}}, -- 1.663 urban (2.525 adm-urb) per citypopulation.de
["Liuzhou"] = {container = {key = "Guangxi, China", placetype = "autonomous region"}}, -- 4.157 prefectural, 2.2 urban; 2.205 urban (2.500 adm-urb) per citypopulation.de
["Anshan"] = {container = "Liaoning"}, -- 1.480 urban (2.350 adm-urb including Liáoyáng) per citypopulation.de
["Yangzhou"] = {container = "Jiangsu"}, -- 2.067 urban (2.300 adm-urb) per citypopulation.de
["Jiaxing"] = {container = "Zhejiang"}, -- 1.188 urban (2.275 adm-urb) per citypopulation.de
["Xining"] = {container = "Qinghai"}, -- 1.677 urban (2.250 adm-urb) per citypopulation.de
-- includes Dìngzhōu city and Xióngān Xīnqū
["Baoding"] = {container = "Hebei"}, -- 11.5 prefectural, 2.0 urban; 1.940 urban (2.225 adm-urb) per citypopulation.de
["Baotou"] = {container = {key = "Inner Mongolia, China", placetype = "autonomous region"}}, -- 2.709 prefectural, 2.2 urban; 2.104 urban (2.200 adm-urb) per citypopulation.de
["Ganzhou"] = {container = "Jiangxi"}, -- 9.0 prefectural, 1.6 urban; 1.778 urban (2.150 adm-urb) per citypopulation.de
["Pingdingshan"] = {container = "Henan"}, -- 1.046 urban (2.100 adm-urb) per citypopulation.de
["Zunyi"] = {container = "Guizhou"}, -- 6.6 prefectural, 2.4 urban/metro; 1.675 urban (2.025 adm-urb) per citypopulation.de
["Bengbu"] = {container = "Anhui"}, -- 1.078 urban (2.000 adm-urb) per citypopulation.de
["Datong"] = {container = "Shanxi"}, -- 3.105 prefectural, 2.0 urban; 1.810 urban (2.000 adm-urb) per citypopulation.de
["Anyang"] = {container = "Henan"}, -- 1.188 urban (1.960 adm-urb) per citypopulation.de
["Huai'an"] = {container = "Jiangsu"}, -- 4.556 prefectural, 2.6 urban; 1.805 urban (1.940 adm-urb) per citypopulation.de
["Zaozhuang"] = {container = "Shandong"}, -- 1.350 urban (1.900 adm-urb) per citypopulation.de
["Zhanjiang"] = {container = "Guangdong"}, -- 7.0 prefectural, 1.9 urban; 1.401 urban (1.890 adm-urb) per citypopulation.de
["Huainan"] = {container = "Anhui"}, -- 1.256 urban (1.880 adm-urb) per citypopulation.de
["Jining"] = {container = "Shandong"}, -- 8.4 prefectural, 1.5 urban; 1.700 urban (1.880 adm-urb) per citypopulation.de
["Daqing"] = {container = "Heilongjiang"}, -- 1.604 urban (1.860 adm-urb) per citypopulation.de
["Wuhu"] = {container = "Anhui"}, -- 1.598 urban (1.850 adm-urb) per citypopulation.de
["Guilin"] = {container = {key = "Guangxi, China", placetype = "autonomous region"}}, -- 1.361 urban (1.830 adm-urb) per citypopulation.de
["Mianyang"] = {container = "Sichuan"}, -- 1.549 urban (1.800 adm-urb) per citypopulation.de
["Xiangyang"] = {container = "Hubei"}, -- 1.686 urban (1.800 adm-urb) per citypopulation.de
["Huzhou"] = {container = "Zhejiang"}, -- 1.084 urban (1.750 adm-urb) per citypopulation.de
["Puyang"] = {container = "Henan"}, -- 0.824 urban (1.750 adm-urb) per citypopulation.de
["Shangqiu"] = {container = "Henan"}, -- 7.8 prefectural, 1.9 urban (2.8 metro); 1.031 urban (1.750 adm-urb) per citypopulation.de
["Qinhuangdao"] = {container = "Hebei"}, -- 1.520 urban (1.740 adm-urb) per citypopulation.de
["Xingtai"] = {container = "Hebei"}, -- 7.1 prefectural, 971,000 urban; 1.5 urban (1.700 adm-urb) per citypopulation.de
["Nanyang"] = {container = "Henan", wp = "%l, %c"}, -- 9.7 prefectural, 2.1 urban/metro; 1.481 urban (1.680 adm-urb) per citypopulation.de
["Jiaozuo"] = {container = "Henan"}, -- 0.875 urban (1.640 adm-urb) per citypopulation.de
["Jilin City"] = {container = "Jilin"}, -- 1.509 urban (1.610 adm-urb) per citypopulation.de
["Jilin"] = {alias_of = "Jilin City"},
["Jinhua"] = {container = "Zhejiang"}, -- 7.1 prefectural, 1.5 urban; 1.041 urban (1.590 adm-urb) per citypopulation.de
["Shangrao"] = {container = "Jiangxi"}, -- 6.5 prefectural, 2.1 urban, 1.3 metro [sic]; 1.342 urban (1.580 adm-urb) per citypopulation.de
["Heze"] = {container = "Shandong"}, -- 8.8 prefectural, 1.3 urban; 1.294 urban (1.570 adm-urb) per citypopulation.de
["Yulin"] = {container = {key = "Guangxi, China", placetype = "autonomous region"}, wp = "%l, %c"}, -- 0.878 urban (1.570 adm-urb) per citypopulation.de
["Tai'an"] = {container = "Shandong"}, -- 1.417 urban (1.560 adm-urb) per citypopulation.de
["Weihai"] = {container = "Shandong"}, -- 1.340 urban (1.510 adm-urb) per citypopulation.de
-- Taizhou, Jiangsu would be here (1.490 adm-urb) but moved to china_prefecture_level_cities_2 to avoid clash
["Yancheng"] = {container = "Jiangsu"}, -- 6.7 prefectural, 1.6 urban; 1.353 urban (1.460 adm-urb) per citypopulation.de
["Zhangjiakou"] = {container = "Hebei"}, -- 1.339 urban (1.450 adm-urb) per citypopulation.de
["Maoming"] = {container = "Guangdong"}, -- 6.2 prefectural, 2.5 urban; 1.308 urban (1.440 adm-urb) per citypopulation.de
["Nanchong"] = {container = "Sichuan"}, -- 1.254 urban (1.440 adm-urb) per citypopulation.de
["Fuyang"] = {container = "Anhui", wp = "%l, %c"}, -- 8.2 prefectural, 2.1 urban; 1.191 urban (1.410 adm-urb) per citypopulation.de
["Xuchang"] = {container = "Henan"}, -- 0.850 urban (1.390 adm-urb) per citypopulation.de
["Yichang"] = {container = "Hubei"}, -- 1.284 urban (1.390 adm-urb) per citypopulation.de
["Dazhou"] = {container = "Sichuan"}, -- 1.136 urban (1.380 adm-urb) per citypopulation.de
["Kaifeng"] = {container = "Henan"}, -- 1.194 urban (1.340 adm-urb) per citypopulation.de
["Luzhou"] = {container = "Sichuan"}, -- 1.128 urban (1.340 adm-urb) per citypopulation.de
["Qingyuan"] = {container = "Guangdong"}, -- 1.198 urban (1.340 adm-urb) per citypopulation.de
["Huaibei"] = {container = "Anhui"}, -- 0.831 urban (1.330 adm-urb) per citypopulation.de
["Yibin"] = {container = "Sichuan"}, -- 1.101 urban (1.310 adm-urb) per citypopulation.de
["Lu'an"] = {container = "Anhui"}, -- 1.070 urban (1.300 adm-urb) per citypopulation.de
["Dezhou"] = {container = "Shandong"}, -- 0.843 urban (1.290 adm-urb) per citypopulation.de
["Rizhao"] = {container = "Shandong"}, -- 1.147 urban (1.270 adm-urb) per citypopulation.de
["Changzhi"] = {container = "Shanxi"}, -- 1.047 urban (1.250 adm-urb) per citypopulation.de
["Hengyang"] = {container = "Hunan"}, -- 6.6 prefectural, 1.5 urban; 1.185 urban (1.250 adm-urb) per citypopulation.de
["Jinzhou"] = {container = "Liaoning"}, -- 1.021 urban (1.240 adm-urb) per citypopulation.de
["Liaocheng"] = {container = "Shandong"}, -- 1.020 urban (1.240 adm-urb) per citypopulation.de
["Changde"] = {container = "Hunan"}, -- 1.101 urban (1.230 adm-urb) per citypopulation.de
["Suqian"] = {container = "Jiangsu"}, -- 1.082 urban (1.230 adm-urb) per citypopulation.de
["Xinyang"] = {container = "Henan"}, -- 6.2 prefectural, 1.4 urban/metro; 1.015 urban (1.230 adm-urb) per citypopulation.de
["Baoji"] = {container = "Shaanxi"}, -- 1.108 urban (1.220 adm-urb) per citypopulation.de
["Yueyang"] = {container = "Hunan"}, -- 1.125 urban (1.220 adm-urb) per citypopulation.de
["Zhenjiang"] = {container = "Jiangsu"}, -- 1.124 urban (1.210 adm-urb) per citypopulation.de
-- Wanzhou is a "district" of the "direct-administered municipality" of Chongqing but in fact is 142 miles away from Chongqing city proper.
["Wanzhou"] = {placetype = "district", container = {key = "Chongqing", placetype = "direct-administered municipality"}, divs = {"subdistricts", "townships"}, wp = "%l, %c"}, -- 1.078 urban (1.190 adm-urb) per citypopulation.de
["Ulanhad"] = {container = {key = "Inner Mongolia, China", placetype = "autonomous region"}}, -- 1.093 urban (1.180 adm-urb) per citypopulation.de
["Chifeng"] = {alias_of = "Ulanhad"},
["Ulankhad"] = {alias_of = "Ulanhad", display = true},
["Ezhou"] = {container = "Hubei"}, -- < 0.750 urban (1.180 adm-urb) per citypopulation.de
["Zhaoqing"] = {container = "Guangdong"}, -- 1.036 urban (1.160 adm-urb) per citypopulation.de
["Lianyungang"] = {container = "Jiangsu"}, -- 4.599 prefectural, 2.0 urban; 1.071 urban (1.150 adm-urb) per citypopulation.de
["Qujing"] = {container = "Yunnan"}, -- 0.976 urban (1.150 adm-urb) per citypopulation.de
-- Shuyang is a "county" of the "prefecture-level city" of Suqian but in fact is 38 miles away from Suqian city proper (urban core to urban core).
-- The county itself is 37 miles by 34 miles.
["Shuyang"] = {placetype = "county", container = {key = "Suqian", placetype = "prefecture-level city"}, divs = {"subdistricts", "townships"}, wp = "%l County"}, -- 0.986 urban (1.120 adm-urb) per citypopulation.de
-- Yongkang is a "county-level city" of the "prefecture-level city" of Jinhua but in fact is 32 miles away from Jinhua city proper (urban core to urban core).
["Yongkang"] = {placetype = "county-level city", container = {key = "Jinhua", placetype = "prefecture-level city"}, divs = {"subdistricts", "townships"}, wp = "%l, Zhejiang"}, -- < 0.750 urban (1.110 adm-urb) per citypopulation.de
["Zhoukou"] = {container = "Henan"}, -- 9.0 prefectural, 721,000 urban (1.6 metro); < 0.750 urban (1.100 adm-urb) per citypopulation.de
["Beihai"] = {container = {key = "Guangxi, China", placetype = "autonomous region"}}, -- < 1 urban (1.090 adm-urb) per citypopulation.de
["Jiujiang"] = {container = "Jiangxi"}, -- < 0.750 urban (1.080 adm-urb) per citypopulation.de
["Shaoyang"] = {container = "Hunan"}, -- 6.6 prefectural, 802,000 urban, 1.4 metro; < 1 urban (1.080 adm-urb) per citypopulation.de
["Chuzhou"] = {container = "Anhui"}, -- < 0.750 urban (1.070 adm-urb) per citypopulation.de
["Hengshui"] = {container = "Hebei"}, -- 0.885 urban (1.070 adm-urb) per citypopulation.de
["Shiyan"] = {container = "Hubei"}, -- 0.955 urban (1.070 adm-urb) per citypopulation.de
["Huludao"] = {container = "Liaoning"}, -- 0.764 urban (1.060 adm-urb) per citypopulation.de
["Dongying"] = {container = "Shandong"}, -- 0.961 urban (1.050 adm-urb) per citypopulation.de
["Guigang"] = {container = {key = "Guangxi, China", placetype = "autonomous region"}}, -- 0.921 urban (1.050 adm-urb) per citypopulation.de
-- Liuyang is a "county-level city" of the "prefecture-level city" of Changsha but in fact is 47 miles away from Changsha city proper (urban core to urban core).
["Liuyang"] = {placetype = "county-level city", container = {key = "Changsha", placetype = "prefecture-level city"}, divs = {"subdistricts", "townships"}}, -- 0.886 urban (1.040 adm-urb) per citypopulation.de
-- NOTE: Not to be confused with Changzhou in Jiangsu
["Cangzhou"] = {container = "Hebei"}, -- 7.3 prefectural, 621,000 urban; 0.759 urban (1.030 adm-urb) per citypopulation.de
["Liupanshui"] = {container = "Guizhou"}, -- < 0.750 urban (1.030 adm-urb) per citypopulation.de
["Panjin"] = {container = "Liaoning"}, -- 0.980 urban (1.030 adm-urb) per citypopulation.de
["Qiqihar"] = {container = "Heilongjiang"}, -- 1.030 urban (1.030 adm-urb) per citypopulation.de
["Linfen"] = {container = "Shanxi"}, -- < 0.750 urban (1.010 adm-urb) per citypopulation.de
-- Tengzhou is a "county-level city" of the "prefecture-level city" of Zaozhuang but in fact is 30 miles away from Zaozhuang city proper (urban core to urban core).
["Tengzhou"] = {placetype = "county-level city", container = {key = "Zaozhuang", placetype = "prefecture-level city"}, divs = {"subdistricts", "townships"}}, -- 0.937 urban (1.010 adm-urb) per citypopulation.de
-- 3 extra that got added in earlier incarnations and aren't found in the "major agglomerations of the world" page https://citypopulation.de/en/world/agglomerations/ reference date 2025-01-01
["Kunshan"] = {container = "Jiangsu"}, -- 1.652 urban (2020 China census) per citypopulation.de
["Zhumadian"] = {container = "Henan"}, -- 7.0 prefectural, 722,000 urban per Wikipedia; 0.754 urban per citypopulation.de
["Bijie"] = {container = "Guizhou"}, -- 6.9 prefectural, ? urban, ? metro (not listed in Wikipedia); < 0.750 urban per citypopulation.de
}
export.china_prefecture_level_cities_group = {
-- don't do any transformations between key and placename; in particular, don't chop off anything from
-- "Taizhou, Zhejiang" or "Suzhou, Anhui".
key_to_placename = false,
placename_to_key = false, -- don't add ", China" to make the key
default_container = "China",
canonicalize_key_container = make_canonicalize_key_container(", China", "province"),
-- Prefecture-level cities aren't really cities but allow them to be identified that way, as many people
-- don't understand how Chinese administrative divisions work.
default_placetype = {"prefecture-level city", "city"},
default_divs = {
-- "towns" (but not "townships") are automatically added as they are specified as generic_before_non_cities,
-- and prefecture-level cities (as well as county-level cities) are considered non-cities.
"districts", "subdistricts", "townships",
{type = "counties", cat_as = "counties and county-level cities"},
{type = "county-level cities", cat_as = "counties and county-level cities"},
},
data = export.china_prefecture_level_cities,
}
-- Needed to avoid problems with two cities called Taizhou and Suzhou.
export.china_prefecture_level_cities_2 = {
-- NOTE: There is also a larger and better-known prefecture-level city Taizhou in Zhejiang.
["Taizhou, Jiangsu"] = {container = "Jiangsu"}, -- 1.3 urban (1.490 adm-urb) per citypopulation.de 2020 census
["Taizhou"] = {alias_of = "Taizhou, Jiangsu"},
-- NOTE: There is also a larger and better-known prefecture-level city Suzhou in Jiangsu.
["Suzhou, Anhui"] = {container = "Anhui"}, -- 5.3 prefectural, 1.766 metro and "urban"; < 1 urban (1.010 adm-urb) per citypopulation.de 2020 census
-- hopefully this will work because we also have Suzhou as a key by itself for the larger, more-well-known Suzhou in Jiangsu
["Suzhou"] = {alias_of = "Suzhou, Anhui"},
}
export.china_prefecture_level_cities_group_2 = {
-- don't do any transformations between key and placename; in particular, don't chop off anything from
-- "Taizhou, Jiangsu".
placename_to_key = false, -- don't add ", China" to make the key
default_container = "China",
canonicalize_key_container = make_canonicalize_key_container(", China", "province"),
-- Prefecture-level cities aren't really cities but allow them to be identified that way, as many people
-- don't understand how Chinese administrative divisions work.
default_placetype = {"prefecture-level city", "city"},
default_divs = {
-- "towns" (but not "townships") are automatically added as they are specified as generic_before_non_cities,
-- and prefecture-level cities (as well as county-level cities) are considered non-cities.
"districts", "subdistricts", "townships",
{type = "counties", cat_as = "counties and county-level cities"},
{type = "county-level cities", cat_as = "counties and county-level cities"},
},
data = export.china_prefecture_level_cities_2,
}
export.finland_regions = {
["Lapland, Finland"] = {wp = "%l (%c)"},
["North Ostrobothnia, Finland"] = {},
["Northern Ostrobothnia, Finland"] = {alias_of = "North Ostrobothnia, Finland", display = true},
["Kainuu, Finland"] = {},
["North Karelia, Finland"] = {},
["Northern Savonia, Finland"] = {},
["North Savo, Finland"] = {alias_of = "Northern Savonia, Finland", display = true},
["Southern Savonia, Finland"] = {},
["South Savo, Finland"] = {alias_of = "Southern Savonia, Finland", display = true},
["South Karelia, Finland"] = {},
["Central Finland, Finland"] = {},
["South Ostrobothnia, Finland"] = {},
["Southern Ostrobothnia, Finland"] = {alias_of = "South Ostrobothnia, Finland", display = true},
["Ostrobothnia, Finland"] = {wp = "%l (region)"},
["Central Ostrobothnia, Finland"] = {},
["Pirkanmaa, Finland"] = {},
["Satakunta, Finland"] = {},
["Päijänne Tavastia, Finland"] = {},
["Päijät-Häme, Finland"] = {alias_of = "Päijänne Tavastia, Finland", display = true},
["Tavastia Proper, Finland"] = {},
["Kanta-Häme, Finland"] = {alias_of = "Tavastia Proper, Finland", display = true},
["Kymenlaakso, Finland"] = {},
["Uusimaa, Finland"] = {},
["Southwest Finland, Finland"] = {},
["Åland Islands, Finland"] = {the = true, wp = "Åland"},
["Åland, Finland"] = {alias_of = "Åland Islands, Finland"}, -- differs in "the"
}
-- regions of Finland
export.finland_group = {
default_container = "Finland",
default_placetype = "region",
default_divs = "municipalities",
data = export.finland_regions,
}
export.france_administrative_regions = {
["Auvergne-Rhône-Alpes, France"] = {},
["Bourgogne-Franche-Comté, France"] = {},
["Brittany, France"] = {wp = "%l (administrative region)"},
["Centre-Val de Loire, France"] = {},
["Corsica, France"] = {},
-- overseas departments are handled in `export.country_like_entities`
-- ["French Guiana"] = {},
["Grand Est, France"] = {},
-- ["Guadeloupe"] = {},
["Hauts-de-France, France"] = {},
["Île-de-France, France"] = {},
-- ["Martinique"] = {},
-- ["Mayotte"] = {},
["Normandy, France"] = {wp = "%l (administrative region)"},
["Nouvelle-Aquitaine, France"] = {},
["Occitania, France"] = {wp = "%l (administrative region)"},
["Occitanie, France"] = {alias_of = "Occitania, France", display = true},
["Pays de la Loire, France"] = {},
["Provence-Alpes-Côte d'Azur, France"] = {},
-- ["Réunion"] = {},
}
-- administrative regions of France
export.france_group = {
default_container = "France",
-- Canonically these are 'administrative regions' but also treat as 'region' ('administrative region' falls back
-- to 'region').
default_placetype = "region",
default_divs = {
"communes",
{type = "municipalities", cat_as = "communes"},
"departments",
{type = "prefectures", cat_as = {"prefectures", "departmental capitals"}},
{type = "French prefectures", cat_as = {"prefectures", "departmental capitals"}},
},
data = export.france_administrative_regions,
}
export.france_departments = {
["Ain, France"] = {container = "Auvergne-Rhône-Alpes"}, -- 01
["Aisne, France"] = {container = "Hauts-de-France"}, -- 02
["Allier, France"] = {container = "Auvergne-Rhône-Alpes"}, -- 03
["Alpes-de-Haute-Provence, France"] = {container = "Provence-Alpes-Côte d'Azur"}, -- 04
["Hautes-Alpes, France"] = {container = "Provence-Alpes-Côte d'Azur"}, -- 05
["Alpes-Maritimes, France"] = {container = "Provence-Alpes-Côte d'Azur"}, -- 06
["Ardèche, France"] = {container = "Auvergne-Rhône-Alpes"}, -- 07
["Ardennes, France"] = {container = "Grand Est", wp = "%l (department)"}, -- 08
["Ariège, France"] = {container = "Occitania", wp = "%l (department)"}, -- 09
["Aube, France"] = {container = "Grand Est"}, -- 10
["Aude, France"] = {container = "Occitania"}, -- 11
["Aveyron, France"] = {container = "Occitania"}, -- 12
["Bouches-du-Rhône, France"] = {container = "Provence-Alpes-Côte d'Azur"}, -- 13
["Calvados, France"] = {container = "Normandy", wp = "%l (department)"}, -- 14
["Cantal, France"] = {container = "Auvergne-Rhône-Alpes"}, -- 15
["Charente, France"] = {container = "Nouvelle-Aquitaine"}, -- 16
["Charente-Maritime, France"] = {container = "Nouvelle-Aquitaine"}, -- 17
["Cher, France"] = {container = "Centre-Val de Loire", wp = "%l (department)"}, -- 18
["Corrèze, France"] = {container = "Nouvelle-Aquitaine"}, -- 19
["Corse-du-Sud, France"] = {container = "Corsica"}, -- 2A
["Haute-Corse, France"] = {container = "Corsica"}, -- 2B
["Côte-d'Or, France"] = {container = "Bourgogne-Franche-Comté"}, -- 21
["Côte d'Or, France"] = {alias_of = "Côte-d'Or, France", display = true},
["Côtes-d'Armor, France"] = {container = "Brittany"}, -- 22
["Côtes d'Armor, France"] = {alias_of = "Côtes-d'Armor, France", display = true},
["Creuse, France"] = {container = "Nouvelle-Aquitaine"}, -- 23
["Dordogne, France"] = {container = "Nouvelle-Aquitaine"}, -- 24
["Doubs, France"] = {container = "Bourgogne-Franche-Comté"}, -- 25
["Drôme, France"] = {container = "Auvergne-Rhône-Alpes"}, -- 26
["Eure, France"] = {container = "Normandy"}, -- 27
["Eure-et-Loir, France"] = {container = "Centre-Val de Loire"}, -- 28
["Finistère, France"] = {container = "Brittany"}, -- 29
["Gard, France"] = {container = "Occitania"}, -- 30
["Haute-Garonne, France"] = {container = "Occitania"}, -- 31
["Gers, France"] = {container = "Occitania"}, -- 32
["Gironde, France"] = {container = "Nouvelle-Aquitaine"}, -- 33
["Hérault, France"] = {container = "Occitania"}, -- 34
["Ille-et-Vilaine, France"] = {container = "Brittany"}, -- 35
["Indre, France"] = {container = "Centre-Val de Loire"}, -- 36
["Indre-et-Loire, France"] = {container = "Centre-Val de Loire"}, -- 37
["Isère, France"] = {container = "Auvergne-Rhône-Alpes"}, -- 38
["Jura, France"] = {container = "Bourgogne-Franche-Comté", wp = "%l (department)"}, -- 39
["Landes, France"] = {container = "Nouvelle-Aquitaine", wp = "%l (department)"}, -- 40
["Loir-et-Cher, France"] = {container = "Centre-Val de Loire"}, -- 41
["Loire, France"] = {container = "Auvergne-Rhône-Alpes", wp = "%l (department)"}, -- 42
["Haute-Loire, France"] = {container = "Auvergne-Rhône-Alpes"}, -- 43
["Loire-Atlantique, France"] = {container = "Pays de la Loire"}, -- 44
["Loiret, France"] = {container = "Centre-Val de Loire"}, -- 45
["Lot, France"] = {container = "Occitania", wp = "%l (department)"}, -- 46
["Lot-et-Garonne, France"] = {container = "Nouvelle-Aquitaine"}, -- 47
["Lozère, France"] = {container = "Occitania"}, -- 48
["Maine-et-Loire, France"] = {container = "Pays de la Loire"}, -- 49
["Manche, France"] = {container = "Normandy"}, -- 50
["Marne, France"] = {container = "Grand Est", wp = "%l (department)"}, -- 51
["Haute-Marne, France"] = {container = "Grand Est"}, -- 52
["Mayenne, France"] = {container = "Pays de la Loire"}, -- 53
["Meurthe-et-Moselle, France"] = {container = "Grand Est"}, -- 54
["Meuse, France"] = {container = "Grand Est", wp = "%l (department)"}, -- 55
["Morbihan, France"] = {container = "Brittany"}, -- 56
["Moselle, France"] = {container = "Grand Est", wp = "%l (department)"}, -- 57
["Nièvre, France"] = {container = "Bourgogne-Franche-Comté"}, -- 58
["Nord, France"] = {container = "Hauts-de-France", wp = "%l (French department)"}, -- 59
["Oise, France"] = {container = "Hauts-de-France"}, -- 60
["Orne, France"] = {container = "Normandy"}, -- 61
["Pas-de-Calais, France"] = {container = "Hauts-de-France"}, -- 62
["Puy-de-Dôme, France"] = {container = "Auvergne-Rhône-Alpes"}, -- 63
["Pyrénées-Atlantiques, France"] = {container = "Nouvelle-Aquitaine"}, -- 64
["Hautes-Pyrénées, France"] = {container = "Occitania"}, -- 65
["Pyrénées-Orientales, France"] = {container = "Occitania"}, -- 66
["Bas-Rhin, France"] = {container = "Grand Est"}, -- 67
["Haut-Rhin, France"] = {container = "Grand Est"}, -- 68
["Rhône, France"] = {container = "Auvergne-Rhône-Alpes", wp = "%l (department)"}, -- 69D
["Metropolis of Lyon, France"] = {container = "Auvergne-Rhône-Alpes", the = true}, -- 69M
["Lyon Metropolis, France"] = {alias_of = "Metropolis of Lyon, France"},
["Lyon, France"] = {alias_of = "Metropolis of Lyon, France"},
["Haute-Saône, France"] = {container = "Bourgogne-Franche-Comté"}, -- 70
["Saône-et-Loire, France"] = {container = "Bourgogne-Franche-Comté"}, -- 71
["Sarthe, France"] = {container = "Pays de la Loire"}, -- 72
["Savoie, France"] = {container = "Auvergne-Rhône-Alpes"}, -- 73
["Haute-Savoie, France"] = {container = "Auvergne-Rhône-Alpes"}, -- 74
["Paris, France"] = {container = "Île-de-France"}, -- 75
["Seine-Maritime, France"] = {container = "Normandy"}, -- 76
["Seine-et-Marne, France"] = {container = "Île-de-France"}, -- 77
["Yvelines, France"] = {container = "Île-de-France"}, -- 78
["Deux-Sèvres, France"] = {container = "Nouvelle-Aquitaine"}, -- 79
["Somme, France"] = {container = "Hauts-de-France", wp = "%l (department)"}, -- 80
["Tarn, France"] = {container = "Occitania", wp = "%l (department)"}, -- 81
["Tarn-et-Garonne, France"] = {container = "Occitania"}, -- 82
["Var, France"] = {container = "Provence-Alpes-Côte d'Azur", wp = "%l (department)"}, -- 83
["Vaucluse, France"] = {container = "Provence-Alpes-Côte d'Azur"}, -- 84
["Vendée, France"] = {container = "Pays de la Loire"}, -- 85
["Vienne, France"] = {container = "Nouvelle-Aquitaine", wp = "%l (department)"}, -- 86
["Haute-Vienne, France"] = {container = "Nouvelle-Aquitaine"}, -- 87
["Vosges, France"] = {container = "Grand Est", wp = "%l (department)"}, -- 88
["Yonne, France"] = {container = "Bourgogne-Franche-Comté"}, -- 89
["Territoire de Belfort, France"] = {container = "Bourgogne-Franche-Comté"}, -- 90
["Essonne, France"] = {container = "Île-de-France"}, -- 91
["Hauts-de-Seine, France"] = {container = "Île-de-France"}, -- 92
["Seine-Saint-Denis, France"] = {container = "Île-de-France"}, -- 93
["Val-de-Marne, France"] = {container = "Île-de-France"}, -- 94
["Val-d'Oise, France"] = {container = "Île-de-France"}, -- 95
--["Guadeloupe"] = {container = "Guadeloupe"}, -- 971
--["Martinique"] = {container = "Martinique"}, -- 972
--["Guyane"] = {container = "French Guiana", wp = "French Guiana"}, -- 973
--["La Réunion"] = {container = "Réunion", wp = "Réunion"}, -- 974
--["Mayotte"] = {container = "Mayotte"}, -- 976
}
export.france_departments_group = {
placename_to_key = make_placename_to_key(", France"),
canonicalize_key_container = make_canonicalize_key_container(", France", "region"),
default_placetype = "department",
default_divs = {
"communes",
{type = "municipalities", cat_as = "communes"},
},
data = export.france_departments,
}
export.germany_states = {
["Baden-Württemberg, Germany"] = {},
["Bavaria, Germany"] = {},
-- Berlin, Bremen and Hamburg are effectively city-states and don't have districts ([[Kreise]]), so override
-- the default_divs setting. Better not to include them at all since they're included as cities down below.
-- ["Berlin"] = {divs = {}},
["Brandenburg, Germany"] = {},
-- ["Bremen"] = {divs = {}},
-- ["Hamburg"] = {divs = {}},
["Hesse, Germany"] = {},
["Lower Saxony, Germany"] = {},
["Mecklenburg-Vorpommern, Germany"] = {},
["Mecklenburg-Western Pomerania, Germany"] = {alias_of = "Mecklenburg-Vorpommern, Germany", display = true},
["North Rhine-Westphalia, Germany"] = {},
["Rhineland-Palatinate, Germany"] = {},
["Saarland, Germany"] = {},
["Saxony, Germany"] = {},
["Saxony-Anhalt, Germany"] = {},
["Schleswig-Holstein, Germany"] = {},
["Thuringia, Germany"] = {},
}
-- states of Germany
export.germany_group = {
default_container = "Germany",
default_placetype = "state",
default_divs = {"districts", "municipalities"},
data = export.germany_states,
}
export.greece_regions = {
["Attica, Greece"] = {wp = "%l (region)"},
["Central Greece, Greece"] = {wp = "%l (administrative region)"},
["Central Macedonia, Greece"] = {},
["Crete, Greece"] = {},
["Eastern Macedonia and Thrace, Greece"] = {},
["Epirus, Greece"] = {wp = "%l (region)"},
["Ionian Islands, Greece"] = {the = true, wp = "%l (region)"},
["North Aegean, Greece"] = {the = true},
-- I would expect 'the Peloponnese' but Wikipedia mostly has categories like [[w:Category:Geography of Peloponnese (region)]]
-- and [[w:Category:Buildings and structures in Peloponnese (region)]]; only [[w:Category:People from the Peloponnese (region)]]
-- has "the" in it.
["Peloponnese, Greece"] = {wp = "%l (region)"},
["South Aegean, Greece"] = {the = true},
["Thessaly, Greece"] = {},
["Western Greece, Greece"] = {},
["Western Macedonia, Greece"] = {},
["Mount Athos, Greece"] = {placetype = {"autonomous region", "region"}, wp = "Monastic community of Mount Athos"},
}
-- regions of Greece
export.greece_group = {
default_container = "Greece",
default_placetype = "region",
data = export.greece_regions,
}
local india_polity_with_divisions = {"divisions", "districts"}
local india_polity_without_divisions = {"districts"}
-- States and union territories of India. Only some of them are divided into divisions.
export.india_states_and_union_territories = {
["Andaman and Nicobar Islands, India"] =
{the = true, placetype = "union territory", divs = india_polity_without_divisions},
["Andhra Pradesh, India"] = {divs = india_polity_without_divisions},
["Arunachal Pradesh, India"] = {divs = india_polity_with_divisions},
["Assam, India"] = {divs = india_polity_with_divisions},
["Bihar, India"] = {divs = india_polity_with_divisions},
["Chandigarh, India"] = {placetype = "union territory", divs = india_polity_without_divisions},
["Chhattisgarh, India"] = {divs = india_polity_with_divisions},
["Dadra and Nagar Haveli and Daman and Diu, India"] = {placetype = "union territory", divs = india_polity_without_divisions},
["Delhi, India"] = {placetype = "union territory", divs = india_polity_with_divisions},
["Goa, India"] = {divs = india_polity_without_divisions},
["Gujarat, India"] = {divs = india_polity_without_divisions},
["Haryana, India"] = {divs = india_polity_with_divisions},
["Himachal Pradesh, India"] = {divs = india_polity_with_divisions},
["Jammu and Kashmir, India"] = {placetype = "union territory", divs = india_polity_with_divisions,
wp = "%l (union territory)"},
["Jharkhand, India"] = {divs = india_polity_with_divisions},
["Karnataka, India"] = {divs = india_polity_with_divisions},
["Kerala, India"] = {divs = india_polity_without_divisions},
["Ladakh, India"] = {placetype = "union territory", divs = india_polity_with_divisions},
["Lakshadweep, India"] = {placetype = "union territory", divs = india_polity_without_divisions},
["Madhya Pradesh, India"] = {divs = india_polity_with_divisions},
["Maharashtra, India"] = {divs = india_polity_with_divisions},
["Manipur, India"] = {divs = india_polity_without_divisions},
["Meghalaya, India"] = {divs = india_polity_with_divisions},
["Mizoram, India"] = {divs = india_polity_without_divisions},
["Nagaland, India"] = {divs = india_polity_with_divisions},
["Odisha, India"] = {divs = india_polity_with_divisions},
["Puducherry, India"] = {placetype = "union territory", divs = india_polity_without_divisions,
wp = "%l (union territory)"},
["Pondicherry, India"] = {alias_of = "Puducherry, India", display = true},
["Punjab, India"] = {divs = india_polity_with_divisions, wp = "%l, %c"},
["Rajasthan, India"] = {divs = india_polity_with_divisions},
["Sikkim, India"] = {divs = india_polity_without_divisions},
["Tamil Nadu, India"] = {divs = india_polity_without_divisions},
["Telangana, India"] = {divs = india_polity_without_divisions},
["Tripura, India"] = {divs = india_polity_without_divisions},
["Uttar Pradesh, India"] = {divs = india_polity_with_divisions},
["Uttarakhand, India"] = {divs = india_polity_with_divisions},
["West Bengal, India"] = {divs = india_polity_with_divisions},
}
-- states and union territories of India
export.india_group = {
default_container = "India",
default_placetype = "state",
data = export.india_states_and_union_territories,
}
export.indonesia_provinces = {
["Aceh, Indonesia"] = {},
["Bali, Indonesia"] = {},
["Bangka Belitung Islands, Indonesia"] = {the = true},
["Banten, Indonesia"] = {},
["Bengkulu, Indonesia"] = {},
["Central Java, Indonesia"] = {},
["Central Kalimantan, Indonesia"] = {},
["Central Papua, Indonesia"] = {},
["Central Sulawesi, Indonesia"] = {},
["East Java, Indonesia"] = {},
["East Kalimantan, Indonesia"] = {},
["East Nusa Tenggara, Indonesia"] = {},
["Gorontalo, Indonesia"] = {},
["Highland Papua, Indonesia"] = {wp = "%l"},
["Special Capital Region of Jakarta, Indonesia"] = {the = true, wp = "Jakarta"},
["Jakarta, Indonesia"] = {alias_of = "Special Capital Region of Jakarta, Indonesia"},
["Jambi, Indonesia"] = {},
["Lampung, Indonesia"] = {},
["Maluku, Indonesia"] = {},
["North Kalimantan, Indonesia"] = {},
["North Maluku, Indonesia"] = {},
["North Sulawesi, Indonesia"] = {},
["North Papua, Indonesia"] = {},
["North Sumatra, Indonesia"] = {},
["Papua, Indonesia"] = {wp = "%l (province)"},
["Riau, Indonesia"] = {},
["Riau Islands, Indonesia"] = {the = true},
["Southeast Sulawesi, Indonesia"] = {},
["South Kalimantan, Indonesia"] = {},
["South Papua, Indonesia"] = {},
["South Sulawesi, Indonesia"] = {},
["South Sumatra, Indonesia"] = {},
["Southwest Papua, Indonesia"] = {},
["West Java, Indonesia"] = {},
["West Kalimantan, Indonesia"] = {},
["West Nusa Tenggara, Indonesia"] = {},
["West Papua, Indonesia"] = {wp = "%l (province)"},
["West Sulawesi, Indonesia"] = {},
["West Sumatra, Indonesia"] = {},
["Special Region of Yogyakarta, Indonesia"] = {the = true},
["Yogyakarta, Indonesia"] = {alias_of = "Special Region of Yogyakarta, Indonesia"},
}
-- provinces of Indonesia
export.indonesia_group = {
default_container = "Indonesia",
default_placetype = "province",
-- per https://www.quora.com/Does-Indonesia-use-British-or-American-English, Indonesia tends to use American
-- spellings.
data = export.indonesia_provinces,
}
export.iran_provinces = {
["Alborz Province, Iran"] = {}, -- abbreviation AL, capital [[w:Karaj]]
["Ardabil Province, Iran"] = {}, -- abbreviation AR, capital [[w:Ardabil]]
["Bushehr Province, Iran"] = {}, -- abbreviation BU, capital [[w:Bushehr]]
["Chaharmahal and Bakhtiari Province, Iran"] = {}, -- abbreviation CB, capital [[w:Shahr-e Kord]]
["East Azerbaijan Province, Iran"] = {}, -- abbreviation EA, capital [[w:Tabriz]]
["Fars Province, Iran"] = {}, -- abbreviation FA, capital [[w:Shiraz]]
["Pars Province, Iran"] = {alias_of = "Fars Province, Iran", display = true},
["Gilan Province, Iran"] = {}, -- abbreviation GN, capital [[w:Rasht]]
["Golestan Province, Iran"] = {}, -- abbreviation GO, capital [[w:Gorgan]]
["Hamadan Province, Iran"] = {}, -- abbreviation HA, capital [[w:Hamadan]]
["Hormozgan Province, Iran"] = {}, -- abbreviation HO, capital [[w:Bandar Abbas]]
["Ilam Province, Iran"] = {}, -- abbreviation IL, capital [[w:Ilam, Iran|Ilam]]
["Isfahan Province, Iran"] = {}, -- abbreviation IS, capital [[w:Isfahan]]
["Kerman Province, Iran"] = {}, -- abbreviation KN, capital [[w:Kerman]]
["Kermanshah Province, Iran"] = {}, -- abbreviation KE, capital [[w:Kermanshah]]
["Khuzestan Province, Iran"] = {}, -- abbreviation KH, capital [[w:Ahvaz]]
["Kohgiluyeh and Boyer-Ahmad Province, Iran"] = {}, -- abbreviation KB, capital [[w:Yasuj]]
["Kurdistan Province, Iran"] = {}, -- abbreviation KU, capital [[w:Sanandaj]]
["Lorestan Province, Iran"] = {}, -- abbreviation LO, capital [[w:Khorramabad]]
["Markazi Province, Iran"] = {}, -- abbreviation MA, capital [[w:Arak, Iran|Arak]]
["Mazandaran Province, Iran"] = {}, -- abbreviation MN, capital [[w:Sari, Iran|Sari]]
["North Khorasan Province, Iran"] = {}, -- abbreviation NK, capital [[w:Bojnord]]
["Qazvin Province, Iran"] = {}, -- abbreviation QA, capital [[w:Qazvin]]
["Qom Province, Iran"] = {}, -- abbreviation QM, capital [[w:Qom]]
["Razavi Khorasan Province, Iran"] = {}, -- abbreviation RK, capital [[w:Mashhad]]
["Semnan Province, Iran"] = {}, -- abbreviation SE, capital [[w:Semnan, Iran|Semnan]]
["Sistan and Baluchestan Province, Iran"] = {}, -- abbreviation SB, capital [[w:Zahedan]]
["South Khorasan Province, Iran"] = {}, -- abbreviation SK, capital [[w:Birjand]]
["Tehran Province, Iran"] = {}, -- abbreviation TE, capital [[w:Tehran]]
["West Azerbaijan Province, Iran"] = {}, -- abbreviation WA, capital [[w:Urmia]]
["Yazd Province, Iran"] = {}, -- abbreviation YA, capital [[w:Yazd]]
["Zanjan Province, Iran"] = {}, -- abbreviation ZA, capital [[w:Zanjan, Iran|Zanjan]]
}
-- provinces of Iran
export.iran_group = {
key_to_placename = make_key_to_placename(", Iran", " Province$"),
placename_to_key = make_placename_to_key(", Iran", " Province"),
default_container = "Iran",
default_placetype = "province",
-- There aren't nearly enough counties of Iran currently entered in any language to allow for categorizing them
-- per-province. (As of 2025-05-09, there are only 6 counties in each of [[Category:en:Counties of Iran]],
-- [[Category:fa:Counties of Iran]] and [[Category:ar:Counties of Iran]].)
-- default_divs = "counties",
-- For obscure reasons, provinces of Iran, Laos, Thailand and Vietnam use lowercase 'province'
default_wp = "%e province",
data = export.iran_provinces,
}
export.ireland_counties = {
["County Carlow, Ireland"] = {},
["County Cavan, Ireland"] = {},
["County Clare, Ireland"] = {},
["County Cork, Ireland"] = {},
["County Donegal, Ireland"] = {},
["County Dublin, Ireland"] = {},
["County Galway, Ireland"] = {},
["County Kerry, Ireland"] = {},
["County Kildare, Ireland"] = {},
["County Kilkenny, Ireland"] = {},
["County Laois, Ireland"] = {},
["County Leitrim, Ireland"] = {},
["County Limerick, Ireland"] = {},
["County Longford, Ireland"] = {},
["County Louth, Ireland"] = {},
["County Mayo, Ireland"] = {},
["County Meath, Ireland"] = {},
["County Monaghan, Ireland"] = {},
["County Offaly, Ireland"] = {},
["County Roscommon, Ireland"] = {},
["County Sligo, Ireland"] = {},
["County Tipperary, Ireland"] = {},
["County Waterford, Ireland"] = {},
["County Westmeath, Ireland"] = {},
["County Wexford, Ireland"] = {},
["County Wicklow, Ireland"] = {},
}
local function make_irish_type_key_to_placename(container_pattern)
return function(key)
key = key:gsub(container_pattern, "")
local elliptical_key = key:gsub("^County ", "")
return key, elliptical_key
end
end
local function make_irish_type_placename_to_key(container_suffix)
return function(placename)
if not placename:find("^County ") and not placename:find("^City ") then
placename = "County " .. placename
end
return placename .. container_suffix
end
end
-- counties of Ireland
export.ireland_group = {
key_to_placename = make_irish_type_key_to_placename(", Ireland$"),
placename_to_key = make_irish_type_placename_to_key(", Ireland"),
default_container = "Ireland",
default_placetype = "county",
data = export.ireland_counties,
}
export.italy_administrative_regions = {
["Abruzzo, Italy"] = {},
["Aosta Valley, Italy"] = {placetype = {"autonomous region", "administrative region", "region"}},
["Apulia, Italy"] = {},
["Basilicata, Italy"] = {},
["Calabria, Italy"] = {},
["Campania, Italy"] = {},
["Emilia-Romagna, Italy"] = {},
["Friuli-Venezia Giulia, Italy"] = {placetype = {"autonomous region", "administrative region", "region"}},
["Lazio, Italy"] = {},
["Liguria, Italy"] = {},
["Lombardy, Italy"] = {},
["Marche, Italy"] = {},
["Molise, Italy"] = {},
["Piedmont, Italy"] = {},
["Sardinia, Italy"] = {placetype = {"autonomous region", "administrative region", "region"}},
["Sicily, Italy"] = {placetype = {"autonomous region", "administrative region", "region"}},
["Trentino-Alto Adige, Italy"] = {placetype = {"autonomous region", "administrative region", "region"}},
["Tuscany, Italy"] = {},
["Umbria, Italy"] = {},
["Veneto, Italy"] = {},
}
-- administrative regions of Italy
export.italy_group = {
default_container = "Italy",
default_placetype = "region",
data = export.italy_administrative_regions,
}
-- table of Japanese prefectures; interpolated into the main 'places' table, but also needed separately
export.japan_prefectures = {
["Aichi Prefecture, Japan"] = {},
["Akita Prefecture, Japan"] = {},
["Aomori Prefecture, Japan"] = {},
["Chiba Prefecture, Japan"] = {},
["Ehime Prefecture, Japan"] = {},
["Fukui Prefecture, Japan"] = {},
["Fukuoka Prefecture, Japan"] = {},
["Fukushima Prefecture, Japan"] = {},
["Gifu Prefecture, Japan"] = {},
["Gunma Prefecture, Japan"] = {},
["Hiroshima Prefecture, Japan"] = {},
["Hokkaido Prefecture, Japan"] = {divs = "subprefectures", wp = "Hokkaido"},
["Hyōgo Prefecture, Japan"] = {},
["Hyogo Prefecture, Japan"] = {alias_of = "Hyōgo Prefecture, Japan", display = true},
["Ibaraki Prefecture, Japan"] = {},
["Ishikawa Prefecture, Japan"] = {},
["Iwate Prefecture, Japan"] = {},
["Kagawa Prefecture, Japan"] = {},
["Kagoshima Prefecture, Japan"] = {},
["Kanagawa Prefecture, Japan"] = {},
["Kōchi Prefecture, Japan"] = {},
["Kochi Prefecture, Japan"] = {alias_of = "Kōchi Prefecture, Japan", display = true},
["Kumamoto Prefecture, Japan"] = {},
["Kyoto Prefecture, Japan"] = {},
["Mie Prefecture, Japan"] = {},
["Miyagi Prefecture, Japan"] = {},
["Miyazaki Prefecture, Japan"] = {},
["Nagano Prefecture, Japan"] = {},
["Nagasaki Prefecture, Japan"] = {},
["Nara Prefecture, Japan"] = {},
["Niigata Prefecture, Japan"] = {},
["Ōita Prefecture, Japan"] = {},
["Oita Prefecture, Japan"] = {alias_of = "Ōita Prefecture, Japan", display = true},
["Okayama Prefecture, Japan"] = {},
["Okinawa Prefecture, Japan"] = {},
["Osaka Prefecture, Japan"] = {},
["Saga Prefecture, Japan"] = {},
["Saitama Prefecture, Japan"] = {},
["Shiga Prefecture, Japan"] = {},
["Shimane Prefecture, Japan"] = {},
["Shizuoka Prefecture, Japan"] = {},
["Tochigi Prefecture, Japan"] = {},
["Tokushima Prefecture, Japan"] = {},
["Tottori Prefecture, Japan"] = {},
["Toyama Prefecture, Japan"] = {},
["Wakayama Prefecture, Japan"] = {},
["Yamagata Prefecture, Japan"] = {},
["Yamaguchi Prefecture, Japan"] = {},
["Yamanashi Prefecture, Japan"] = {},
}
-- prefectures of Japan
export.japan_group = {
key_to_placename = make_key_to_placename(", Japan$", " Prefecture$"),
placename_to_key = make_placename_to_key(", Japan", " Prefecture"),
default_container = "Japan",
default_placetype = "prefecture",
data = export.japan_prefectures,
}
export.laos_provinces = {
["Attapeu Province, Laos"] = {},
["Bokeo Province, Laos"] = {},
["Bolikhamxai Province, Laos"] = {},
["Champasak Province, Laos"] = {},
["Houaphanh Province, Laos"] = {},
["Khammouane Province, Laos"] = {},
["Luang Namtha Province, Laos"] = {},
["Luang Prabang Province, Laos"] = {},
["Oudomxay Province, Laos"] = {},
["Phongsaly Province, Laos"] = {},
["Salavan Province, Laos"] = {},
["Savannakhet Province, Laos"] = {},
["Vientiane Province, Laos"] = {},
["Vientiane Prefecture, Laos"] = {placetype = "prefecture", wp = "%l"},
["Sainyabuli Province, Laos"] = {},
["Sekong Province, Laos"] = {},
["Xaisomboun Province, Laos"] = {},
["Xiangkhouang Province, Laos"] = {},
}
local function laos_placename_to_key(placename)
if placename == "Vientiane Prefecture" then
return placename .. ", Laos"
end
if placename:find(" Province$") then
return placename .. ", Laos"
end
return placename .. " Province, Laos"
end
-- provinces of Laos
export.laos_group = {
key_to_placename = make_key_to_placename(", Laos$", {" Province$", " Prefecture$"}),
placename_to_key = laos_placename_to_key,
default_container = "Laos",
default_placetype = "province",
-- For obscure reasons, provinces of Iran, Laos, Thailand and Vietnam use lowercase 'province'
default_wp = "%e province",
data = export.laos_provinces,
}
export.lebanon_governorates = {
["Akkar Governorate, Lebanon"] = {},
["Baalbek-Hermel Governorate, Lebanon"] = {},
["Beirut Governorate, Lebanon"] = {},
["Beqaa Governorate, Lebanon"] = {},
["Keserwan-Jbeil Governorate, Lebanon"] = {},
["Mount Lebanon Governorate, Lebanon"] = {},
["Nabatieh Governorate, Lebanon"] = {},
-- These two are generic enough that we don't want to automatically augment a use of `gov/North Governorate` or
-- `gov/South Governorate` with `c/Lebanon`.
["North Governorate, Lebanon"] = {no_auto_augment_container = true},
["South Governorate, Lebanon"] = {no_auto_augment_container = true},
}
-- governorates of Lebanon
export.lebanon_group = {
key_to_placename = make_key_to_placename(", Lebanon$", " Governorate$"),
placename_to_key = make_placename_to_key(", Lebanon", " Governorate"),
default_container = "Lebanon",
default_placetype = "governorate",
data = export.lebanon_governorates,
}
export.malaysia_states = {
["Johor, Malaysia"] = {},
["Kedah, Malaysia"] = {},
["Kelantan, Malaysia"] = {},
["Malacca, Malaysia"] = {},
["Negeri Sembilan, Malaysia"] = {},
["Pahang, Malaysia"] = {},
["Penang, Malaysia"] = {},
["Perak, Malaysia"] = {},
["Perlis, Malaysia"] = {},
["Sabah, Malaysia"] = {},
["Sarawak, Malaysia"] = {},
["Selangor, Malaysia"] = {},
["Terengganu, Malaysia"] = {},
}
-- states of Malaysia
export.malaysia_group = {
default_container = "Malaysia",
default_placetype = "state",
default_wp = "%l, %c",
data = export.malaysia_states,
}
export.malta_regions = {
-- Some of the regions are generic enough that we don't want to automatically augment a use of e.g.
-- `r/Northern Region` with `c/Malta`. In particular;
-- * "Eastern Region" also occurs at least in Ghana, Uganda, Iceland, Nigeria, Venezuela, North Macedonia and
-- El Salvador;
-- * "Northern Region" also occurs at least in Ghana, Uganda, Malawi, Nigeria, Canada and South Africa;
-- * "Western Region" also occurs at least in Abu Dhabi, Bahrain, South Africa, Ghana, Iceland, Nepal, Nigeria,
-- Serbia and Uganda;
-- * "Southern Region" also occurs at least in Nigeria, Eritrea, Iceland, Ireland, Malawi and Serbia.
["Eastern Region, Malta"] = {no_auto_augment_container = true},
["Gozo Region, Malta"] = {wp = "%l"},
["Northern Region, Malta"] = {no_auto_augment_container = true},
["Port Region, Malta"] = {},
["Southern Region, Malta"] = {no_auto_augment_container = true},
["Western Region, Malta"] = {no_auto_augment_container = true},
}
-- regions of Malta
export.malta_group = {
key_to_placename = make_key_to_placename(", Malta$", " Region"),
placename_to_key = make_placename_to_key(", Malta", " Region"),
default_container = "Malta",
default_placetype = "region",
default_wp = "%l, %c",
default_the = true,
data = export.malta_regions,
}
export.mexico_states = {
["Aguascalientes, Mexico"] = {},
["Baja California, Mexico"] = {},
-- not display-canonicalizing because the "Norte" could be for emphasis
["Baja California Norte, Mexico"] = {alias_of = "Baja California, Mexico"},
["Baja California Sur, Mexico"] = {},
["Campeche, Mexico"] = {},
["Chiapas, Mexico"] = {},
["Chihuahua, Mexico"] = {wp = "%l (state)"},
["Coahuila, Mexico"] = {},
["Colima, Mexico"] = {},
["Durango, Mexico"] = {},
["Guanajuato, Mexico"] = {},
["Guerrero, Mexico"] = {},
["Hidalgo, Mexico"] = {wp = "%l (state)"},
["Jalisco, Mexico"] = {},
["State of Mexico, Mexico"] = {the = true},
["Mexico, Mexico"] = {alias_of = "State of Mexico, Mexico"}, -- differs in "the"
-- ["Mexico City, Mexico"] = {}, doesn't belong here because it's a city
["Michoacán, Mexico"] = {},
["Michoacan, Mexico"] = {alias_of = "Michoacán, Mexico", display = true},
["Morelos, Mexico"] = {},
["Nayarit, Mexico"] = {},
["Nuevo León, Mexico"] = {},
["Nuevo Leon, Mexico"] = {alias_of = "Nuevo León, Mexico", display = true},
["Oaxaca, Mexico"] = {},
["Puebla, Mexico"] = {},
["Querétaro, Mexico"] = {},
["Queretaro, Mexico"] = {alias_of = "Querétaro, Mexico", display = true},
["Quintana Roo, Mexico"] = {},
["San Luis Potosí, Mexico"] = {},
["San Luis Potosi, Mexico"] = {alias_of = "San Luis Potosí, Mexico", display = true},
["Sinaloa, Mexico"] = {},
["Sonora, Mexico"] = {},
["Tabasco, Mexico"] = {},
["Tamaulipas, Mexico"] = {},
["Tlaxcala, Mexico"] = {},
["Veracruz, Mexico"] = {},
["Yucatán, Mexico"] = {},
["Yucatan, Mexico"] = {alias_of = "Yucatán, Mexico", display = true},
["Zacatecas, Mexico"] = {},
}
-- Mexican states
export.mexico_group = {
default_container = "Mexico",
default_placetype = "state",
data = export.mexico_states,
}
export.moldova_districts_and_autonomous_territorial_units = {
["Anenii Noi District, Moldova"] = {}, -- capital [[Anenii Noi]]
["Basarabeasca District, Moldova"] = {}, -- capital [[Basarabeasca]]
["Briceni District, Moldova"] = {}, -- capital [[Briceni]]
["Cahul District, Moldova"] = {}, -- capital [[Cahul]]
["Cantemir District, Moldova"] = {}, -- capital [[Cantemir, Moldova|Cantemir]]
["Călărași District, Moldova"] = {}, -- capital [[Călărași, Moldova|Călărași]]
["Căușeni District, Moldova"] = {}, -- capital [[Căușeni]]
["Cimișlia District, Moldova"] = {}, -- capital [[Cimișlia]]
["Criuleni District, Moldova"] = {}, -- capital [[Criuleni]]
["Dondușeni District, Moldova"] = {}, -- capital [[Dondușeni]]
["Drochia District, Moldova"] = {}, -- capital [[Drochia]]
["Dubăsari District, Moldova"] = {}, -- capital [[Cocieri]]
["Edineț District, Moldova"] = {}, -- capital [[Edineț]]
["Fălești District, Moldova"] = {}, -- capital [[Fălești]]
["Florești District, Moldova"] = {}, -- capital [[Florești, Moldova|Florești]]
["Glodeni District, Moldova"] = {}, -- capital [[Glodeni]]
["Hîncești District, Moldova"] = {}, -- capital [[Hîncești]]
["Ialoveni District, Moldova"] = {}, -- capital [[Ialoveni]]
["Leova District, Moldova"] = {}, -- capital [[Leova]]
["Nisporeni District, Moldova"] = {}, -- capital [[Nisporeni]]
["Ocnița District, Moldova"] = {}, -- capital [[Ocnița]]
["Orhei District, Moldova"] = {}, -- capital [[Orhei]]
["Rezina District, Moldova"] = {}, -- capital [[Rezina]]
["Rîșcani District, Moldova"] = {}, -- capital [[Rîșcani]]
["Sîngerei District, Moldova"] = {}, -- capital [[Sîngerei]]
["Soroca District, Moldova"] = {}, -- capital [[Soroca]]
["Strășeni District, Moldova"] = {}, -- capital [[Strășeni]]
["Șoldănești District, Moldova"] = {}, -- capital [[Șoldănești]]
["Ștefan Vodă District, Moldova"] = {}, -- capital [[Ștefan Vodă]]
["Taraclia District, Moldova"] = {}, -- capital [[Taraclia]]
["Telenești District, Moldova"] = {}, -- capital [[Telenești]]
["Ungheni District, Moldova"] = {}, -- capital [[Ungheni]]
["Chișinău, Moldova"] = {placetype = "municipality"},
["Bălți, Moldova"] = {placetype = "municipality"},
["Gagauzia, Moldova"] = {placetype = {"autonomous territorial unit", "autonomous region", "region"}}, -- capital [[Comrat]]
-- the remainder are under the de-facto control of the unrecognized state of Transnistria
["Bender, Moldova"] = {placetype = "municipality"},
["Tighina, Moldova"] = {alias_of = "Bender, Moldova"},
["Transnistria, Moldova"] = {placetype = {"autonomous territorial unit", "autonomous region", "region"}}, -- capital [[Tiraspol]]
["Left Bank of the Dniester, Moldova"] = {alias_of = "Transnistria, Moldova", the = true},
["Administrative-Territorial Units of the Left Bank of the Dniester, Moldova"] = {alias_of = "Transnistria, Moldova", the = true},
}
local function moldova_placename_to_key(placename)
local elliptical_key = placename .. ", Moldova"
if export.moldova_districts_and_autonomous_territorial_units[elliptical_key] then
return elliptical_key
end
if placename:find(" District$") then
return placename .. ", Moldova"
end
return placename .. " District, Moldova"
end
-- Moldovan districts (raions) and autonomous territorial units
export.moldova_group = {
key_to_placename = make_key_to_placename(", Moldova$", " District"),
placename_to_key = moldova_placename_to_key,
default_container = "Moldova",
default_placetype = {"district", "raion"},
default_divs = "communes",
data = export.moldova_districts_and_autonomous_territorial_units,
}
export.morocco_regions = {
["Tangier-Tetouan-Al Hoceima, Morocco"] = {},
["Oriental, Morocco"] = {wp = "%l (%c)"},
["L'Oriental, Morocco"] = {alias_of = "Oriental, Morocco", display = true},
["Fez-Meknes, Morocco"] = {},
["Rabat-Sale-Kenitra, Morocco"] = {wp = "Rabat-Salé-Kénitra"},
["Rabat-Salé-Kénitra, Morocco"] = {alias_of = "Rabat-Sale-Kenitra, Morocco", display = true},
["Beni Mellal-Khenifra, Morocco"] = {wp = "Béni Mellal-Khénifra"},
["Béni Mellal-Khénifra, Morocco"] = {alias_of = "Beni Mellal-Khenifra, Morocco", display = true},
["Casablanca-Settat, Morocco"] = {},
["Marrakesh-Safi, Morocco"] = {wp = "Marrakesh–Safi"}, -- WP title has en-dash
["Marrakech-Safi, Morocco"] = {alias_of = "Marrakesh-Safi, Morocco", display = true},
["Draa-Tafilalet, Morocco"] = {wp = "Drâa-Tafilalet"},
["Drâa-Tafilalet, Morocco"] = {alias_of = "Draa-Tafilalet, Morocco", display = true},
["Souss-Massa, Morocco"] = {},
["Guelmim-Oued Noun, Morocco"] = {
keydesc = "+++. '''NOTE:''' This region lies partly within the disputed territory of [[Western Sahara]]"
},
["Laayoune-Sakia El Hamra, Morocco"] = {
wp = "Laâyoune-Sakia El Hamra",
keydesc = "+++. '''NOTE:''' This region lies almost completely within the disputed territory of [[Western Sahara]]",
},
["Laâyoune-Sakia El Hamra, Morocco"] = {alias_of = "Laayoune-Sakia El Hamra, Morocco", display = true},
["Dakhla-Oued Ed-Dahab, Morocco"] = {
keydesc = "+++. '''NOTE:''' This region lies completely within the disputed territory of [[Western Sahara]]",
},
}
-- regions of Morocco
export.morocco_group = {
default_container = "Morocco",
default_placetype = "region",
data = export.morocco_regions,
}
export.egypt_governorates = {
["Cairo Governorate, Egypt"] = {},
["Giza Governorate, Egypt"] = {},
["Sharqia Governorate, Egypt"] = {},
["Dakahlia Governorate, Egypt"] = {},
["Beheira Governorate, Egypt"] = {},
["Minya Governorate, Egypt"] = {},
["Qalyubia Governorate, Egypt"] = {},
["Sohag Governorate, Egypt"] = {},
["Alexandria Governorate, Egypt"] = {},
["Gharbia Governorate, Egypt"] = {},
["Asyut Governorate, Egypt"] = {},
["Monufia Governorate, Egypt"] = {},
["Faiyum Governorate, Egypt"] = {},
["Kafr El Sheikh Governorate, Egypt"] = {},
["Qena Governorate, Egypt"] = {},
["Beni Suef Governorate, Egypt"] = {},
["Damietta Governorate, Egypt"] = {},
["Aswan Governorate, Egypt"] = {},
["Ismailia Governorate, Egypt"] = {},
["Luxor Governorate, Egypt"] = {},
["Suez Governorate, Egypt"] = {},
["Port Said Governorate, Egypt"] = {},
["Matrouh Governorate, Egypt"] = {},
["North Sinai Governorate, Egypt"] = {},
["Red Sea Governorate, Egypt"] = {},
["New Valley Governorate, Egypt"] = {},
["South Sinai Governorate, Egypt"] = {},
}
-- governorates of Egypt
export.egypt_group = {
key_to_placename = make_key_to_placename(", Egypt$", " Governorate$"),
placename_to_key = make_placename_to_key(", Egypt", " Governorate"),
default_container = "Egypt",
default_placetype = "governorate",
data = export.egypt_governorates,
}
export.netherlands_provinces = {
["Drenthe, Netherlands"] = {},
["Flevoland, Netherlands"] = {},
["Friesland, Netherlands"] = {},
["Gelderland, Netherlands"] = {},
["Groningen, Netherlands"] = {wp = "%l (province)"},
["Limburg, Netherlands"] = {wp = "%l (%c)"},
["North Brabant, Netherlands"] = {},
-- Foreign forms get display-canonicalized.
["Noord-Brabant, Netherlands"] = {alias_of = "North Brabant, Netherlands", display = true},
["North Holland, Netherlands"] = {},
["Noord-Holland, Netherlands"] = {alias_of = "North Holland, Netherlands", display = true},
["Overijssel, Netherlands"] = {},
["South Holland, Netherlands"] = {},
["Zuid-Holland, Netherlands"] = {alias_of = "South Holland, Netherlands", display = true},
["Utrecht, Netherlands"] = {wp = "%l (province)"},
["Zeeland, Netherlands"] = {},
}
-- provinces of the Netherlands
export.netherlands_group = {
default_container = "Netherlands",
default_placetype = "province",
default_divs = "municipalities",
data = export.netherlands_provinces,
}
export.new_zealand_regions = {
-- North Island regions
["Northland, New Zealand"] = {wp = "%l Region"}, -- ISO 3166-2 code NZ-NTL, number 1, capital [[Whangārei]]
["Auckland, New Zealand"] = {wp = "%l Region"}, -- ISO 3166-2 code NZ-AUK, number 2, capital [[Auckland]]
["Waikato, New Zealand"] = {}, -- ISO 3166-2 code NZ-WKO, number 3, capital [[Hamilton, New Zealand|Hamilton]]
["Bay of Plenty, New Zealand"] = {the = true, wp = "%l Region"}, -- ISO 3166-2 code NZ-BOP, number 4, capital [[Whakatāne]]
["Gisborne, New Zealand"] = {placetype = {"region", "district"}, wp = "%l District"}, -- ISO 3166-2 code NZ-GIS, number 5, capital [[Gisborne, New Zealand|Gisborne]]
["Hawke's Bay, New Zealand"] = {}, -- ISO 3166-2 code NZ-HKB, number 6, capital [[Napier, New Zealand|Napier]]
["Taranaki, New Zealand"] = {}, -- ISO 3166-2 code NZ-TKI, number 7, capital [[Stratford, New Zealand|Stratford]]
["Manawatū-Whanganui, New Zealand"] = {}, -- ISO 3166-2 code NZ-MWT, number 8, capital [[Palmerston North]]
["Manawatu-Whanganui, New Zealand"] = {alias_of = "Manawatū-Whanganui, New Zealand", display = true},
["Manawatu-Wanganui, New Zealand"] = {alias_of = "Manawatū-Whanganui, New Zealand", display = true},
["Wellington, New Zealand"] = {wp = "%l Region"}, -- ISO 3166-2 code NZ-WGN, number 9, capital [[Wellington]]
-- South Island regions
["Tasman, New Zealand"] = {placetype = {"region", "district"}, wp = "%l District"}, -- ISO 3166-2 code NZ-TAS, number 10, capital [[Richmond, New Zealand|Richmond]]
["Nelson, New Zealand"] = {placetype = {"region", "city"}, wp = "%l, %c", is_city = true}, -- ISO 3166-2 code NZ-NSN, number 11, capital [[Nelson, New Zealand|Nelson]]
["Marlborough, New Zealand"] = {placetype = {"region", "district"}, wp = "%l District"}, -- ISO 3166-2 code NZ-MBH, number 12, capital [[Blenheim, New Zealand|Blenheim]]
["West Coast, New Zealand"] = {the = true, wp = "%l Region"}, -- ISO 3166-2 code NZ-WTC, number 13, capital [[Greymouth]]
["Canterbury, New Zealand"] = {wp = "%l Region"}, -- ISO 3166-2 code NZ-CAN, number 14, capital [[Christchurch]]
["Otago, New Zealand"] = {}, -- ISO 3166-2 code NZ-OTA, number 15, capital [[Dunedin]]
["Southland, New Zealand"] = {wp = "%l Region"}, -- ISO 3166-2 code NZ-STL, number 16, capital [[Invercargill]]
}
-- regions of New Zealand
export.new_zealand_group = {
default_container = "New Zealand",
default_placetype = "region",
data = export.new_zealand_regions,
}
export.nigeria_states = {
["Abia State, Nigeria"] = {},
["Adamawa State, Nigeria"] = {},
["Akwa Ibom State, Nigeria"] = {},
["Anambra State, Nigeria"] = {},
["Bauchi State, Nigeria"] = {},
["Bayelsa State, Nigeria"] = {},
["Benue State, Nigeria"] = {},
["Borno State, Nigeria"] = {},
["Cross River State, Nigeria"] = {},
["Delta State, Nigeria"] = {},
["Ebonyi State, Nigeria"] = {},
["Edo State, Nigeria"] = {},
["Ekiti State, Nigeria"] = {},
["Enugu State, Nigeria"] = {},
["Federal Capital Territory, Nigeria"] = {
-- not a state but allow it to be referenced as one in holonyms
placetype = {"federal territory", "territory", "state"}, the = true, wp = "%l (%c)",
},
["Gombe State, Nigeria"] = {},
["Imo State, Nigeria"] = {},
["Jigawa State, Nigeria"] = {},
["Kaduna State, Nigeria"] = {},
["Kano State, Nigeria"] = {},
["Katsina State, Nigeria"] = {},
["Kebbi State, Nigeria"] = {},
["Kogi State, Nigeria"] = {},
["Kwara State, Nigeria"] = {},
["Lagos State, Nigeria"] = {},
["Nasarawa State, Nigeria"] = {},
["Niger State, Nigeria"] = {},
["Ogun State, Nigeria"] = {},
["Ondo State, Nigeria"] = {},
["Osun State, Nigeria"] = {},
["Oyo State, Nigeria"] = {},
["Plateau State, Nigeria"] = {},
["Rivers State, Nigeria"] = {},
["Sokoto State, Nigeria"] = {},
["Taraba State, Nigeria"] = {},
["Yobe State, Nigeria"] = {},
["Zamfara State, Nigeria"] = {},
}
-- states of Nigeria
export.nigeria_group = {
key_to_placename = make_key_to_placename(", Nigeria$", " State$"),
placename_to_key = make_placename_to_key(", Nigeria", " State"),
default_container = "Nigeria",
default_placetype = "state",
data = export.nigeria_states,
}
export.north_korea_provinces = {
["Chagang Province, North Korea"] = {},
["North Hamgyong Province, North Korea"] = {},
["South Hamgyong Province, North Korea"] = {},
["North Hwanghae Province, North Korea"] = {},
["South Hwanghae Province, North Korea"] = {},
["Kangwon Province, North Korea"] = {wp = "%l (%c)"},
["North Pyongan Province, North Korea"] = {},
["South Pyongan Province, North Korea"] = {},
["Ryanggang Province, North Korea"] = {},
}
-- provinces of North Korea
export.north_korea_group = {
key_to_placename = make_key_to_placename(", North Korea$", " Province$"),
placename_to_key = make_placename_to_key(", North Korea", " Province"),
default_container = "North Korea",
default_placetype = "province",
data = export.north_korea_provinces,
}
export.norwegian_counties = {
["Oslo, Norway"] = {},
["Rogaland, Norway"] = {},
["Møre og Romsdal, Norway"] = {},
["Nordland, Norway"] = {},
["Østfold, Norway"] = {},
["Akershus, Norway"] = {},
["Buskerud, Norway"] = {},
-- the following two were merged into Innlandet
-- ["Hedmark, Norway"] = {},
-- ["Oppland, Norway"] = {},
["Innlandet, Norway"] = {},
["Vestfold, Norway"] = {},
["Telemark, Norway"] = {},
-- the following two were merged into Agder
-- ["Aust-Agder, Norway"] = {},
-- ["Vest-Agder, Norway"] = {},
["Agder, Norway"] = {},
-- the following two were merged into Vestland
-- ["Hordaland, Norway"] = {},
-- ["Sogn og Fjordane, Norway"] = {},
["Vestland, Norway"] = {},
["Trøndelag, Norway"] = {},
["Troms, Norway"] = {},
["Finnmark, Norway"] = {},
}
-- counties of Norway
export.norway_group = {
default_container = "Norway",
default_placetype = "county",
data = export.norwegian_counties,
}
export.pakistan_provinces_and_territories = {
["Azad Kashmir, Pakistan"] = {
placetype = {"administrative territory", "autonomous territory", "territory"},
},
["Azad Jammu and Kashmir, Pakistan"] = {alias_of = "Azad Kashmir, Pakistan", display = true},
["Balochistan, Pakistan"] = {wp = "%l, %c"},
["Gilgit-Baltistan, Pakistan"] = {
placetype = {"administrative territory", "territory"},
},
["Islamabad Capital Territory, Pakistan"] = {
the = true,
divs = {}, -- no divisions
placetype = {"federal territory", "administrative territory", "territory"},
},
-- Islamabad is an accepted alias for Islamabad Capital Territory given the above placetypes
["Islamabad, Pakistan"] = {alias_of = "Islamabad Capital Territory, Pakistan"},
["Khyber Pakhtunkhwa, Pakistan"] = {},
["Punjab, Pakistan"] = {wp = "%l, %c"},
["Sindh, Pakistan"] = {},
}
-- provinces and territories of Pakistan
export.pakistan_group = {
default_container = "Pakistan",
default_placetype = "province",
default_divs = "divisions",
data = export.pakistan_provinces_and_territories,
}
export.philippines_provinces = {
["Abra, Philippines"] = {wp = "%l (province)"},
["Agusan del Norte, Philippines"] = {},
["Agusan del Sur, Philippines"] = {},
["Aklan, Philippines"] = {},
["Albay, Philippines"] = {},
["Antique, Philippines"] = {wp = "%l (province)"},
["Apayao, Philippines"] = {},
["Aurora, Philippines"] = {wp = "%l (province)"},
["Basilan, Philippines"] = {},
["Bataan, Philippines"] = {},
["Batanes, Philippines"] = {},
["Batangas, Philippines"] = {},
["Benguet, Philippines"] = {},
["Biliran, Philippines"] = {},
["Bohol, Philippines"] = {},
["Bukidnon, Philippines"] = {},
["Bulacan, Philippines"] = {},
["Cagayan, Philippines"] = {},
["Camarines Norte, Philippines"] = {},
["Camarines Sur, Philippines"] = {},
["Camiguin, Philippines"] = {},
["Capiz, Philippines"] = {},
["Catanduanes, Philippines"] = {},
["Cavite, Philippines"] = {},
["Cebu, Philippines"] = {},
["Cotabato, Philippines"] = {},
["Davao de Oro, Philippines"] = {},
["Davao del Norte, Philippines"] = {},
["Davao del Sur, Philippines"] = {},
["Davao Occidental, Philippines"] = {},
["Davao Oriental, Philippines"] = {},
["Dinagat Islands, Philippines"] = {the = true},
["Eastern Samar, Philippines"] = {},
["Guimaras, Philippines"] = {},
["Ifugao, Philippines"] = {},
["Ilocos Norte, Philippines"] = {},
["Ilocos Sur, Philippines"] = {},
["Iloilo, Philippines"] = {},
["Isabela, Philippines"] = {wp = "%l (province)"},
["Kalinga, Philippines"] = {wp = "%l (province)"},
["La Union, Philippines"] = {},
["Laguna, Philippines"] = {wp = "%l (province)"},
["Lanao del Norte, Philippines"] = {},
["Lanao del Sur, Philippines"] = {},
["Leyte, Philippines"] = {wp = "%l (province)"},
["Maguindanao del Norte, Philippines"] = {},
["Maguindanao del Sur, Philippines"] = {},
["Marinduque, Philippines"] = {},
["Masbate, Philippines"] = {},
["Misamis Occidental, Philippines"] = {},
["Misamis Oriental, Philippines"] = {},
["Mountain Province, Philippines"] = {},
["Negros Occidental, Philippines"] = {},
["Negros Oriental, Philippines"] = {},
["Northern Samar, Philippines"] = {},
["Nueva Ecija, Philippines"] = {},
["Nueva Vizcaya, Philippines"] = {},
["Occidental Mindoro, Philippines"] = {},
["Oriental Mindoro, Philippines"] = {},
["Palawan, Philippines"] = {},
["Pampanga, Philippines"] = {},
["Pangasinan, Philippines"] = {},
["Quezon, Philippines"] = {},
["Quirino, Philippines"] = {},
["Rizal, Philippines"] = {wp = "%l (province)"},
["Romblon, Philippines"] = {},
["Samar, Philippines"] = {wp = "%l (province)"},
["Sarangani, Philippines"] = {},
["Siquijor, Philippines"] = {},
["Sorsogon, Philippines"] = {},
["South Cotabato, Philippines"] = {},
["Southern Leyte, Philippines"] = {},
["Sultan Kudarat, Philippines"] = {},
["Sulu, Philippines"] = {},
["Surigao del Norte, Philippines"] = {},
["Surigao del Sur, Philippines"] = {},
["Tarlac, Philippines"] = {},
["Tawi-Tawi, Philippines"] = {},
["Zambales, Philippines"] = {},
["Zamboanga del Norte, Philippines"] = {},
["Zamboanga del Sur, Philippines"] = {},
["Zamboanga Sibugay, Philippines"] = {},
-- not a province but treated as one; allow it to be referred to as a province in holonyms
["Metro Manila, Philippines"] = {placetype = {"region", "province"}},
}
-- provinces of the Philippines
export.philippines_group = {
default_container = "Philippines",
default_placetype = "province",
default_divs = {"municipalities", "barangays"},
data = export.philippines_provinces,
}
export.poland_voivodeships = {
["Lower Silesian Voivodeship, Poland"] = {}, -- abbr DS, code 02, capital Wrocław
["Kuyavian-Pomeranian Voivodeship, Poland"] = {}, -- abbr KP, code 04, capital Bydgoszcz (seat of voivode), Toruń (seat of sejmik and marshal)
["Lublin Voivodeship, Poland"] = {}, -- abbr LU, code 06, capital Lublin
["Lubusz Voivodeship, Poland"] = {}, -- abbr LB, code 08, capital Gorzów Wielkopolski (seat of voivode), Zielona Góra (seat of sejmik and marshal)
["Lodz Voivodeship, Poland"] = {wp = "Łódź Voivodeship"}, -- abbr LD, code 10, capital Łódź
["Łódź Voivodeship, Poland"] = {alias_of = "Lodz Voivodeship, Poland", display = true, display_as_full = true},
["Lesser Poland Voivodeship, Poland"] = {}, -- abbr MA, code 12, capital Kraków
["Masovian Voivodeship, Poland"] = {}, -- abbr MZ, code 14, capital Warsaw
["Opole Voivodeship, Poland"] = {}, -- abbr OP, code 16, capital Opole
["Subcarpathian Voivodeship, Poland"] = {}, -- abbr PK, code 18, capital Rzeszów
["Podlaskie Voivodeship, Poland"] = {}, -- abbr PD, code 20, capital Białystok
["Pomeranian Voivodeship, Poland"] = {}, -- abbr PM, code 22, capital Gdańsk
["Silesian Voivodeship, Poland"] = {}, -- abbr SL, code 24, capital Katowice
["Holy Cross Voivodeship, Poland"] = {wp = "Świętokrzyskie Voivodeship"}, -- abbr SK, code 26, capital Kielce
["Świętokrzyskie Voivodeship, Poland"] = {alias_of = "Holy Cross Voivodeship, Poland", display = true, display_as_full = true},
["Warmian-Masurian Voivodeship, Poland"] = {}, -- abbr WN, code 28, capital Olsztyn
["Greater Poland Voivodeship, Poland"] = {}, -- abbr WP, code 30, capital Poznań
["West Pomeranian Voivodeship, Poland"] = {}, -- abbr ZP, code 32, capital Szczecin
}
-- voivodeships of Poland
export.poland_group = {
key_to_placename = make_key_to_placename(", Poland$", " Voivodeship$"),
placename_to_key = make_placename_to_key(", Poland", " Voivodeship"),
default_container = "Poland",
default_placetype = "voivodeship",
default_divs = {
-- "counties", -- not enough of them currently
{type = "Polish colonies", cat_as = {{type = "villages", prep = "in"}}},
},
data = export.poland_voivodeships,
}
export.portugal_districts_and_autonomous_regions = {
["Azores, Portugal"] = {the = true, placetype = {"autonomous region", "region"}},
["Aveiro District, Portugal"] = {},
["Beja District, Portugal"] = {},
["Braga District, Portugal"] = {},
["Bragança District, Portugal"] = {},
["Castelo Branco District, Portugal"] = {},
["Coimbra District, Portugal"] = {},
["Évora District, Portugal"] = {},
["Faro District, Portugal"] = {},
["Guarda District, Portugal"] = {},
["Leiria District, Portugal"] = {},
["Lisbon District, Portugal"] = {},
["Lisboa District, Portugal"] = {alias_of = "Lisbon District, Portugal", display = true},
["Madeira, Portugal"] = {placetype = {"autonomous region", "region"}},
["Portalegre District, Portugal"] = {},
["Porto District, Portugal"] = {},
["Santarém District, Portugal"] = {},
["Setúbal District, Portugal"] = {},
["Viana do Castelo District, Portugal"] = {},
["Vila Real District, Portugal"] = {},
["Viseu District, Portugal"] = {},
}
local function portugal_placename_to_key(placename)
if placename == "Azores" or placename == "Madeira" then
return placename .. ", Portugal"
end
if placename:find(" District$") then
return placename .. ", Portugal"
end
return placename .. " District, Portugal"
end
-- districts and autonomous regions of Portugal
export.portugal_group = {
key_to_placename = make_key_to_placename(", Portugal$", " District$"),
placename_to_key = portugal_placename_to_key,
default_container = "Portugal",
default_placetype = "district",
default_divs = "municipalities",
data = export.portugal_districts_and_autonomous_regions,
}
export.romania_counties = {
["Alba County, Romania"] = {},
["Arad County, Romania"] = {},
["Argeș County, Romania"] = {},
["Bacău County, Romania"] = {},
["Bihor County, Romania"] = {},
["Bistrița-Năsăud County, Romania"] = {},
["Botoșani County, Romania"] = {},
["Brașov County, Romania"] = {},
["Brăila County, Romania"] = {},
-- Bucharest: not in a county
["Buzău County, Romania"] = {},
["Caraș-Severin County, Romania"] = {},
["Cluj County, Romania"] = {},
["Constanța County, Romania"] = {},
["Covasna County, Romania"] = {},
["Călărași County, Romania"] = {},
["Dolj County, Romania"] = {},
["Dâmbovița County, Romania"] = {},
["Galați County, Romania"] = {},
["Giurgiu County, Romania"] = {},
["Gorj County, Romania"] = {},
["Harghita County, Romania"] = {},
["Hunedoara County, Romania"] = {},
["Ialomița County, Romania"] = {},
["Iași County, Romania"] = {},
["Ilfov County, Romania"] = {},
["Maramureș County, Romania"] = {},
["Mehedinți County, Romania"] = {},
["Mureș County, Romania"] = {},
["Neamț County, Romania"] = {},
["Olt County, Romania"] = {},
["Prahova County, Romania"] = {},
["Satu Mare County, Romania"] = {},
["Sibiu County, Romania"] = {},
["Suceava County, Romania"] = {},
["Sălaj County, Romania"] = {},
["Teleorman County, Romania"] = {},
["Timiș County, Romania"] = {},
["Tulcea County, Romania"] = {},
["Vaslui County, Romania"] = {},
["Vrancea County, Romania"] = {},
["Vâlcea County, Romania"] = {},
}
-- counties of Romania
export.romania_group = {
key_to_placename = make_key_to_placename(", Romania$", " County$"),
placename_to_key = make_placename_to_key(", Romania", " County"),
default_container = "Romania",
default_placetype = "county",
default_divs = "communes",
data = export.romania_counties,
}
local function make_russia_federal_subject_spec(spectype, use_the, wp)
return {
placetype = spectype,
the = not not use_the,
bare_category_parent_type = {"federal subjects", spectype .. "s"},
wp = wp,
}
end
local russia_autonomous_okrug_no_the =
{placetype = {"autonomous okrug", "okrug"}, bare_category_parent_type = {"federal subjects", "autonomous okrugs"}}
local russia_autonomous_okrug_the =
{placetype = {"autonomous okrug", "okrug"}, bare_category_parent_type = {"federal subjects", "autonomous okrugs"},
the = true}
local russia_krai = make_russia_federal_subject_spec("krai")
local russia_oblast = make_russia_federal_subject_spec("oblast")
local russia_republic_the = make_russia_federal_subject_spec("republic", "use the")
local russia_republic_no_the = make_russia_federal_subject_spec("republic")
export.russia_federal_subjects = {
-- autonomous oblasts
["Jewish Autonomous Oblast, Russia"] =
{the = true, placetype = {"autonomous oblast", "oblast"},
bare_category_parent_type = {"federal subjects", "autonomous oblasts"}},
-- autonomous okrugs
["Chukotka Autonomous Okrug, Russia"] = russia_autonomous_okrug_the,
["Chukotka, Russia"] = {alias_of = "Chukotka Autonomous Okrug, Russia"},
["Khanty-Mansi Autonomous Okrug, Russia"] = russia_autonomous_okrug_the,
["Khanty-Mansia, Russia"] = {alias_of = "Khanty-Mansi Autonomous Okrug, Russia"},
["Khantia-Mansia, Russia"] = {alias_of = "Khanty-Mansi Autonomous Okrug, Russia"},
["Yugra, Russia"] = {alias_of = "Khanty-Mansi Autonomous Okrug, Russia"},
["Nenets Autonomous Okrug, Russia"] = russia_autonomous_okrug_the,
["Nenetsia, Russia"] = {alias_of = "Nenets Autonomous Okrug, Russia"},
["Yamalo-Nenets Autonomous Okrug, Russia"] = russia_autonomous_okrug_the,
["Yamalia, Russia"] = {alias_of = "Yamalo-Nenets Autonomous Okrug, Russia"},
-- krais
["Altai Krai, Russia"] = russia_krai,
["Kamchatka Krai, Russia"] = russia_krai,
["Khabarovsk Krai, Russia"] = russia_krai,
["Krasnodar Krai, Russia"] = russia_krai,
["Krasnoyarsk Krai, Russia"] = russia_krai,
["Perm Krai, Russia"] = russia_krai,
["Primorsky Krai, Russia"] = russia_krai,
["Stavropol Krai, Russia"] = russia_krai,
["Zabaykalsky Krai, Russia"] = russia_krai,
-- oblasts
["Amur Oblast, Russia"] = russia_oblast,
["Arkhangelsk Oblast, Russia"] = russia_oblast,
["Astrakhan Oblast, Russia"] = russia_oblast,
["Belgorod Oblast, Russia"] = russia_oblast,
["Bryansk Oblast, Russia"] = russia_oblast,
["Chelyabinsk Oblast, Russia"] = russia_oblast,
["Irkutsk Oblast, Russia"] = russia_oblast,
["Ivanovo Oblast, Russia"] = russia_oblast,
["Kaliningrad Oblast, Russia"] = russia_oblast,
["Kaluga Oblast, Russia"] = russia_oblast,
["Kemerovo Oblast, Russia"] = russia_oblast,
["Kirov Oblast, Russia"] = russia_oblast,
["Kostroma Oblast, Russia"] = russia_oblast,
["Kurgan Oblast, Russia"] = russia_oblast,
["Kursk Oblast, Russia"] = russia_oblast,
["Leningrad Oblast, Russia"] = russia_oblast,
["Lipetsk Oblast, Russia"] = russia_oblast,
["Magadan Oblast, Russia"] = russia_oblast,
["Moscow Oblast, Russia"] = russia_oblast,
["Murmansk Oblast, Russia"] = russia_oblast,
["Nizhny Novgorod Oblast, Russia"] = russia_oblast,
["Novgorod Oblast, Russia"] = russia_oblast,
["Novosibirsk Oblast, Russia"] = russia_oblast,
["Omsk Oblast, Russia"] = russia_oblast,
["Orenburg Oblast, Russia"] = russia_oblast,
["Oryol Oblast, Russia"] = russia_oblast,
["Penza Oblast, Russia"] = russia_oblast,
["Pskov Oblast, Russia"] = russia_oblast,
["Rostov Oblast, Russia"] = russia_oblast,
["Ryazan Oblast, Russia"] = russia_oblast,
["Sakhalin Oblast, Russia"] = russia_oblast,
["Samara Oblast, Russia"] = russia_oblast,
["Saratov Oblast, Russia"] = russia_oblast,
["Smolensk Oblast, Russia"] = russia_oblast,
["Sverdlovsk Oblast, Russia"] = russia_oblast,
["Tambov Oblast, Russia"] = russia_oblast,
["Tomsk Oblast, Russia"] = russia_oblast,
["Tula Oblast, Russia"] = russia_oblast,
["Tver Oblast, Russia"] = russia_oblast,
["Tyumen Oblast, Russia"] = russia_oblast,
["Ulyanovsk Oblast, Russia"] = russia_oblast,
["Vladimir Oblast, Russia"] = russia_oblast,
["Volgograd Oblast, Russia"] = russia_oblast,
["Vologda Oblast, Russia"] = russia_oblast,
["Voronezh Oblast, Russia"] = russia_oblast,
["Yaroslavl Oblast, Russia"] = russia_oblast,
-- republics
--
-- We only need to include cases that aren't just shortened versions of the full federal subject name (i.e. where
-- words like "Republic" and "Oblast" are omitted but the name is not otherwise modified; these are handled by
-- key_to_placename). Non-display-canonicalizing aliases are generally due to differences in the presence or absence
-- of "the".
["Adygea, Russia"] = russia_republic_no_the,
["Republic of Adygea, Russia"] = {alias_of = "Adygea, Russia", the = true},
["Bashkortostan, Russia"] = russia_republic_no_the,
["Republic of Bashkortostan, Russia"] = {alias_of = "Bashkortostan, Russia", the = true},
["Bashkiria, Russia"] = {alias_of = "Bashkortostan, Russia"},
["Buryatia, Russia"] = russia_republic_no_the,
["Republic of Buryatia, Russia"] = {alias_of = "Buryatia, Russia", the = true},
["Dagestan, Russia"] = russia_republic_no_the,
["Republic of Dagestan, Russia"] = {alias_of = "Dagestan, Russia", the = true},
["Ingushetia, Russia"] = russia_republic_no_the,
["Republic of Ingushetia, Russia"] = {alias_of = "Ingushetia, Russia", the = true},
["Kalmykia, Russia"] = russia_republic_no_the,
["Republic of Kalmykia, Russia"] = {alias_of = "Kalmykia, Russia", the = true},
["Karelia, Russia"] = make_russia_federal_subject_spec("republic", nil, "Republic of Karelia"),
["Republic of Karelia, Russia"] = {alias_of = "Karelia, Russia", the = true},
["Khakassia, Russia"] = russia_republic_no_the,
["Republic of Khakassia, Russia"] = {alias_of = "Khakassia, Russia", the = true},
["Mordovia, Russia"] = russia_republic_no_the,
["Republic of Mordovia, Russia"] = {alias_of = "Mordovia, Russia", the = true},
["North Ossetia-Alania, Russia"] = make_russia_federal_subject_spec("republic", nil, "North Ossetia–Alania"), -- with en-dash
["Republic of North Ossetia-Alania, Russia"] = {alias_of = "North Ossetia-Alania, Russia", the = true},
["North Ossetia, Russia"] = {alias_of = "North Ossetia-Alania, Russia", display = true},
["Alania, Russia"] = {alias_of = "North Ossetia-Alania, Russia", display = true},
["Tatarstan, Russia"] = russia_republic_no_the,
["Republic of Tatarstan, Russia"] = {alias_of = "Tatarstan, Russia", the = true},
["Altai Republic, Russia"] = russia_republic_the,
["Chechnya, Russia"] = russia_republic_no_the,
["Chechen Republic, Russia"] = {alias_of = "Chechnya, Russia", the = true},
["Chuvashia, Russia"] = russia_republic_no_the,
["Chuvash Republic, Russia"] = {alias_of = "Chuvashia, Russia", the = true},
["Kabardino-Balkaria, Russia"] = russia_republic_no_the,
["Kabardino-Balkariya, Russia"] = {alias_of = "Kabardino-Balkaria, Russia", display = true},
["Kabardino-Balkarian Republic, Russia"] = {alias_of = "Kabardino-Balkaria, Russia", the = true},
["Kabardino-Balkar Republic, Russia"] = {alias_of = "Kabardino-Balkaria, Russia",
display = "Kabardino-Balkarian Republic, Russia", the = true},
["Karachay-Cherkessia, Russia"] = russia_republic_no_the,
["Karachay-Cherkess Republic, Russia"] = {alias_of = "Karachay-Cherkessia, Russia"},
["Komi, Russia"] = make_russia_federal_subject_spec("republic", nil, "Komi Republic"),
["Komi Republic, Russia"] = {alias_of = "Komi, Russia", the = true},
["Mari El, Russia"] = russia_republic_no_the,
["Mari El Republic, Russia"] = {alias_of = "Mari El, Russia", the = true},
["Sakha, Russia"] = make_russia_federal_subject_spec("republic", nil, "Sakha Republic"),
["Sakha Republic, Russia"] = {alias_of = "Sakha, Russia", the = true},
["Yakutia, Russia"] = {alias_of = "Sakha, Russia"},
["Yakutiya, Russia"] = {alias_of = "Sakha, Russia", display = "Yakutia, Russia"},
["Republic of Yakutia (Sakha), Russia"] = {alias_of = "Sakha, Russia", display = "Sakha Republic, Russia",
the = true},
["Tuva, Russia"] = russia_republic_no_the,
["Tyva, Russia"] = {alias_of = "Tuva, Russia", display = true},
["Tuva Republic, Russia"] = {alias_of = "Tuva, Russia", the = true},
["Tyva Republic, Russia"] = {alias_of = "Tuva, Russia", display= "Tuva Republic, Russia", the = true},
["Udmurtia, Russia"] = russia_republic_no_the,
["Udmurt Republic, Russia"] = {alias_of = "Udmurtia, Russia", the = true},
-- Not included due to being unrecognized and only partly controlled:
-- ["Crimea, Russia"] = make_russia_federal_subject_spec("republic", nil, "Republic of Crimea (Russia)")
-- ["Donetsk People's Republic, Russia"] = russia_republic_the,
-- ["Luhansk People's Republic, Russia"] = russia_republic_the,
-- ["Zaporozhye Oblast, Russia"] = make_russia_federal_subject_spec("oblast", nil, "Russian occupation of Zaporizhzhia Oblast"),
-- ["Kherson Oblast, Russia"] = make_russia_federal_subject_spec("oblast", nil, "Russian occupation of Kherson Oblast"),
-- There are also federal cities (not included because they're cities):
-- Moscow, Saint Petersburg; Sevastopol (unrecognized; same status as for "Crimea, Russia" above)
}
local function russia_key_to_placename(key)
key = key:gsub(",.*", "")
local full_placename = key
if key == "Jewish Autonomous Oblast" then
return full_placename, full_placename
end
local elliptical_placename
for _, suffix in ipairs({"Krai", "Oblast"}) do
elliptical_placename = key:match("^(.*) " .. suffix .. "$")
if elliptical_placename then
return full_placename, elliptical_placename
end
end
return full_placename, full_placename
end
local function russia_placename_to_key(placename)
local key = placename .. ", Russia"
if export.russia_federal_subjects[key] then
return key
end
-- We allow the user to say e.g. "obl/Samara" in place of "obl/Samara Oblast".
for _, suffix in ipairs({"Krai", "Oblast"}) do
local suffixed_key = placename .. " " .. suffix .. ", Russia"
if export.russia_federal_subjects[suffixed_key] then
return suffixed_key
end
end
return placename .. ", Russia"
end
local function construct_russia_federal_subject_keydesc(group, key, spec)
local placename = key:gsub(",.*", "")
local linked_placename = export.construct_linked_placename(spec, placename)
local placetype = spec.placetype
if type(placetype) == "table" then
placetype = placetype[1]
end
if placetype == "oblast" then
-- Hack: Oblasts generally don't have entries under "Foo Oblast"
-- but just under "Foo", so fix the linked key appropriately;
-- doesn't apply to the Jewish Autonomous Oblast
linked_placename = linked_placename:gsub(" Oblast%]%]", "%]%] Oblast")
end
return linked_placename .. ", a [[federal subject]] ([[" .. placetype .. "]]) of [[Russia]]"
end
-- federal subjects of Russia
export.russia_group = {
key_to_placename = russia_key_to_placename,
placename_to_key = russia_placename_to_key,
default_container = "Russia",
default_keydesc = construct_russia_federal_subject_keydesc,
default_overriding_bare_label_parents = {"federal subjects of Russia", "+++"},
data = export.russia_federal_subjects,
}
export.saudi_arabia_provinces = {
["Riyadh Province, Saudi Arabia"] = {},
["Mecca Province, Saudi Arabia"] = {},
-- Name is too generic to assume it's in Saudi Arabia if not specified.
["Eastern Province, Saudi Arabia"] = {no_auto_augment_container = true, wp = "%l, %c"},
["Medina Province, Saudi Arabia"] = {wp = "%l (%c)"},
["Aseer Province, Saudi Arabia"] = {wp = "Asir"},
["Asir Province, Saudi Arabia"] = {alias_of = "Aseer Province, Saudi Arabia", display = true},
["Jazan Province, Saudi Arabia"] = {},
["Qassim Province, Saudi Arabia"] = {wp = "Al-Qassim Province"},
["Al-Qassim Province, Saudi Arabia"] = {alias_of = "Qassim Province, Saudi Arabia", display = true},
["Tabuk Province, Saudi Arabia"] = {},
["Hail Province, Saudi Arabia"] = {wp = "Ḥa'il Province"},
["Ha'il Province, Saudi Arabia"] = {alias_of = "Hail Province, Saudi Arabia", display = true},
["Ḥa'il Province, Saudi Arabia"] = {alias_of = "Hail Province, Saudi Arabia", display = true},
["Al-Jouf Province, Saudi Arabia"] = {wp = "Al-Jawf Province"},
["Al-Jawf Province, Saudi Arabia"] = {alias_of = "Al-Jouf Province, Saudi Arabia", display = true},
["Najran Province, Saudi Arabia"] = {},
["Northern Borders Province, Saudi Arabia"] = {},
["Al-Bahah Province, Saudi Arabia"] = {},
}
-- provinces of Saudi Arabia
export.saudi_arabia_group = {
key_to_placename = make_key_to_placename(", Saudi Arabia$", " Province$"),
placename_to_key = make_placename_to_key(", Saudi Arabia", " Province"),
default_container = "Saudi Arabia",
default_placetype = "province",
data = export.saudi_arabia_provinces,
}
export.south_africa_provinces = {
["Eastern Cape, South Africa"] = {the = true},
["Free State, South Africa"] = {the = true, wp = "%l (province)"},
["Gauteng, South Africa"] = {},
["KwaZulu-Natal, South Africa"] = {},
["Limpopo, South Africa"] = {},
["Mpumalanga, South Africa"] = {},
-- per Wikipedia and other sources, `North West` doesn't normally have `the` before it
["North West, South Africa"] = {wp = "%l (South African province)"},
["Northern Cape, South Africa"] = {the = true},
["Western Cape, South Africa"] = {the = true},
}
-- provinces of South Africa
export.south_africa_group = {
default_container = "South Africa",
default_placetype = "province",
default_divs = "municipalities",
data = export.south_africa_provinces,
}
export.south_korea_provinces = {
["North Chungcheong Province, South Korea"] = {},
["South Chungcheong Province, South Korea"] = {},
["Gangwon Province, South Korea"] = {wp = "%l, %c"},
["Gyeonggi Province, South Korea"] = {},
["North Gyeongsang Province, South Korea"] = {},
["South Gyeongsang Province, South Korea"] = {},
["North Jeolla Province, South Korea"] = {},
["South Jeolla Province, South Korea"] = {},
["Jeju Province, South Korea"] = {},
}
-- provinces of South Korea
export.south_korea_group = {
key_to_placename = make_key_to_placename(", South Korea$", " Province$"),
placename_to_key = make_placename_to_key(", South Korea", " Province"),
default_container = "South Korea",
default_placetype = "province",
data = export.south_korea_provinces,
}
export.spain_autonomous_communities = {
["Andalusia, Spain"] = {},
["Aragon, Spain"] = {},
["Asturias, Spain"] = {},
["Balearic Islands, Spain"] = {the = true},
["Basque Country, Spain"] = {the = true, wp = "%l (autonomous community)"},
["Canary Islands, Spain"] = {the = true},
["Cantabria, Spain"] = {},
["Castile and León, Spain"] = {},
["Castilla-La Mancha, Spain"] = {wp = "Castilla–La Mancha"}, -- with en-dash
["Catalonia, Spain"] = {},
["Community of Madrid, Spain"] = {the = true},
["Extremadura, Spain"] = {},
["Galicia, Spain"] = {wp = "%l (Spain)"},
["La Rioja, Spain"] = {},
["Murcia, Spain"] = {wp = "Region of %l"},
["Navarre, Spain"] = {},
["Valencia, Spain"] = {wp = "Valencian Community"},
["Valencian Community, Spain"] = {alias_of = "Valencia, Spain", the = true},
}
-- autonomous communities of Spain
export.spain_group = {
default_container = "Spain",
default_placetype = "autonomous community",
default_divs = {"municipalities", "comarcas"},
data = export.spain_autonomous_communities,
}
export.taiwan_counties = {
["Changhua County, Taiwan"] = {},
["Chiayi County, Taiwan"] = {},
["Hsinchu County, Taiwan"] = {},
["Hualien County, Taiwan"] = {},
["Kinmen County, Taiwan"] = {wp = "Kinmen"},
["Lienchiang County, Taiwan"] = {wp = "Matsu Islands"},
["Miaoli County, Taiwan"] = {},
["Nantou County, Taiwan"] = {},
["Penghu County, Taiwan"] = {wp = "Penghu"},
["Pingtung County, Taiwan"] = {},
["Taitung County, Taiwan"] = {},
["Yilan County, Taiwan"] = {wp = "%l, %c"},
["Yunlin County, Taiwan"] = {},
}
-- counties of Taiwan
export.taiwan_group = {
key_to_placename = make_key_to_placename(", Taiwan$", " County$"),
placename_to_key = make_placename_to_key(", Taiwan", " County"),
default_container = "Taiwan",
default_placetype = "county",
default_divs = {"districts", "townships"},
data = export.taiwan_counties,
}
export.thailand_provinces = {
-- Bangkok (special administrative area)
["Amnat Charoen Province, Thailand"] = {},
["Ang Thong Province, Thailand"] = {},
["Bueng Kan Province, Thailand"] = {},
["Buriram Province, Thailand"] = {},
["Chachoengsao Province, Thailand"] = {},
["Chai Nat Province, Thailand"] = {},
["Chaiyaphum Province, Thailand"] = {},
["Chanthaburi Province, Thailand"] = {},
["Chiang Mai Province, Thailand"] = {},
["Chiang Rai Province, Thailand"] = {},
["Chonburi Province, Thailand"] = {},
["Chumphon Province, Thailand"] = {},
["Kalasin Province, Thailand"] = {},
["Kamphaeng Phet Province, Thailand"] = {},
["Kanchanaburi Province, Thailand"] = {},
["Khon Kaen Province, Thailand"] = {},
["Krabi Province, Thailand"] = {},
["Lampang Province, Thailand"] = {},
["Lamphun Province, Thailand"] = {},
["Loei Province, Thailand"] = {},
["Lopburi Province, Thailand"] = {},
["Mae Hong Son Province, Thailand"] = {},
["Maha Sarakham Province, Thailand"] = {},
["Mukdahan Province, Thailand"] = {},
["Nakhon Nayok Province, Thailand"] = {},
["Nakhon Pathom Province, Thailand"] = {},
["Nakhon Phanom Province, Thailand"] = {},
["Nakhon Ratchasima Province, Thailand"] = {},
["Nakhon Sawon Province, Thailand"] = {},
["Nakhon Si Thammarat Province, Thailand"] = {},
["Nan Province, Thailand"] = {},
["Narathiwat Province, Thailand"] = {},
["Nong Bua Lamphu Province, Thailand"] = {},
["Nong Khai Province, Thailand"] = {},
["Nonthaburi Province, Thailand"] = {},
["Pathum Thani Province, Thailand"] = {},
["Pattani Province, Thailand"] = {},
["Phang Nga Province, Thailand"] = {},
["Phatthalung Province, Thailand"] = {},
["Phayao Province, Thailand"] = {},
["Phetchabun Province, Thailand"] = {},
["Phetchaburi Province, Thailand"] = {},
["Phichit Province, Thailand"] = {},
["Phitsanulok Province, Thailand"] = {},
["Phra Nakhon Si Ayutthaya Province, Thailand"] = {},
["Phrae Province, Thailand"] = {},
["Phuket Province, Thailand"] = {},
["Prachinburi Province, Thailand"] = {},
["Prachuap Khiri Khan Province, Thailand"] = {},
["Ranong Province, Thailand"] = {},
["Ratchaburi Province, Thailand"] = {},
["Rayong Province, Thailand"] = {},
["Roi Et Province, Thailand"] = {},
["Sa Kaeo Province, Thailand"] = {},
["Sakon Nakhon Province, Thailand"] = {},
["Samut Prakan Province, Thailand"] = {},
["Samut Sakhon Province, Thailand"] = {},
["Samut Songkhram Province, Thailand"] = {},
["Saraburi Province, Thailand"] = {},
["Satun Province, Thailand"] = {},
["Sing Buri Province, Thailand"] = {},
["Sisaket Province, Thailand"] = {},
["Songkhla Province, Thailand"] = {},
["Sukhothai Province, Thailand"] = {},
["Suphan Buri Province, Thailand"] = {},
["Surat Thani Province, Thailand"] = {},
["Surin Province, Thailand"] = {},
["Tak Province, Thailand"] = {},
["Trang Province, Thailand"] = {},
["Trat Province, Thailand"] = {},
["Ubon Ratchathani Province, Thailand"] = {},
["Udon Thani Province, Thailand"] = {},
["Uthai Thani Province, Thailand"] = {},
["Uttaradit Province, Thailand"] = {},
["Yala Province, Thailand"] = {},
["Yasothon Province, Thailand"] = {},
}
-- provinces of Thailand
export.thailand_group = {
key_to_placename = make_key_to_placename(", Thailand$", " Province$"),
placename_to_key = make_placename_to_key(", Thailand", " Province"),
default_container = "Thailand",
default_placetype = "province",
default_divs = "districts",
-- For obscure reasons, provinces of Iran, Laos, Thailand and Vietnam use lowercase 'province'
default_wp = "%e province",
data = export.thailand_provinces,
}
export.turkey_provinces = {
["Adana Province, Turkey"] = {}, -- code 01
["Adıyaman Province, Turkey"] = {}, -- code 02
["Afyonkarahisar Province, Turkey"] = {}, -- code 03
["Ağrı Province, Turkey"] = {}, -- code 04
["Amasya Province, Turkey"] = {}, -- code 05
["Ankara Province, Turkey"] = {}, -- code 06
["Antalya Province, Turkey"] = {}, -- code 07
["Artvin Province, Turkey"] = {}, -- code 08
["Aydın Province, Turkey"] = {}, -- code 09
["Balıkesir Province, Turkey"] = {}, -- code 10
["Bilecik Province, Turkey"] = {}, -- code 11
["Bingöl Province, Turkey"] = {}, -- code 12
["Bitlis Province, Turkey"] = {}, -- code 13
["Bolu Province, Turkey"] = {}, -- code 14
["Burdur Province, Turkey"] = {}, -- code 15
["Bursa Province, Turkey"] = {}, -- code 16
["Çanakkale Province, Turkey"] = {}, -- code 17
["Çankırı Province, Turkey"] = {}, -- code 18
["Çorum Province, Turkey"] = {}, -- code 19
["Denizli Province, Turkey"] = {}, -- code 20
["Diyarbakır Province, Turkey"] = {}, -- code 21
["Edirne Province, Turkey"] = {}, -- code 22
["Elazığ Province, Turkey"] = {}, -- code 23
["Elâzığ Province, Turkey"] = {alias_of = "Elazığ Province, Turkey", display = true},
["Erzincan Province, Turkey"] = {}, -- code 24
["Erzurum Province, Turkey"] = {}, -- code 25
["Eskişehir Province, Turkey"] = {}, -- code 26
["Gaziantep Province, Turkey"] = {}, -- code 27
["Giresun Province, Turkey"] = {}, -- code 28
["Gümüşhane Province, Turkey"] = {}, -- code 29
["Hakkâri Province, Turkey"] = {}, -- code 30
["Hakkari Province, Turkey"] = {alias_of = "Hakkâri Province, Turkey", display = true},
["Hatay Province, Turkey"] = {}, -- code 31
["Isparta Province, Turkey"] = {}, -- code 32
["Mersin Province, Turkey"] = {}, -- code 33
-- ["Istanbul Province, Turkey"] = {}, -- code 34; this is coextensive with the city itself
["İzmir Province, Turkey"] = {}, -- code 35
["Izmir Province, Turkey"] = {alias_of = "İzmir Province, Turkey", display = true},
["Kars Province, Turkey"] = {}, -- code 36
["Kastamonu Province, Turkey"] = {}, -- code 37
["Kayseri Province, Turkey"] = {}, -- code 38
["Kırklareli Province, Turkey"] = {}, -- code 39
["Kırşehir Province, Turkey"] = {}, -- code 40
["Kocaeli Province, Turkey"] = {}, -- code 41
["Konya Province, Turkey"] = {}, -- code 42
["Kütahya Province, Turkey"] = {}, -- code 43
["Malatya Province, Turkey"] = {}, -- code 44
["Manisa Province, Turkey"] = {}, -- code 45
["Kahramanmaraş Province, Turkey"] = {}, -- code 46
["Mardin Province, Turkey"] = {}, -- code 47
["Muğla Province, Turkey"] = {}, -- code 48
["Muş Province, Turkey"] = {}, -- code 49
["Nevşehir Province, Turkey"] = {}, -- code 50
["Niğde Province, Turkey"] = {}, -- code 51
["Ordu Province, Turkey"] = {}, -- code 52
["Rize Province, Turkey"] = {}, -- code 53
["Sakarya Province, Turkey"] = {}, -- code 54
["Samsun Province, Turkey"] = {}, -- code 55
["Siirt Province, Turkey"] = {}, -- code 56
["Sinop Province, Turkey"] = {}, -- code 57
["Sivas Province, Turkey"] = {}, -- code 58
["Tekirdağ Province, Turkey"] = {}, -- code 59
["Tokat Province, Turkey"] = {}, -- code 60
["Trabzon Province, Turkey"] = {}, -- code 61
["Tunceli Province, Turkey"] = {}, -- code 62
["Şanlıurfa Province, Turkey"] = {}, -- code 63
["Uşak Province, Turkey"] = {}, -- code 64
["Van Province, Turkey"] = {}, -- code 65
["Yozgat Province, Turkey"] = {}, -- code 66
["Zonguldak Province, Turkey"] = {}, -- code 67
["Aksaray Province, Turkey"] = {}, -- code 68
["Bayburt Province, Turkey"] = {}, -- code 69
["Karaman Province, Turkey"] = {}, -- code 70
["Kırıkkale Province, Turkey"] = {}, -- code 71
["Batman Province, Turkey"] = {}, -- code 72
["Şırnak Province, Turkey"] = {}, -- code 73
["Bartın Province, Turkey"] = {}, -- code 74
["Ardahan Province, Turkey"] = {}, -- code 75
["Iğdır Province, Turkey"] = {}, -- code 76
["Yalova Province, Turkey"] = {}, -- code 77
["Karabük Province, Turkey"] = {}, -- code 78
["Kilis Province, Turkey"] = {}, -- code 79
["Osmaniye Province, Turkey"] = {}, -- code 80
["Düzce Province, Turkey"] = {}, -- code 81
}
-- provinces of Turkey
export.turkey_group = {
key_to_placename = make_key_to_placename(", Turkey$", " Province$"),
placename_to_key = make_placename_to_key(", Turkey", " Province"),
default_container = "Turkey",
default_placetype = "province",
default_divs = "districts",
data = export.turkey_provinces,
}
export.ukraine_oblasts = {
["Cherkasy Oblast, Ukraine"] = {}, -- capital [[Cherkasy]], license plate prefix CA, IA
["Chernihiv Oblast, Ukraine"] = {}, -- capital [[Chernihiv]], license plate prefix CB, IB
["Chernivtsi Oblast, Ukraine"] = {}, -- capital [[Chernivtsi]], license plate prefix CE, IE
-- apparently will be renamed to 'Dnipro Oblast'
["Dnipropetrovsk Oblast, Ukraine"] = {}, -- capital [[Dnipro]], license plate prefix AE, KE
["Donetsk Oblast, Ukraine"] = {}, -- capital ''[[Donetsk]] ([[Kramatorsk]])'', license plate prefix AH, KH
["Ivano-Frankivsk Oblast, Ukraine"] = {}, -- capital [[Ivano-Frankivsk]], license plate prefix AT, KT
["Kharkiv Oblast, Ukraine"] = {}, -- capital [[Kharkiv]], license plate prefix AX, KX
["Kherson Oblast, Ukraine"] = {}, -- capital ''[[Kherson]]'', license plate prefix ''BT, HT''
["Khmelnytskyi Oblast, Ukraine"] = {}, -- capital [[Khmelnytskyi]], license plate prefix BX, HX
-- apparently will be renamed to 'Kropyvnytskyi Oblast'
["Kirovohrad Oblast, Ukraine"] = {}, -- capital [[Kropyvnytskyi]], license plate prefix BA, HA
["Kyiv Oblast, Ukraine"] = {}, -- capital [[Kyiv]], license plate prefix AI, KI
["Kiev Oblast, Ukraine"] = {alias_of = "Kyiv Oblast, Ukraine", display = true},
["Luhansk Oblast, Ukraine"] = {}, -- capital ''[[Luhansk]] ([[Sievierodonetsk]])'', license plate prefix BB, HB
["Lviv Oblast, Ukraine"] = {}, -- capital [[Lviv]], license plate prefix BC, HC
["Mykolaiv Oblast, Ukraine"] = {}, -- capital [[Mykolaiv]], license plate prefix BE, HE
["Odesa Oblast, Ukraine"] = {}, -- capital [[Odesa]], license plate prefix BH, HH
["Odessa Oblast, Ukraine"] = {alias_of = "Odesa Oblast, Ukraine", display = true},
["Poltava Oblast, Ukraine"] = {}, -- capital [[Poltava]], license plate prefix BI, HI
["Rivne Oblast, Ukraine"] = {}, -- capital [[Rivne]], license plate prefix BK, HK
["Sumy Oblast, Ukraine"] = {}, -- capital [[Sumy]], license plate prefix BM, HM
["Ternopil Oblast, Ukraine"] = {}, -- capital [[Ternopil]], license plate prefix BO, HO
["Vinnytsia Oblast, Ukraine"] = {}, -- capital [[Vinnytsia]], license plate prefix AB, KB
["Volyn Oblast, Ukraine"] = {}, -- capital [[Lutsk]], license plate prefix AC, KC
["Zakarpattia Oblast, Ukraine"] = {}, -- capital [[Uzhhorod]], license plate prefix AO, KO
["Zaporizhzhia Oblast, Ukraine"] = {}, -- capital ''[[Zaporizhzhia]]'', license plate prefix AP, KP
["Zaporizhia Oblast, Ukraine"] = {alias_of = "Zaporizhzhia Oblast, Ukraine", display = true},
["Zhytomyr Oblast, Ukraine"] = {}, -- capital [[Zhytomyr]], license plate prefix AM, KM
}
-- oblasts of Ukraine
export.ukraine_group = {
key_to_placename = make_key_to_placename(", Ukraine$", " Oblast$"),
placename_to_key = make_placename_to_key(", Ukraine", " Oblast"),
default_container = "Ukraine",
default_placetype = "oblast",
default_divs = {"raions", "hromadas"},
data = export.ukraine_oblasts,
}
export.united_kingdom_constituent_countries = {
["England"] = {divs = {
"counties",
"districts",
{type = "local government districts", cat_as = "districts"},
{
type = "local government districts with borough status",
cat_as = {"districts", "boroughs"},
},
{type = "boroughs", cat_as = {"districts", "boroughs"}},
{type = "civil parishes", container_parent_type = false},
}},
["Northern Ireland"] = {
placetype = {"constituent country", "province", "country"},
divs = {"counties", "districts"},
},
["Scotland"] = {divs = {
{type = "council areas", container_parent_type = false},
"districts",
}},
["Wales"] = {divs = {
"counties",
{type = "county boroughs", container_parent_type = false},
{type = "communities", container_parent_type = false},
{type = "Welsh communities", cat_as = {{type = "communities", container_parent_type = false}}},
}},
}
-- constituent countries and provinces of the United Kingdom
export.united_kingdom_group = {
placename_to_key = false,
default_container = "United Kingdom",
default_placetype = {"constituent country", "country"},
addl_divs = {
"traditional counties",
{type = "historical counties", cat_as = "traditional counties"},
},
-- Don't create categories like 'Category:en:Towns in the United Kingdom'
-- or 'Category:en:Places in the United Kingdom'.
default_no_container_cat = true,
data = export.united_kingdom_constituent_countries,
}
export.england_counties = {
-- NOTE: We used to have various other "no longer" counties commented out, which seems to refer to counties that
-- existed officially at some point between 1889 and 1974, which I have removed. I have only kept the three
-- ceremonial counties that existed from 1974 (when ceremonial counties were created) to 1996, as well as those
-- still considered "historic counties" per [[w:Historic counties of England]].
-- ["Avon, England"] = {wp = "%l (county)"}, -- no longer (1974 to 1996)
["Bedfordshire, England"] = {},
["Berkshire, England"] = {},
-- ["Brighton and Hove, England"] = {}, -- city
-- ["Bristol, England"] = {}, -- city
["Buckinghamshire, England"] = {},
["Cambridgeshire, England"] = {},
["Cheshire, England"] = {},
-- ["Cleveland, England"] = {wp = "%l (county)"}, -- no longer (1974 to 1996)
["Cornwall, England"] = {},
-- ["Cumberland, England"] = {}, -- no longer (historic county)
["Cumbria, England"] = {},
["Derbyshire, England"] = {},
["Devon, England"] = {},
["Dorset, England"] = {},
["County Durham, England"] = {},
["East Sussex, England"] = {},
["Essex, England"] = {},
["Gloucestershire, England"] = {},
["Greater London, England"] = {},
["Greater Manchester, England"] = {},
["Hampshire, England"] = {},
["Herefordshire, England"] = {},
["Hertfordshire, England"] = {},
-- ["Humberside, England"] = {}, -- no longer (1974 to 1996)
-- ["Huntingdonshire, England"] = {}, -- no longer (historic county)
["Isle of Wight, England"] = {the = true},
["Kent, England"] = {},
["Lancashire, England"] = {},
["Leicestershire, England"] = {},
["Lincolnshire, England"] = {},
["Merseyside, England"] = {},
-- ["Middlesex, England"] = {}, -- no longer (historic county)
["Norfolk, England"] = {},
["Northamptonshire, England"] = {},
["Northumberland, England"] = {},
["North Yorkshire, England"] = {},
["Nottinghamshire, England"] = {},
["Oxfordshire, England"] = {},
["Rutland, England"] = {},
["Shropshire, England"] = {},
["Somerset, England"] = {},
["South Humberside, England"] = {},
["South Yorkshire, England"] = {},
["Staffordshire, England"] = {},
["Suffolk, England"] = {},
["Surrey, England"] = {},
-- ["Sussex, England"] = {}, -- no longer (historic county)
["Tyne and Wear, England"] = {},
["Warwickshire, England"] = {},
["West Midlands, England"] = {the = true, wp = "%l (county)"},
-- ["Westmorland, England"] = {}, -- no longer (historic county)
["West Sussex, England"] = {},
["West Yorkshire, England"] = {},
["Wiltshire, England"] = {},
["Worcestershire, England"] = {},
-- ["Yorkshire, England"] = {}, -- no longer (historic county)
["East Riding of Yorkshire, England"] = {the = true},
}
-- counties of England
export.england_group = {
default_container = {key = "England", placetype = "constituent country"},
default_placetype = "county",
default_divs = {
"districts",
{type = "local government districts", cat_as = "districts"},
{
type = "local government districts with borough status",
cat_as = {"districts", "boroughs"},
},
{type = "boroughs", cat_as = {"districts", "boroughs"}},
"civil parishes",
},
data = export.england_counties,
}
export.northern_ireland_counties = {
["County Antrim, Northern Ireland"] = {},
["County Armagh, Northern Ireland"] = {},
["City of Belfast, Northern Ireland"] = {the = true, is_city = true, wp = "Belfast"},
["County Down, Northern Ireland"] = {},
["County Fermanagh, Northern Ireland"] = {},
["County Londonderry, Northern Ireland"] = {},
["City of Derry, Northern Ireland"] = {the = true, is_city = true, wp = "Derry"},
["County Tyrone, Northern Ireland"] = {},
}
-- counties of Northern Ireland
export.northern_ireland_group = {
key_to_placename = make_irish_type_key_to_placename(", Northern Ireland$"),
placename_to_key = make_irish_type_placename_to_key(", Northern Ireland"),
default_container = {key = "Northern Ireland", placetype = "constituent country"},
default_placetype = "county",
data = export.northern_ireland_counties,
}
export.scotland_council_areas = {
["Aberdeenshire, Scotland"] = {},
["Angus, Scotland"] = {wp = "%l, %c"},
["Argyll and Bute, Scotland"] = {},
["City of Aberdeen, Scotland"] = {the = true, wp = "Aberdeen"},
["Aberdeen"] = {alias_of = "City of Aberdeen, Scotland"},
["Aberdeen City"] = {alias_of = "City of Aberdeen, Scotland"},
["City of Dundee, Scotland"] = {the = true, wp = "Dundee"},
["Dundee"] = {alias_of = "City of Dundee, Scotland"},
["Dundee City"] = {alias_of = "City of Dundee, Scotland"},
["City of Edinburgh, Scotland"] = {the = true, wp = "%l council area"},
["Edinburgh"] = {alias_of = "City of Edinburgh, Scotland"},
["City of Glasgow, Scotland"] = {the = true, wp = "Glasgow"},
["Glasgow"] = {alias_of = "City of Glasgow, Scotland"},
["Clackmannanshire, Scotland"] = {},
["Dumfries and Galloway, Scotland"] = {},
["East Ayrshire, Scotland"] = {},
["East Dunbartonshire, Scotland"] = {},
["East Lothian, Scotland"] = {},
["East Renfrewshire, Scotland"] = {},
["Falkirk, Scotland"] = {wp = "%l council area"},
["Fife, Scotland"] = {},
["Highland, Scotland"] = {wp = "%l council area"},
["Inverclyde, Scotland"] = {},
["Midlothian, Scotland"] = {},
["Moray, Scotland"] = {},
["North Ayrshire, Scotland"] = {},
["North Lanarkshire, Scotland"] = {},
["Orkney Islands, Scotland"] = {the = true},
["Perth and Kinross, Scotland"] = {},
["Renfrewshire, Scotland"] = {},
["Scottish Borders, Scotland"] = {the = true},
["Shetland Islands, Scotland"] = {the = true},
["South Ayrshire, Scotland"] = {},
["South Lanarkshire, Scotland"] = {},
["Stirling, Scotland"] = {wp = "%l council area"},
["West Dunbartonshire, Scotland"] = {},
["West Lothian, Scotland"] = {},
["Western Isles, Scotland"] = {the = true, wp = "Outer Hebrides"},
["Na h-Eileanan Siar, Scotland"] = {alias_of = "Western Isles, Scotland"},
}
-- council areas of Scotland
export.scotland_group = {
default_container = {key = "Scotland", placetype = "constituent country"},
default_placetype = "council area",
data = export.scotland_council_areas,
}
export.wales_principal_areas = {
["Blaenau Gwent, Wales"] = {},
["Bridgend, Wales"] = {wp = "%l County Borough"},
["Caerphilly, Wales"] = {wp = "%l County Borough"},
-- ["Cardiff, Wales"] = {placetype = "city"},
["Carmarthenshire, Wales"] = {placetype = "county"},
["Ceredigion, Wales"] = {placetype = "county"},
["Conwy, Wales"] = {wp = "%l County Borough"},
["Denbighshire, Wales"] = {placetype = "county"},
["Flintshire, Wales"] = {placetype = "county"},
["Gwynedd, Wales"] = {placetype = "county"},
["Isle of Anglesey, Wales"] = {the = true, placetype = "county"},
["Anglesey, Wales"] = {alias_of = "Isle of Anglesey, Wales"}, -- differs in "the"
["Merthyr Tydfil, Wales"] = {wp = "%l County Borough"},
["Monmouthshire, Wales"] = {placetype = "county"},
["Neath Port Talbot, Wales"] = {},
-- ["Newport, Wales"] = {placetype = "city", wp = "%l, %c"},
["Pembrokeshire, Wales"] = {placetype = "county"},
["Powys, Wales"] = {placetype = "county"},
["Rhondda Cynon Taf, Wales"] = {},
-- ["Swansea, Wales"] = {placetype = "city"},
["Torfaen, Wales"] = {},
["Vale of Glamorgan, Wales"] = {the = true},
["Wrexham, Wales"] = {wp = "%l County Borough"},
}
-- principal areas (cities, counties and county boroughs) of Wales
export.wales_group = {
default_container = {key = "Wales", placetype = "constituent country"},
default_placetype = "county borough",
data = export.wales_principal_areas,
}
export.united_states_states = {
["Alabama, USA"] = {},
["Alaska, USA"] = {divs = {
{type = "boroughs", container_parent_type = "counties"},
{type = "borough seats", container_parent_type = "county seats"},
}},
["Arizona, USA"] = {},
["Arkansas, USA"] = {},
["California, USA"] = {},
["Colorado, USA"] = {divs = {"counties", "county seats", "municipalities"}},
["Connecticut, USA"] = {divs = {"counties", "county seats", "municipalities"}},
["Delaware, USA"] = {},
["Florida, USA"] = {},
["Georgia, USA"] = {wp = "%l (U.S. state)"},
["Hawaii, USA"] = {addl_parents = {"Polynesia"}},
["Idaho, USA"] = {},
["Illinois, USA"] = {},
["Indiana, USA"] = {},
["Iowa, USA"] = {},
["Kansas, USA"] = {},
["Kentucky, USA"] = {},
["Louisiana, USA"] = {divs = {
{type = "parishes", container_parent_type = "counties"},
{type = "parish seats", container_parent_type = "county seats"},
}},
["Maine, USA"] = {},
["Maryland, USA"] = {},
["Massachusetts, USA"] = {},
["Michigan, USA"] = {},
["Minnesota, USA"] = {},
["Mississippi, USA"] = {},
["Missouri, USA"] = {},
["Montana, USA"] = {},
["Nebraska, USA"] = {},
["Nevada, USA"] = {},
["New Hampshire, USA"] = {},
["New Jersey, USA"] = {divs = {
"counties", "county seats",
{type = "boroughs", prep = "in"},
}},
["New Mexico, USA"] = {},
["New York, USA"] = {wp = "%l (state)"},
["North Carolina, USA"] = {},
["North Dakota, USA"] = {},
["Ohio, USA"] = {},
["Oklahoma, USA"] = {},
["Oregon, USA"] = {},
["Pennsylvania, USA"] = {divs = {
"counties", "county seats",
{type = "boroughs", prep = "in"},
}},
["Rhode Island, USA"] = {},
["South Carolina, USA"] = {},
["South Dakota, USA"] = {},
["Tennessee, USA"] = {},
["Texas, USA"] = {},
["Utah, USA"] = {},
["Vermont, USA"] = {},
["Virginia, USA"] = {},
["Washington, USA"] = {wp = "%l (state)"},
["West Virginia, USA"] = {},
["Wisconsin, USA"] = {},
["Wyoming, USA"] = {},
}
-- states of the United States
export.united_states_group = {
placename_to_key = make_placename_to_key(", USA"),
default_container = "United States",
default_placetype = "state",
default_divs = {"counties", "county seats"},
addl_divs = {
{type = "census-designated places", prep = "in"},
{type = "unincorporated communities", prep = "in"},
},
data = export.united_states_states,
}
export.vietnam_provinces = {
-- [[Northeast (Vietnam)|Northeast]] region
["Bắc Giang Province, Vietnam"] = {}, -- capital [[Bắc Giang]]
["Bắc Kạn Province, Vietnam"] = {}, -- capital [[Bắc Kạn]]
["Cao Bằng Province, Vietnam"] = {}, -- capital [[Cao Bằng]]
["Hà Giang Province, Vietnam"] = {}, -- capital [[Hà Giang]]
["Lạng Sơn Province, Vietnam"] = {}, -- capital [[Lạng Sơn]]
["Phú Thọ Province, Vietnam"] = {}, -- capital [[Việt Trì]]
["Quảng Ninh Province, Vietnam"] = {}, -- capital [[Hạ Long]]
["Thái Nguyên Province, Vietnam"] = {}, -- capital [[Thái Nguyên]]
["Tuyên Quang Province, Vietnam"] = {}, -- capital [[Tuyên Quang]]
-- [[Northwest (Vietnam)|Northwest]] region
["Lào Cai Province, Vietnam"] = {}, -- capital [[Lào Cai]]
["Yên Bái Province, Vietnam"] = {}, -- capital [[Yên Bái]]
["Điện Biên Province, Vietnam"] = {}, -- capital [[Điện Biên Phủ]]
["Hoà Bình Province, Vietnam"] = {}, -- capital [[Hoà Bình City|Hoà Bình]]
["Hòa Bình Province, Vietnam"] = {alias_of = "Hoà Bình Province, Vietnam", display = true},
["Lai Châu Province, Vietnam"] = {}, -- capital [[Lai Châu]]
["Sơn La Province, Vietnam"] = {}, -- capital [[Sơn La]]
-- [[Red River Delta]] region
["Bắc Ninh Province, Vietnam"] = {}, -- capital [[Bắc Ninh]]
["Hà Nam Province, Vietnam"] = {}, -- capital [[Phủ Lý]]
["Hải Dương Province, Vietnam"] = {}, -- capital [[Hải Dương]]
["Hưng Yên Province, Vietnam"] = {}, -- capital [[Hưng Yên]]
["Nam Định Province, Vietnam"] = {}, -- capital [[Nam Định]]
["Ninh Bình Province, Vietnam"] = {}, -- capital [[Ninh Bình|Hoa Lư]]
["Thái Bình Province, Vietnam"] = {}, -- capital [[Thái Bình]]
["Vĩnh Phúc Province, Vietnam"] = {}, -- capital [[Vĩnh Yên]]
-- ["Hanoi"] = {placetype = {"municipality", "city"}}, -- capital [[Hoàn Kiếm district]]
-- ["Haiphong"] = {placetype = {"municipality", "city"}}, -- capital [[Hồng Bàng district]]
-- [[North Central Coast]] region
["Hà Tĩnh Province, Vietnam"] = {}, -- capital [[Hà Tĩnh]]
["Nghệ An Province, Vietnam"] = {}, -- capital [[Vinh]]
["Quảng Bình Province, Vietnam"] = {}, -- capital [[Đồng Hới]]
["Quảng Trị Province, Vietnam"] = {}, -- capital [[Đông Hà]]
["Thanh Hoá Province, Vietnam"] = {}, -- capital [[Thanh Hoá]]
["Thanh Hóa Province, Vietnam"] = {alias_of = "Thanh Hoá Province, Vietnam", display = true},
-- ["Hue"] = {placetype = {"municipality", "city"}, wp = "Huế"}, -- capital [[Thuận Hoá district]]
-- [[Central Highlands (Vietnam)|Central Highlands]] region
["Đắk Lắk Province, Vietnam"] = {}, -- capital [[Buôn Ma Thuột]]
["Đăk Nông Province, Vietnam"] = {}, -- capital [[Gia Nghĩa]]
["Gia Lai Province, Vietnam"] = {}, -- capital [[Pleiku]]
["Kon Tum Province, Vietnam"] = {}, -- capital [[Kon Tum]]
["Lâm Đồng Province, Vietnam"] = {}, -- capital [[Đà Lạt]]
-- [[South Central Coast]] region
["Bình Định Province, Vietnam"] = {}, -- capital [[Quy Nhon]]
["Bình Thuận Province, Vietnam"] = {}, -- capital [[Phan Thiết]]
["Khánh Hoà Province, Vietnam"] = {}, -- capital [[Nha Trang]]
["Khánh Hòa Province, Vietnam"] = {alias_of = "Khánh Hoà Province, Vietnam", display = true},
["Ninh Thuận Province, Vietnam"] = {}, -- capital [[Phan Rang–Tháp Chàm]]
["Phú Yên Province, Vietnam"] = {}, -- capital [[Tuy Hoà]]
["Quảng Nam Province, Vietnam"] = {}, -- capital [[Tam Kỳ]]
["Quảng Ngãi Province, Vietnam"] = {}, -- capital [[Quảng Ngãi]]
-- ["Da Nang"] = {placetype = {"municipality", "city"}}, -- capital [[Hải Châu district]]
-- [[Southeast (Vietnam)|Southeast]] region
["Bà Rịa–Vũng Tàu Province, Vietnam"] = {}, -- capital [[Bà Rịa]]
["Bình Dương Province, Vietnam"] = {}, -- capital [[Thủ Dầu Một]]
["Bình Phước Province, Vietnam"] = {}, -- capital [[Đồng Xoài]]
["Đồng Nai Province, Vietnam"] = {}, -- capital [[Biên Hoà]]
["Tây Ninh Province, Vietnam"] = {}, -- capital [[Tây Ninh]]
-- ["Ho Chi Minh City"] = {placetype = {"municipality", "city"}}, -- capital [[District 1, Ho Chi Minh City|'''District 1''']]
-- [[Mekong Delta]] region
["An Giang Province, Vietnam"] = {}, -- capital [[Long Xuyên]]
["Bạc Liêu Province, Vietnam"] = {}, -- capital [[Bạc Liêu]]
["Bến Tre Province, Vietnam"] = {}, -- capital [[Bến Tre]]
["Cà Mau Province, Vietnam"] = {}, -- capital [[Cà Mau]]
["Đồng Tháp Province, Vietnam"] = {}, -- capital [[Cao Lãnh City|Cao Lãnh]]
["Hậu Giang Province, Vietnam"] = {}, -- capital [[Vị Thanh]]
["Kiên Giang Province, Vietnam"] = {}, -- capital [[Rạch Giá]]
["Long An Province, Vietnam"] = {}, -- capital [[Tân An]]
["Sóc Trăng Province, Vietnam"] = {}, -- capital [[Sóc Trăng]]
["Tiền Giang Province, Vietnam"] = {}, -- capital [[Mỹ Tho]]
["Trà Vinh Province, Vietnam"] = {}, -- capital [[Trà Vinh]]
["Vĩnh Long Province, Vietnam"] = {}, -- capital [[Vĩnh Long]]
-- ["Can Tho"] = {placetype = {"municipality", "city"}, wp = "Cần Thơ"}, -- capital [[Ninh Kiều district]]
}
-- provinces of Vietnam
export.vietnam_group = {
key_to_placename = make_key_to_placename(", Vietnam$", " Province$"),
placename_to_key = make_placename_to_key(", Vietnam", " Province"),
default_container = "Vietnam",
default_placetype = "province",
-- There may not be enough districts to subcategorize like this.
-- default_divs = "districts",
-- For obscure reasons, provinces of Iran, Laos, Thailand and Vietnam use lowercase 'province'
default_wp = "%e province",
data = export.vietnam_provinces,
}
-----------------------------------------------------------------------------------
-- City data --
-----------------------------------------------------------------------------------
export.australia_cities = {
["Adelaide"] = {container = "South Australia"}, -- 1,450,000 (Agglomeration)
["Brisbane"] = {container = "Queensland"}, -- 3,450,000 (Conglomeration; including the Gold Coast [750,997 2024 estiamte])
["Canberra"] = {container = {key = "Australian Capital Territory, Australia", placetype = "territory"}}, -- 510,641 (2024 estimate)
["Melbourne"] = {container = "Victoria"}, -- 5,200,000 (Agglomeration)
["Newcastle, New South Wales"] = {container = "New South Wales", wp = "%l, %c"}, -- 534,033 (2024 estimate)
["Newcastle"] = {alias_of = "Newcastle, New South Wales"},
["Perth"] = {container = "Western Australia"}, -- 2,350,000 (Agglomeration)
["Sydney"] = {container = "New South Wales"}, -- 5,100,000 (Agglomeration)
}
export.australia_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(", Australia", "state"),
default_placetype = "city",
data = export.australia_cities,
}
export.brazil_cities = {
-- Figures from citypopulation.de; retrieved 2025-04-27; reference date 2025-01-01.
["São Paulo"] = {container = "São Paulo"}, -- 22,600,000 (Consolidated Urban Area; including Guarulhos)
["Sao Paulo"] = {alias_of = "São Paulo", display = true},
["Rio de Janeiro"] = {container = "Rio de Janeiro"}, -- 13,600,000 (Consolidated Urban Area)
["Belo Horizonte"] = {container = "Minas Gerais"}, -- 5,300,000
["Recife"] = {container = "Pernambuco"}, -- 4,100,000
["Porto Alegre"] = {container = "Rio Grande do Sul"}, -- 3,950,000 (Consolidated Urban Area)
["Brasília"] = {container = "Distrito Federal"}, -- 3,850,000
["Brasilia"] = {alias_of = "Brasília", display = true},
["Fortaleza"] = {container = "Ceará"}, -- 3,825,000
["Salvador"] = {container = "Bahia", wp = "%l, %c", commonscat = "%l (%c)"}, -- 3,400,000
["Curitiba"] = {container = "Paraná"}, -- 3,375,000
["Campinas"] = {container = "São Paulo"}, -- 3,250,000
["Goiânia"] = {container = "Goiás"}, -- 2,525,000
["Goiania"] = {alias_of = "Goiânia", display = true},
["Manaus"] = {container = "Amazonas"}, -- 2,275,000
["Belém"] = {container = "Pará"}, -- 2,200,000
["Belem"] = {alias_of = "Belém", display = true},
["Vitória"] = {container = "Espírito Santo", wp = "%l, %c"}, -- 1,870,000
["Vitoria"] = {alias_of = "Vitória", display = true},
["Santos"] = {container = "São Paulo", wp = "%l, %c"}, -- 1,760,000
["São Luís"] = {container = "Maranhão", wp = "%l, %c"}, -- 1,530,000
["Sao Luis"] = {alias_of = "São Luís", display = true},
["Natal"] = {container = "Rio Grande do Norte", wp = "%l, %c"}, -- 1,360,000
["Florianópolis"] = {container = "Santa Catarina"}, -- 1,260,000
["Florianopolis"] = {alias_of = "Florianópolis", display = true},
["Maceió"] = {container = "Alagoas"}, -- 1,220,000
["Maceio"] = {alias_of = "Maceió", display = true},
["João Pessoa"] = {container = "Paraíba", wp = "%l, %c"}, -- 1,210,000
["Joao Pessoa"] = {alias_of = "João Pessoa", display = true},
["São José dos Campos"] = {container = "São Paulo"}, -- 1,090,000
["Sao Jose dos Campos"] = {alias_of = "São José dos Campos", display = true},
["Londrina"] = {container = "Paraná"}, -- 1,050,000
["Teresina"] = {container = "Piauí"}, -- 1,040,000
}
export.brazil_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(", Brazil", "state"),
default_placetype = "city",
data = export.brazil_cities,
}
export.canada_cities = {
-- Figures from citypopulation.de; retrieved 2025-04-27; reference date 2025-01-01.
["Toronto"] = {container = "Ontario"}, -- 7,850,000 (Consolidated Urban Area; including Hamilton)
["Montreal"] = {container = "Quebec"}, -- 4,500,000 (Consolidated Urban Area)
["Vancouver"] = {container = "British Columbia"}, -- 3,175,000 (Consolidated Urban Area)
["Calgary"] = {container = "Alberta"}, -- 1,510,000 (Consolidated Urban Area)
["Edmonton"] = {container = "Alberta"}, -- 1,460,000 (Consolidated Urban Area)
["Ottawa"] = {container = "Ontario"}, -- 1,390,000 (Consolidated Urban Area)
["Quebec City"] = {container = "Quebec"}, -- 839,311 metro per Wikipedia (2021 census)
["Winnipeg"] = {container = "Manitoba"}, -- 834,678 metro per Wikipedia (2021 census)
["Hamilton"] = {container = "Ontario", wp = "%l, %c"}, -- 785,184 metro per Wikipedia (2021 census)
["Kitchener"] = {container = "Ontario", wp = "%l, %c"}, -- 575,847 metro per Wikipedia (2021 census)
}
export.canada_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(", Canada", "province"),
default_placetype = "city",
data = export.canada_cities,
}
export.france_cities = {
-- Figures from citypopulation.de unless otherwise indicated; retrieved 2025-04-26; reference date 2025-01-01.
["Paris"] = {container = "Île-de-France"}, -- 11,500,000 (Conglomeration)
["Lyon"] = {container = "Auvergne-Rhône-Alpes"}, -- 2,050,000 (Conglomeration)
["Lyons"] = {alias_of = "Lyon", display = true},
["Marseille"] = {container = "Provence-Alpes-Côte d'Azur"}, -- 1,710,000 (Conglomeration)
["Marseilles"] = {alias_of = "Marseille", display = true},
["Lille"] = {container = "Hauts-de-France"}, -- 1,320,000 (Conglomeration)
["Bordeaux"] = {container = "Nouvelle-Aquitaine"}, -- 1,160,000 (Conglomeration)
["Toulouse"] = {container = "Occitania"}, -- 1,150,000 (Conglomeration)
["Nice"] = {container = "Provence-Alpes-Côte d'Azur"},
["Nantes"] = {container = "Pays de la Loire"},
["Strasbourg"] = {container = "Grand Est"},
["Rennes"] = {container = "Brittany"},
}
export.france_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(", France", "region"),
default_placetype = "city",
data = export.france_cities,
}
export.germany_cities = {
-- Figures from citypopulation.de unless otherwise indicated; retrieved 2025-04-26; reference date 2025-01-01.
-- listed under Rhein-Ruhr Area, total population 10,900,000 (Consolidated Urban Area)
["Cologne"] = {container = "North Rhine-Westphalia"},
["Köln"] = {alias_of = "Cologne", display = true},
["Düsseldorf"] = {container = "North Rhine-Westphalia"},
["Dusseldorf"] = {alias_of = "Düsseldorf", display = true},
["Dortmund"] = {container = "North Rhine-Westphalia"},
["Essen"] = {container = "North Rhine-Westphalia"},
["Duisberg"] = {container = "North Rhine-Westphalia"},
["Berlin"] = {}, -- 4,700,000
["Frankfurt"] = {container = "Hesse"}, -- 3,225,000
["Frankfurt am Main"] = {alias_of = "Frankfurt"}, -- not a display alias as it's longer
["Hamburg"] = {}, -- 2,900,000
["Munich"] = {container = "Bavaria"}, -- 2,300,000
["Stuttgart"] = {container = "Baden-Württemberg"}, -- 2,300,000
["Mannheim"] = {container = "Baden-Württemberg"}, -- 1,550,000
["Nuremberg"] = {container = "Bavaria"}, -- 1,120,000
["Hanover"] = {"Lower Saxony"}, -- 1,090,000
["Bielefeld"] = {container = "North Rhine-Westphalia"}, -- 1,080,000
["Leipzig"] = {container = "Saxony"}, -- 1,080,000
["Aachen"] = {container = "North Rhine-Westphalia"}, -- 1,000,000
["Aix-la-Chapelle"] = {alias_of = "Aachen"}, -- historical; not a display alias
["Bremen"] = {},
}
export.germany_cities_group = {
default_container = "Germany",
canonicalize_key_container = make_canonicalize_key_container(", Germany", "state"),
default_placetype = "city",
data = export.germany_cities,
}
export.india_cities = {
-- This lists the 65 metro areas per Demographia's 2023 estimates, as found in
-- [[w:List_of_million-plus_urban_agglomerations_in_India]]. The last census in India (as of April 2025) was
-- conducted in 2011, and the results are not accurate any more.
["Delhi"] = {container = {key = "Delhi, India", placetype = "union territory"}}, -- 31,190,000
["Mumbai"] = {container = "Maharashtra"}, -- 25,189,000
["Kolkata"] = {container = "West Bengal"}, -- 21,747,000
["Bangalore"] = {container = "Karnataka", wp = "Bengaluru"}, -- 15,257,000
["Bengaluru"] = {alias_of = "Bangalore"},
["Chennai"] = {container = "Tamil Nadu"}, -- 11,570,000
["Hyderabad"] = {container = "Telangana"}, -- 9,797,000
["Ahmedabad"] = {container = "Gujarat"}, -- 8,006,000
["Pune"] = {container = "Maharashtra"}, -- 6,819,000
["Surat"] = {container = "Gujarat"}, -- 6,601,000
["Lucknow"] = {container = "Uttar Pradesh"}, -- 4,661,000
["Jaipur"] = {container = "Rajasthan"}, -- 4,360,000
["Kanpur"] = {container = "Uttar Pradesh"}, -- 4,350,000
["Indore"] = {container = "Madhya Pradesh"}, -- 3,765,000
["Nagpur"] = {container = "Maharashtra"}, -- 3,493,000
["Patna"] = {container = "Bihar"}, -- 3,331,000
["Varanasi"] = {container = "Uttar Pradesh"}, -- 3,229,000
["Kozhikode"] = {container = "Kerala"}, -- 3,049,000
["Thiruvananthapuram"] = {container = "Kerala"}, -- 2,851,000
["Agra"] = {container = "Uttar Pradesh"}, -- 2,737,000
["Bhopal"] = {container = "Madhya Pradesh"}, -- 2,562,000
["Coimbatore"] = {container = "Tamil Nadu"}, -- 2,551,000
["Allahabad"] = {container = "Uttar Pradesh", wp = "Prayagraj"}, -- 2,438,000
["Prayagraj"] = {alias_of = "Allahabad"},
["Kochi"] = {container = "Kerala"}, -- 2,381,000
["Ludhiana"] = {container = "Punjab"}, -- 2,205,000
["Vadodara"] = {container = "Gujarat"}, -- 2,182,000
["Chandigarh"] = {container = {key = "Chandigarh, India", placetype = "union territory"}}, -- 2,168,000
["Madurai"] = {container = "Tamil Nadu"}, -- 2,048,000
["Meerut"] = {container = "Uttar Pradesh"}, -- 2,011,000
["Visakhapatnam"] = {container = "Andhra Pradesh"}, -- 2,005,000
["Jamshedpur"] = {container = "Jharkhand"}, -- 1,925,000
["Malappuram"] = {container = "Kerala"}, -- 1,868,000
["Nashik"] = {container = "Maharashtra"}, -- 1,810,000
["Asansol"] = {container = "West Bengal"}, -- 1,720,000
["Aligarh"] = {container = "Uttar Pradesh"}, -- 1,660,000
["Ranchi"] = {container = "Jharkhand"}, -- 1,638,000
["Thrissur"] = {container = "Kerala"}, -- 1,578,000
["Kollam"] = {container = "Kerala"}, -- 1,576,000
["Jabalpur"] = {container = "Madhya Pradesh"}, -- 1,533,000
["Dhanbad"] = {container = "Jharkhand"}, -- 1,503,000
["Jodhpur"] = {container = "Rajasthan"}, -- 1,497,000
["Aurangabad"] = {container = "Maharashtra"}, -- 1,490,000
["Chhatrapati Sambhajinagar"] = {alias_of = "Aurangabad"},
["Rajkot"] = {container = "Gujarat"}, -- 1,487,000
["Gwalior"] = {container = "Madhya Pradesh"}, -- 1,477,000
["Raipur"] = {container = "Chhattisgarh"}, -- 1,429,000
["Gorakhpur"] = {container = "Uttar Pradesh"}, -- 1,410,000
["Kannur"] = {container = "Kerala"}, -- 1,360,000
["Bareilly"] = {container = "Uttar Pradesh"}, -- 1,355,000
["Guwahati"] = {container = "Assam"}, -- 1,355,000
["Moradabad"] = {container = "Uttar Pradesh"}, -- 1,345,000
["Amritsar"] = {container = "Punjab"}, -- 1,313,000
["Mysore"] = {container = "Karnataka"}, -- 1,296,000
["Bhilai"] = {container = "Chhattisgarh"}, -- 1,293,000
["Durg-Bhilainagar"] = {alias_of = "Bhilai"},
["Durg-Bhilai"] = {alias_of = "Bhilai"},
["Durg"] = {alias_of = "Bhilai"},
["Bhilainagar"] = {alias_of = "Bhilai"},
["Vijayawada"] = {container = "Andhra Pradesh"}, -- 1,232,000
["Srinagar"] = {container = {key = "Jammu and Kashmir, India", placetype = "union territory"}}, -- 1,212,000
["Salem"] = {container = "Tamil Nadu", wp = "%l, %c"}, -- 1,189,000
["Kota"] = {container = "Rajasthan"}, -- 1,172,000
["Jalandhar"] = {container = "Punjab"}, -- 1,165,000
["Saharanpur"] = {container = "Uttar Pradesh"}, -- 1,152,000
["Dehradun"] = {container = "Uttarakhand"}, -- 1,136,000
["Tiruchirappalli"] = {container = "Tamil Nadu"}, -- 1,131,000
["Bhubaneswar"] = {container = "Odisha"}, -- 1,112,000
["Jammu"] = {container = {key = "Jammu and Kashmir, India", placetype = "union territory"}}, -- 1,103,000
["Solapur"] = {container = "Maharashtra"}, -- 1,082,000
["Hubli-Dharwad"] = {container = "Karnataka", wp = "Hubli–Dharwad"}, -- 1,062,000; wp with en dash
["Hubli"] = {alias_of = "Hubli-Dharwad"},
["Dharwad"] = {alias_of = "Hubli-Dharwad"},
["Puducherry"] = {container = {key = "Puducherry, India", placetype = "union territory"}}, -- 1,024,000
["Pondicherry"] = {alias_of = "Puducherry", display = true},
-- satellite/secondary cities of metro area (none in citypopulation.de)
["Ghaziabad"] = {container = "Uttar Pradesh"}, -- 1,729,000 city, 2,358,525 urban agglomeration per 2011 census; 3,406,061 2025 estimate from official website; part of Delhi metro area
["Faridabad"] = {container = "Haryana"}, -- 1,414,050 city per 2011 census; part of Delhi metro area
["Thane"] = {container = "Maharashtra"}, -- 1,841,488 city per 2011 census; part of Mumbai metro area
["Kalyan-Dombivli"] = {container = "Maharashtra"}, -- 1,246,381 city per 2011 census; part of Mumbai metro area
["Kalyan-Dombivali"] = {alias_of = "Kalyan-Dombivli", display = true},
["Kalyan"] = {alias_of = "Kalyan-Dombivli"},
["Dombivli"] = {alias_of = "Kalyan-Dombivli"},
["Dombivali"] = {alias_of = "Kalyan-Dombivli"},
["Vasai-Virar"] = {container = "Maharashtra"}, -- 1,221,233 city per 2011 census; part of Mumbai metro area
["Vasai"] = {alias_of = "Vasai-Virar"},
["Virar"] = {alias_of = "Vasai-Virar"},
["Navi Mumbai"] = {container = "Maharashtra"}, -- 1,120,547 city per 2011 census; part of Mumbai metro area
["Howrah"] = {container = "West Bengal"}, -- 1,077,075 city ("metropolis"), 2,811,344 "metro" per 2011 census; part of Kolkata metro area
["Pimpri-Chinchwad"] = {container = "Maharashtra"}, -- 1,727,692 per 2011 census; part of Pune metro area
["Pimpri Chinchwad"] = {alias_of = "Pimpri-Chinchwad", display = true},
}
export.india_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(", India", "state"),
default_placetype = "city",
data = export.india_cities,
}
export.indonesia_cities = {
-- cities where the city proper has more than 1,000,000 people as of mid-2023 estimate
["Jakarta"] = {container = "Special Capital Region of Jakarta", divs = {
{type = "subdistricts", container_parent_type = false},
}},
["Surabaya"] = {container = "East Java"},
["Bekasi"] = {container = "West Java"}, -- part of Jakarta metro area
["Bandung"] = {container = "West Java"},
["Medan"] = {container = "North Sumatra"},
["Depok"] = {container = "West Java"}, -- part of Jakarta metro area
["Tangerang"] = {container = "Banten"}, -- part of Jakarta metro area
["Palembang"] = {container = "South Sumatra"},
["Semarang"] = {container = "Central Java"},
["Makassar"] = {container = "South Sulawesi"},
["South Tangerang"] = {container = "Banten"}, -- part of Jakarta metro area
["Batam"] = {container = "Riau Islands"},
["Bogor"] = {container = "West Java"}, -- part of Jakarta metro area
["Pekanbaru"] = {container = "Riau"},
["Bandar Lampung"] = {container = "Lampung"},
-- other metro areas over 1,000,000 people
["Padang"] = {container = "West Sumatra"},
["Samarinda"] = {container = "East Kalimantan"},
["Malang"] = {container = "East Java"},
["Yogyakarta"] = {container = "Special Region of Yogyakarta"},
["Denpasar"] = {container = "Bali"},
["Cirebon"] = {container = "West Java"},
["Surakarta"] = {container = "Central Java"},
["Banjarmasin"] = {container = "South Kalimantan"},
["Tasikmalaya"] = {container = "West Java"},
}
export.indonesia_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(", Indonesia", "province"),
default_placetype = "city",
data = export.indonesia_cities,
}
export.italy_cities = {
-- Data per [[w:List_of_metropolitan_areas_of_Italy]]. There are several lists given; the most recent one, used
-- here, only gives estimates as of Jan 1, 2014.
["Milan"] = {container = "Lombardy"}, -- 6,623,798
["Naples"] = {container = "Campania"}, -- 5,294,546
["Rome"] = {container = "Lazio"}, -- 4,447,881
["Turin"] = {container = "Piedmont"}, -- 1,865,284
["Venice"] = {container = "Veneto"}, -- 1,645,900
["Florence"] = {container = "Tuscany"}, -- 1,485,030
["Bari"] = {container = "Apulia"}, -- 1,257,459
["Palermo"] = {container = "Sicily"}, -- 1,183,084
-- include a few just below 1,000,000 metro area that may be above it by now (depending on the definition).
["Catania"] = {container = "Sicily"}, -- 988,240
["Brescia"] = {container = "Lombardy"}, -- 924,090
["Genoa"] = {container = "Liguria"}, -- 861,318
}
export.italy_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(", Italy", "region"),
default_placetype = "city",
data = export.italy_cities,
}
export.japan_cities = {
-- Population figures from [[w:List of cities in Japan]]. Metro areas from
-- [[w:List of metropolitan areas in Japan]].
["Tokyo"] = {keydesc = "[[Tokyo]] Metropolis, the [[capital city]] and a [[prefecture]] of [[Japan]] (which is a country in [[Asia]])",
placetype = {"city", "prefecture"},
divs = {
{type = "special wards", container_parent_type = false},
{type = "cities", prep = "in"},
},
},
["Yokohama"] = {container = "Kanagawa"}, -- 3,697,894
["Osaka"] = {container = "Osaka"}, -- 2,668,586
["Nagoya"] = {container = "Aichi"}, -- 2,283,289
-- FIXME, Hokkaido is handled specially.
["Sapporo"] = {container = "Hokkaido"}, -- 1,918,096
["Fukuoka"] = {container = "Fukuoka"}, -- 1,581,527
["Kobe"] = {container = "Hyōgo"}, -- 1,530,847
["Kyoto"] = {container = "Kyoto"}, -- 1,474,570
["Kawasaki"] = {container = "Kanagawa", wp = "%l, Kanagawa"}, -- 1,373,630
["Saitama"] = {container = "Saitama", wp = "%l (city)", commonscat = "%l, %c"}, -- 1,192,418
["Hiroshima"] = {container = "Hiroshima"}, -- 1,163,806
["Sendai"] = {container = "Miyagi"}, -- 1,029,552
-- the remaining cities are considered "central cities" in a 1,000,000+ metro area
-- (sometimes there is more than one central city in the area).
["Kitakyushu"] = {container = "Fukuoka"}, -- 986,998
["Chiba"] = {container = "Chiba", wp = "%l (city)", commonscat = "%l, %c"}, -- 938,695
["Sakai"] = {container = "Osaka"}, -- 835,333
["Niigata"] = {container = "Niigata", wp = "%l (city)", commonscat = "%l, %c"}, -- 813,053
["Hamamatsu"] = {container = "Shizuoka"}, -- 811,431
["Shizuoka"] = {container = "Shizuoka", wp = "%l (city)", commonscat = "%l, %c"}, -- 710,944
["Sagamihara"] = {container = "Kanagawa"}, -- 706,342
["Okayama"] = {container = "Okayama"}, -- 701,293
["Kumamoto"] = {container = "Kumamoto"}, -- 670,348
["Kagoshima"] = {container = "Kagoshima"}, -- 605,196
-- skipped 6 cities (Funabashi, Hachiōji, Kawaguchi, Himeji, Matsuyama, Higashiōsaka)
-- with population in the range 509k - 587k because not central cities in any
-- 1,000,000+ metro area.
["Utsunomiya"] = {container = "Tochigi"}, -- 507,833
}
export.japan_cities_group = {
default_container = "Japan",
canonicalize_key_container = make_canonicalize_key_container(" Prefecture, Japan", "prefecture"),
default_placetype = "city",
data = export.japan_cities,
}
export.mexico_cities = {
["Mexico City"] = {}, -- its own state
["Monterrey"] = {container = "Nuevo León"},
["Guadalajara"] = {container = "Jalisco"},
["Puebla"] = {container = "Puebla", wp = "%l (city)"},
["Toluca"] = {container = "State of Mexico"},
["Tijuana"] = {container = "Baja California"},
-- Include the state in the category for León due to possible confusion with León, Spain.
["León, Guanajuato"] = {container = "Guanajuato", wp = "%l, %c"},
["León"] = {alias_of = "León, Guanajuato"},
["Leon"] = {alias_of = "León, Guanajuato", display = true},
["Querétaro"] = {container = "Querétaro", wp = "%l (city)"},
["Queretaro"] = {alias_of = "Querétaro", display = true},
["Ciudad Juárez"] = {container = "Chihuahua"},
["Juárez"] = {alias_of = "Ciudad Juárez"},
["Juarez"] = {alias_of = "Ciudad Juárez", display = "Juárez"},
["Torreón"] = {container = "Coahuila"},
["Torreon"] = {alias_of = "Torreón", display = true},
-- Include the state in the category for Mérida due to possible confusion with Mérida, Spain or
-- Mérida, Venezuela.
["Mérida, Yucatán"] = {container = "Yucatán", wp = "%l, %c"},
["Mérida"] = {alias_of = "Mérida, Yucatán"},
["Merida"] = {alias_of = "Mérida, Yucatán", display = true},
["San Luis Potosí"] = {container = "San Luis Potosí", wp = "%l (city)"},
["San Luis Potosi"] = {alias_of = "San Luis Potosí", display = true},
["Aguascalientes"] = {container = "Aguascalientes", wp = "%l (city)"},
["Mexicali"] = {container = "Baja California"},
}
export.mexico_cities_group = {
default_container = "Mexico",
canonicalize_key_container = make_canonicalize_key_container(", Mexico", "state"),
default_placetype = "city",
data = export.mexico_cities,
}
export.nigeria_cities = {
-- Figures from citypopulation.de unless otherwise indicated; retrieved 2025-04-26; reference date 2025-01-01.
["Lagos"] = {container = "Lagos"}, -- 21,300,000 (unindicated; population of low reliability)
["Kano"] = {container = "Kano", wp = "%l (city)"}, -- 5,350,000 (unindicated; population of low reliability)
["Ibadan"] = {container = "Oyo"}, -- 3,400,000 (unindicated; population of low reliability)
["Abuja"] = {container = {key = "Federal Capital Territory, Nigeria", placetype = "federal territory"}}, -- 3,050,000 (unindicated; population of low reliability)
["Port Harcourt"] = {container = "Rivers"}, -- 2,250,000 (unindicated; population of low reliability)
["Kaduna"] = {container = "Kaduna"}, -- 1,980,000 (unindicated; population of low reliability)
["Benin City"] = {container = "Edo"}, -- 1,790,000 (unindicated; population of low reliability)
["Aba"] = {container = "Abia", wp = "%l, Nigeria"}, -- 1,280,000 (unindicated; population of low reliability)
["Onitsha"] = {container = "Anambra"}, -- 1,230,000 (unindicated; population of low reliability)
["Maiduguri"] = {container = "Borno"}, -- 1,190,000 (unindicated; population of low reliability)
["Ilorin"] = {container = "Kwara"}, -- 1,160,000 (unindicated; population of low reliability)
["Sokoto"] = {container = "Sokoto", wp = "%l (city)"}, -- 1,140,000 (unindicated; population of low reliability)
["Jos"] = {container = "Plateau"}, -- 1,110,000 (unindicated; population of low reliability)
["Zaria"] = {container = "Kaduna"}, -- 1,050,000 (unindicated; population of low reliability)
["Enugu"] = {container = "Enugu", wp = "%l (city)"}, -- 1,010,000 (unindicated; population of low reliability)
}
export.nigeria_cities_group = {
default_container = "Nigeria",
canonicalize_key_container = make_canonicalize_key_container(" State, Nigeria", "state"),
default_placetype = "city",
data = export.nigeria_cities,
}
export.pakistan_cities = {
-- Figures from citypopulation.de; retrieved 2025-04-26; reference date 2025-01-01.
["Karachi"] = {container = "Sindh"}, -- 21,000,000 (Consolidated Urban Area)
["Lahore"] = {container = "Punjab"}, -- 14,600,000 (Consolidated Urban Area)
["Rawalpindi"] = {container = "Punjab"}, -- 5,600,000 (Consolidated Urban Area; including Islamabad)
["Islamabad"] = {container = {key = "Islamabad Capital Territory, Pakistan", placetype = "federal territory"}}, -- 5,600,000 (Consolidated Urban Area; including Rawalpindi)
["Faisalabad"] = {container = "Punjab"}, -- 4,125,000 (Consolidated Urban Area)
["Gujranwala"] = {container = "Punjab"}, -- 3,450,000 (Consolidated Urban Area)
-- there is also Hyderabad in India (very confusing)
["Hyderabad, Pakistan"] = {container = "Sindh", wp = "%l, %c"}, -- 2,475,000 (Consolidated Urban Area)
["Hyderabad"] = {alias_of = "Hyderabad, Pakistan"},
["Multan"] = {container = "Punjab"}, -- 2,425,000 (Consolidated Urban Area)
["Peshawar"] = {container = "Khyber Pakhtunkhwa"}, -- 2,150,000 (Consolidated Urban Area)
["Quetta"] = {container = "Balochistan"}, -- 1,720,000 (Urban Area)
["Sargodha"] = {container = "Punjab"}, -- 1,080,000 (Urban Area)
["Sialkot"] = {container = "Punjab"}, -- 1,050,000 (Consolidated Urban Area)
}
export.pakistan_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(", Pakistan", "province"),
default_placetype = "city",
data = export.pakistan_cities,
}
export.philippines_cities = {
-- Skipped some cities in Metro Manila (Taguig, Pasig) which don't have districts.
-- Other cities outside Metro Manila skipped as not central city in their urban area.
["Quezon City"] = {container = {key = "Metro Manila, Philippines", placetype = "region"}},
-- Don't display-canonicalize Foo to Foo City as it may make the display weird.
["Quezon"] = {alias_of = "Quezon City"},
["Manila"] = {container = {key = "Metro Manila, Philippines", placetype = "region"}},
["Davao City"] = {container = "Davao del Sur"},
["Davao"] = {alias_of = "Davao City"},
["Caloocan"] = {container = {key = "Metro Manila, Philippines", placetype = "region"}},
["Zamboanga City"] = {container = "Zamboanga del Sur"},
["Zamboanga"] = {alias_of = "Zamboanga City"},
["Cebu City"] = {container = "Cebu"},
["Cebu"] = {alias_of = "Cebu City"},
["Antipolo"] = {container = "Rizal"},
["Cagayan de Oro"] = {container = "Misamis Oriental"},
["Dasmariñas"] = {container = "Cavite"},
["Dasmarinas"] = {alias_of = "Dasmariñas", display = true},
["General Santos"] = {container = "South Cotabato"},
["San Jose del Monte"] = {container = "Bulacan"},
["Bacolod"] = {container = "Negros Occidental"},
["Calamba"] = {container = "Laguna", wp = "%l, %c"},
["Angeles"] = {container = "Pampanga", wp = "Angeles City"},
["Angeles City"] = {alias_of = "Angeles"},
["Iloilo City"] = {container = "Iloilo"},
["Iloilo"] = {alias_of = "Iloilo City"},
}
export.philippines_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(", Philippines", "province"),
default_placetype = "city",
data = export.philippines_cities,
}
export.russia_cities = {
-- Figures from citypopulation.de; retrieved 2025-04-26; reference date 2025-01-01.
["Moscow"] = {}, -- 18,800,000 (Agglomeration)
["Saint Petersburg"] = {}, -- 6,350,000 (Agglomeration)
["Novosibirsk"] = {container = "Novosibirsk Oblast"}, -- 1,820,000 (Agglomeration)
["Yekaterinburg"] = {container = "Sverdlovsk Oblast"}, -- 1,810,000 (Agglomeration)
["Nizhny Novgorod"] = {container = "Nizhny Novgorod Oblast"}, -- 1,620,000 (Agglomeration)
["Kazan"] = {container = {key = "Tatarstan, Russia", placetype = "republic"}}, -- 1,560,000 (Agglomeration)
["Chelyabinsk"] = {container = "Chelyabinsk Oblast"}, -- 1,430,000 (Agglomeration)
["Rostov-on-Don"] = {container = "Rostov Oblast"}, -- 1,390,000 (Agglomeration)
["Rostov-na-Donu"] = {alias_of = "Rostov-on-Don", display = true},
["Krasnodar"] = {container = {key = "Krasnodar Krai, Russia", placetype = "krai"}}, -- 1,370,000 (Agglomeration)
["Samara"] = {container = "Samara Oblast"}, -- 1,350,000 (Agglomeration)
["Krasnoyarsk"] = {container = {key = "Krasnoyarsk Krai, Russia", placetype = "krai"}}, -- 1,270,000 (Agglomeration)
["Ufa"] = {container = {key = "Bashkortostan, Russia", placetype = "republic"}}, -- 1,230,000 (Agglomeration)
["Saratov"] = {container = "Saratov Oblast"}, -- 1,170,000 (Agglomeration)
["Omsk"] = {container = "Omsk Oblast"}, -- 1,140,000 (Agglomeration)
["Voronezh"] = {container = "Voronezh Oblast"}, -- 1,130,000 (Agglomeration)
["Volgograd"] = {container = "Volgograd Oblast"}, -- 1,080,000 (Agglomeration)
["Perm"] = {container = {key = "Perm Krai, Russia", placetype = "krai"}, wp = "%l, Russia"}, -- 1,070,000 (Agglomeration)
}
export.russia_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(", Russia", "oblast"),
default_container = "Russia",
default_placetype = "city",
data = export.russia_cities,
}
export.saudi_arabia_cities = {
-- Figures for the first five from [[w:List of cities and towns in Saudi Arabia]] as of 2022. Unclear if these are
-- metro, urban or city proper figures.
["Riyadh"] = {container = "Riyadh"}, -- 7,000,100; 7,700,000 per citypopulation.de 2025-01-01 (Agglomeration)
["Jeddah"] = {container = "Mecca"}, -- 3,751,917; 3,950,000 per citypopulation.de 2025-01-01 (Agglomeration)
["Jedda"] = {alias_of = "Jeddah", display = true},
["Jiddah"] = {alias_of = "Jeddah", display = true},
["Jidda"] = {alias_of = "Jeddah", display = true},
["Dammam"] = {container = "Eastern"}, -- 2,638,166; 2,925,000 per citypopulation.de 2025-01-01 (Agglomeration)
["Mecca"] = {container = "Mecca"}, -- 2,385,509; 2,675,000 per citypopulation.de 2025-01-01 (Agglomeration)
["Makkah"] = {alias_of = "Mecca", display = true},
["Medina"] = {container = "Medina"}, -- 1,477,023; 1,530,000 per citypopulation.de 2025-01-01 (City)
["Hofuf"] = {container = "Eastern"}, -- 1,060,000 per citypopulation.de 2025-01-01 (Agglomeration)
["Khamis Mushait"] = {container = "Aseer"}, -- 1,030,000 per citypopulation.de 2025-01-01 (Agglomeration)
["Khamis Mushayt"] = {alias_of = "Khamis Mushait", display = true},
}
export.saudi_arabia_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(" Province, Saudi Arabia", "province"),
default_placetype = "city",
data = export.saudi_arabia_cities,
}
export.south_korea_cities = {
-- All cities listed are not associated with any county.
["Seoul"] = {},
["Busan"] = {},
["Incheon"] = {},
["Daegu"] = {},
["Daejeon"] = {},
["Gwangju"] = {},
["Ulsan"] = {},
}
export.south_korea_cities_group = {
default_container = "South Korea",
canonicalize_key_container = make_canonicalize_key_container(" County, South Korea", "province"),
default_placetype = "city",
data = export.south_korea_cities,
}
export.spain_cities = {
["Madrid"] = {container = "Community of Madrid"},
["Barcelona"] = {container = "Catalonia"},
["Valencia"] = {container = "Valencia"},
["Seville"] = {container = "Andalusia"},
["Bilbao"] = {container = "Basque Country"},
}
export.spain_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(", Spain", "autonomous community"),
default_placetype = "city",
data = export.spain_cities,
}
export.taiwan_cities = {
["New Taipei City"] = {},
["New Taipei"] = {alias_of = "New Taipei City", display = true},
["Taichung"] = {},
["Kaohsiung"] = {wp = "%l, Taiwan"},
["Taipei"] = {},
["Taoyuan"] = {},
["Tainan"] = {},
-- these last three are not special municipalities
["Chiayi"] = {placetype = "city"},
["Hsinchu"] = {placetype = "city"},
["Keelung"] = {placetype = "city"},
}
export.taiwan_cities_group = {
placename_to_key = false, -- don't add ", Taiwan" to make the key
canonicalize_key_container = make_canonicalize_key_container(", Taiwan", "county"),
default_container = "Taiwan",
default_placetype = {"special municipality", "municipality", "city"},
default_is_city = true,
default_divs = {"districts"},
data = export.taiwan_cities,
}
-- NOTE: It's OK to mix cities from different constituent countries; as long as the immediate container is correct,
-- everything else will be figured out.
export.united_kingdom_cities = {
["London"] = {container = "Greater London"},
["Manchester"] = {container = "Greater Manchester"},
["Birmingham"] = {container = "West Midlands"},
["Liverpool"] = {container = "Merseyside"},
["Glasgow"] = {container = {key = "City of Glasgow, Scotland", placetype = "council area"}},
["Leeds"] = {container = "West Yorkshire"},
["Newcastle upon Tyne"] = {container = "Tyne and Wear"},
["Newcastle"] = {alias_of = "Newcastle upon Tyne"},
["Bristol"] = {container = {key = "England", placetype = "constituent country"}},
["Cardiff"] = {container = {key = "Wales", placetype = "constituent country"}},
["Portsmouth"] = {container = "Hampshire"},
["Edinburgh"] = {container = {key = "City of Edinburgh, Scotland", placetype = "council area"}},
-- under 1,000,000 people but principal areas of Wales; requested by [[User:Donnanz]]
["Swansea"] = {container = {key = "Wales", placetype = "constituent country"}},
["Newport"] = {container = {key = "Wales", placetype = "constituent country"}, wp = "Newport, Wales"},
}
export.united_kingdom_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(", England", "county"),
default_placetype = "city",
data = export.united_kingdom_cities,
}
export.united_states_cities = {
-- top 50 CSA's by population, with the top and sometimes 2nd or 3rd city listed
["New York City"] = {container = "New York", wp = "%l", divs = {
{type = "boroughs", container_parent_type = false},
}},
-- Don't display-canonicalize as it may make the display weird (e.g. in the context New York, New York).
["New York"] = {alias_of = "New York City"},
["Newark"] = {container = "New Jersey"},
["Los Angeles"] = {container = "California", wp = "%l"},
["Long Beach"] = {container = "California"},
["Riverside"] = {container = "California"},
["Chicago"] = {container = "Illinois", wp = "%l"},
["Washington, D.C."] = {wp = "%l"},
["Washington, DC"] = {alias_of = "Washington, D.C.", display = true},
["Washington D.C."] = {alias_of = "Washington, D.C.", display = true},
["Washington DC"] = {alias_of = "Washington, D.C.", display = true},
-- Don't display-canonicalize as it may make the display weird (e.g. if the holonym is followed by a District of
-- Columbia holonym).
["Washington"] = {alias_of = "Washington, D.C."},
["Baltimore"] = {container = "Maryland", wp = "%l"},
-- to avoid conflict with San Jose in Costa Rica
["San Jose, California"] = {container = "California"},
["San Jose"] = {alias_of = "San Jose, California"},
["San Francisco"] = {container = "California", wp = "%l"},
["Oakland"] = {container = "California"},
["Boston"] = {container = "Massachusetts", wp = "%l"},
["Providence"] = {container = "Rhode Island"},
["Dallas"] = {container = "Texas", wp = "%l", commonscat = "%l, %c"},
["Fort Worth"] = {container = "Texas"},
["Philadelphia"] = {container = "Pennsylvania", wp = "%l"},
["Houston"] = {container = "Texas", wp = "%l"},
["Miami"] = {container = "Florida", wp = "%l", commonscat = "%l, %c"},
["Atlanta"] = {container = "Georgia", wp = "%l"},
["Detroit"] = {container = "Michigan", wp = "%l"},
["Phoenix"] = {container = "Arizona", wp = "%l", commonscat = "%l, %c"},
["Mesa"] = {container = "Arizona"},
["Seattle"] = {container = "Washington", wp = "%l"},
["Orlando"] = {container = "Florida"},
["Minneapolis"] = {container = "Minnesota", wp = "%l"},
["Cleveland"] = {container = "Ohio", wp = "%l", commonscat = "%l, %c"},
["Denver"] = {container = "Colorado", wp = "%l", commonscat = "%l, %c"},
["San Diego"] = {container = "California", wp = "%l", commonscat = "%l, %c"},
["Portland"] = {container = "Oregon"},
["Tampa"] = {container = "Florida"},
["St. Louis"] = {container = "Missouri", wp = "%l", commonscat = "%l, %c"},
["Saint Louis"] = {alias_of = "St. Louis", display = true},
["Charlotte"] = {container = "North Carolina"},
["Sacramento"] = {container = "California"},
["Pittsburgh"] = {container = "Pennsylvania", wp = "%l"},
["Salt Lake City"] = {container = "Utah", wp = "%l"},
["San Antonio"] = {container = "Texas", wp = "%l", commonscat = "%l, %c"},
["Columbus"] = {container = "Ohio"},
["Kansas City"] = {container = "Missouri", wp = "%l metropolitan area", commonscat = "%l, %c"},
["Indianapolis"] = {container = "Indiana", wp = "%l"},
["Las Vegas"] = {container = "Nevada", wp = "%l"},
["Cincinnati"] = {container = "Ohio", wp = "%l", commonscat = "%l, %c"},
["Austin"] = {container = "Texas"},
["Milwaukee"] = {container = "Wisconsin", wp = "%l", commonscat = "%l, %c"},
["Raleigh"] = {container = "North Carolina"},
["Nashville"] = {container = "Tennessee"},
["Virginia Beach"] = {container = "Virginia"},
["Norfolk"] = {container = "Virginia"},
["Greensboro"] = {container = "North Carolina"},
["Winston-Salem"] = {container = "North Carolina"},
["Jacksonville"] = {container = "Florida"},
["New Orleans"] = {container = "Louisiana", wp = "%l"},
["Louisville"] = {container = "Kentucky"},
["Greenville"] = {container = "South Carolina"},
["Hartford"] = {container = "Connecticut"},
["Oklahoma City"] = {container = "Oklahoma", wp = "%l"},
["Grand Rapids"] = {container = "Michigan"},
["Memphis"] = {container = "Tennessee"},
["Birmingham, Alabama"] = {container = "Alabama"},
["Birmingham"] = {alias_of = "Birmingham, Alabama"},
["Fresno"] = {container = "California"},
["Richmond"] = {container = "Virginia"},
["Harrisburg"] = {container = "Pennsylvania"},
-- any major city of top 50 MSA's that's missed by previous
["Buffalo"] = {container = "New York"},
-- any of the top 50 city by city population that's missed by previous
["El Paso"] = {container = "Texas"},
["Albuquerque"] = {container = "New Mexico"},
["Tucson"] = {container = "Arizona"},
["Colorado Springs"] = {container = "Colorado"},
["Omaha"] = {container = "Nebraska"},
["Tulsa"] = {container = "Oklahoma"},
-- skip Arlington, Texas; too obscure and likely to be interpreted as Arlington, Virginia
}
export.united_states_cities_group = {
default_container = "United States",
canonicalize_key_container = make_canonicalize_key_container(", USA", "state"),
default_placetype = "city",
default_wp = "%l, %c",
data = export.united_states_cities,
}
export.new_york_boroughs = {
["Bronx"] = {the = true, wp = "The Bronx"},
["Brooklyn"] = {},
["Manhattan"] = {},
["Queens"] = {},
["Staten Island"] = {},
}
export.new_york_boroughs_group = {
default_container = {key = "New York City", placetype = "city"},
default_placetype = "borough",
default_is_city = true,
data = export.new_york_boroughs,
}
export.vietnam_cities = {
-- Figures from citypopulation.de (retrieved 2025-04-26; reference date 2025-01-01) unless otherwise indicated.
["Ho Chi Minh City"] = {}, -- 14,300,000 (Agglomeration; inclunding Bien Hoa)
["Saigon"] = {alias_of = "Ho Chi Minh City"},
["Hanoi"] = {}, -- 7,350,000 (Agglomeration)
["Da Nang"] = {}, -- 1,500,000 (Agglomeration)
["Danang"] = {alias_of = "Da Nang", display = true},
["Haiphong"] = {}, -- 1,450,000 (Agglomeration)
["Hai Phong"] = {alias_of = "Haiphong", display = true},
-- This is the one entry in this list that is not a province-level municipality; instead it's a "provincial city"
-- meaning it is directly under its province as opposed to being contained in a district.
["Bien Hoa"] = {placetype = "city", container = "Đồng Nai", wp = "Biên Hòa"}, -- 1,272,235 (2022 city population per Wikipedia)
["Biên Hòa"] = {alias_of = "Bien Hoa", display = true},
["Biên Hoà"] = {alias_of = "Bien Hoa", display = true},
-- These two not in citypopulation.de because the urban population may be slightly under 1,000,000, but they are
-- both province-level municipalities and close to the 1,000,000 mark.
["Can Tho"] = {wp = "Cần Thơ"}, -- 1,456,000 municipality (2019 census), 994,704 urban (2022 General Statistics Office of Vietnam estimate); capital [[Ninh Kiều district]]
["Cần Thơ"] = {alias_of = "Can Tho", display = true},
["Hue"] = {wp = "Huế"}, -- 1,257,000 municipality (2019 census), 840,000 urban (2022 General Statistics Office of Vietnam estimate); -- capital [[Thuận Hóa district]]
["Huế"] = {alias_of = "Hue", display = true},
}
export.vietnam_cities_group = {
placename_to_key = false, -- don't add ", Vietnam" to make the key
default_container = "Vietnam",
canonicalize_key_container = make_canonicalize_key_container(" Province, Vietnam", "province"),
-- Most of the cities listed are province-level municipalities in addition, which contain a certain amount of
-- rural territory surrounding the city, but not enough to separate the municipality from the city as distinct
-- known locations.
default_placetype = {"municipality", "city"},
default_is_city = true,
-- There may not be enough districts to subcategorize like this.
-- default_divs = "districts",
data = export.vietnam_cities,
}
export.misc_cities = {
------------------ Africa -------------------
-- Sorted by country and then within the country, by decreasing population; figures from citypopulation.de
-- (retrieved 2025-04-26; reference date 2025-01-01) unless otherwise indicated; combined with data from
-- [[w:List of urban areas in Africa by population]].
["Algiers"] = {container = "Algeria"}, -- 4,325,000 (Consolidated Urban Area)
["Oran"] = {container = "Algeria"}, -- 1,640,000 (Consolidated Urban Area)
["Luanda"] = {container = "Angola"}, -- 9,650,000 (Urban Area)
["Benguela"] = {container = "Angola"}, -- 1,420,000 (Urban Area)
["Cotonou"] = {container = "Benin"}, -- 2,150,000 (Agglomeration)
["Ouagadougou"] = {container = "Burkina Faso"}, -- 3,425,000 (Agglomeration)
["Bobo-Dioulasso"] = {container = "Burkina Faso"}, -- 1,100,000 (Agglomeration)
["Bujumbura"] = {container = "Burundi"}, -- 1,143,202 (Urban Area 2023 per PopulationStat, cited in Wikipedia)
["Yaoundé"] = {container = "Cameroon"}, -- 3,975,000 (City)
["Yaounde"] = {alias_of = "Yaoundé", display = true},
["Douala"] = {container = "Cameroon"}, -- 3,900,000 (City)
["Bangui"] = {container = "Central African Republic"}, -- 1,680,000 (Agglomeration)
["N'Djamena"] = {container = "Chad"}, -- 1,950,000 (City)
["Ndjamena"] = {alias_of = "N'Djamena", display = true},
["Kinshasa"] = {container = "Democratic Republic of the Congo"}, -- 16,300,000 (City; population of low reliability)
["Lubumbashi"] = {container = "Democratic Republic of the Congo"}, -- 2,875,000 (City; population of low reliability)
["Mbuji-Mayi"] = {container = "Democratic Republic of the Congo"}, -- 2,500,000 (City; population of low reliability)
["Kananga"] = {container = "Democratic Republic of the Congo"}, -- 1,370,000 (City; population of low reliability)
["Kisangani"] = {container = "Democratic Republic of the Congo"}, -- 1,300,000 (City; population of low reliability)
["Bukavu"] = {container = "Democratic Republic of the Congo"}, -- 1,100,000 (City; population of low reliability)
["Goma"] = {container = "Democratic Republic of the Congo"}, -- 1,010,000 (City; population of low reliability)
["Tshikapa"] = {container = "Democratic Republic of the Congo"}, -- 1,020,468 (2023 Wikipedia [[w:List of cities with over one million inhabitants]] from populationstat.com; not in citypopulation.de)
["Cairo"] = {container = "Egypt"}, -- 22,800,000 (Agglomeration, including Giza and Subhra El Kheima)
["Alexandria"] = {container = "Egypt"}, -- 6,250,000 (Agglomeration)
["Giza"] = {container = "Egypt"}, -- 4,458,135 (2023 from citypopulation.de)
["Shubra El Kheima"] = {container = "Egypt"}, -- 1,240,239 (2021 from citypopulation.de)
["Asmara"] = {container = "Eritrea"}, -- 1,090,000 (City; population of low reliability)
["Asmera"] = {alias_of = "Asmara", display = true},
["Addis Ababa"] = {container = "Ethiopia"}, -- 4,825,000 (Agglomeration)
["Banjul"] = {container = "Gambia"}, -- 1,170,000 (Agglomeration)
["Accra"] = {container = "Ghana"}, -- 6,800,000 (Agglomeration)
["Kumasi"] = {container = "Ghana"}, -- 2,900,000 (Agglomeration)
["Conakry"] = {container = "Guinea"}, -- 2,975,000 (Consolidated Urban Area)
["Abidjan"] = {container = "Ivory Coast"}, -- 7,050,000 (Agglomeration)
["Nairobi"] = {container = "Kenya"}, -- 6,900,000 (unindicated)
["Mombasa"] = {container = "Kenya"}, -- 1,370,000 (City)
["Monrovia"] = {container = "Liberia"}, -- 1,940,000 (Urban Area)
["Tripoli"] = {container = "Libya", wp = "%l, %c"}, -- 1,870,000 (unindicated)
["Antananarivo"] = {container = "Madagascar"}, -- 3,150,000 (Agglomeration)
["Lilongwe"] = {container = "Malawi"}, -- 1,210,000 (City)
["Bamako"] = {container = "Mali"}, -- 5,700,000 (Agglomeration)
["Nouakchott"] = {container = "Mauritania"}, -- 1,500,000 (City)
["Casablanca"] = {container = {key = "Casablanca-Settat, Morocco", placetype = "region"}}, -- 4,450,000 (Municipality (urban population))
["Rabat"] = {container = {key = "Rabat-Sale-Kenitra, Morocco", placetype = "region"}}, -- 2,125,000 (Municipality (urban population))
["Tangier"] = {container = {key = "Tangier-Tetouan-Al Hoceima, Morocco", placetype = "region"}}, -- 1,410,000 (Municipality (urban population))
["Tanger"] = {alias_of = "Tangier", display = true},
["Tangiers"] = {alias_of = "Tangier", display = true},
["Fez"] = {container = {key = "Fez-Meknes, Morocco", placetype = "region"}, wp = "%l, Morocco"}, -- 1,310,000 (Municipality (urban population))
["Fes"] = {alias_of = "Fez", display = true},
["Fès"] = {alias_of = "Fez", display = true},
["Agadir"] = {container = {key = "Souss-Massa, Morocco", placetype = "region"}}, -- 1,270,000 (Municipality (urban population))
["Marrakesh"] = {container = {key = "Marrakesh-Safi, Morocco", placetype = "region"}}, -- 1,140,000 (Municipality (urban population))
["Marrakech"] = {alias_of = "Marrakesh", display = true},
["Maputo"] = {container = "Mozambique"}, -- 2,575,000 (Agglomeration)
["Niamey"] = {container = "Niger"}, -- 1,530,000 (City)
["Brazzaville"] = {container = "Republic of the Congo"}, -- 2,475,000 (Agglomeration)
["Pointe-Noire"] = {container = "Republic of the Congo"}, -- 1,480,000 (City)
["Kigali"] = {container = "Rwanda"}, -- 1,960,000 (Municipality (urban population))
["Dakar"] = {container = "Senegal"}, -- 4,225,000 (Agglomeration)
["Touba"] = {container = "Senegal"}, -- 1,320,000 (Agglomeration)
["Freetown"] = {container = "Sierra Leone"}, -- 1,420,000 (Agglomeration)
["Mogadishu"] = {container = "Somalia"}, -- 2,250,000 (unindicated; population of low reliability)
["Johannesburg"] = {container = {key = "Gauteng, South Africa", placetype = "province"}}, -- 14,800,000 (Consolidated Urban Area; including Pretoria, Soweto, etc.)
["Cape Town"] = {container = {key = "Western Cape, South Africa", placetype = "province"}}, -- 5,100,000 (Consolidated Urban Area)
["Durban"] = {container = {key = "KwaZulu-Natal, South Africa", placetype = "province"}}, -- 3,900,000 (Consolidated Urban Area)
["Pretoria"] = {container = {key = "Gauteng, South Africa", placetype = "province"}}, -- 2,921,488 (2011 census)
["Port Elizabeth"] = {container = {key = "Eastern Cape, South Africa", placetype = "province"}, wp = "Gqeberha"}, -- 1,200,000 (Consolidated Urban Area)
["Gqeberha"] = {alias_of = "Port Elizabeth"}, -- official name; not a display alias
["Khartoum"] = {container = "Sudan"}, -- 7,200,000 (unindicated; population of low reliability)
["Dar es Salaam"] = {container = "Tanzania"}, -- 6,650,000 (Agglomeration)
["Mwanza"] = {container = "Tanzania"}, -- 1,340,000 (Agglomeration)
["Mwanza City"] = {alias_of = "Mwanza", display = true},
["Arusha"] = {container = "Tanzania"}, -- 1,190,000 (Agglomeration)
["Zanzibar"] = {container = "Tanzania"}, -- 1,030,000 (Agglomeration)
["Lomé"] = {container = "Togo"}, -- 2,625,000 (unindicated)
["Lome"] = {alias_of = "Lomé", display = true},
["Tunis"] = {container = "Tunisia"}, -- 2,725,000 (Municipality (urban population))
["Sousse"] = {container = "Tunisia"}, -- 1,180,000 (Municipality (urban population))
["Soussa"] = {alias_of = "Sousse", display = true},
["Kampala"] = {container = "Uganda"}, -- 4,300,000 (unindicated)
["Lusaka"] = {container = "Zambia"}, -- 3,000,000 (Consolidated Urban Area)
["Harare"] = {container = "Zimbabwe"}, -- 2,675,000 (Agglomeration)
------------------ Asia -------------------
-- sorted by country and then within the country, by decreasing population; figures from citypopulation.de
-- (retrieved 2025-04-26; reference date 2025-01-01) unless otherwise indicated.
["Kabul"] = {container = "Afghanistan"}, -- 5,250,000 (Agglomeration)
["Baku"] = {container = "Azerbaijan"}, -- 3,725,000 (Administrative Area (urban population))
["Manama"] = {container = "Bahrain"}, -- 1,560,000 (unindicated)
["Dhaka"] = {container = {key = "Dhaka Division, Bangladesh", placetype = "division"}}, -- 23,100,000 (Agglomeration)
["Dacca"] = {alias_of = "Dhaka", display = true},
["Chittagong"] = {container = {key = "Chittagong Division, Bangladesh", placetype = "division"}}, -- 5,050,000 (Agglomeration)
["Gazipur"] = {container = {key = "Dhaka Division, Bangladesh", placetype = "division"}}, -- 2,674,697 (City per 2022; countied in citypopulation.de as part of Dhaka metro area)
["Khulna"] = {container = {key = "Khulna Division, Bangladesh", placetype = "division"}}, -- 1,210,000 (Agglomeration)
["Phnom Penh"] = {container = "Cambodia"}, -- 2,925,000 (Agglomeration)
["Tehran"] = {container = {key = "Tehran Province, Iran", placetype = "province"}}, -- 16,800,000 (Agglomeration)
["Teheran"] = {alias_of = "Tehran", display = true},
["Mashhad"] = {container = {key = "Razavi Khorasan Province, Iran", placetype = "province"}}, -- 3,475,000 (Agglomeration)
["Mashad"] = {alias_of = "Mashhad", display = true},
["Meshhed"] = {alias_of = "Mashhad", display = true},
["Meshed"] = {alias_of = "Mashhad", display = true},
["Isfahan"] = {container = {key = "Isfahan Province, Iran", placetype = "province"}}, -- 3,425,000 (Agglomeration)
["Esfahan"] = {alias_of = "Isfahan", display = true},
["Tabriz"] = {container = {key = "East Azerbaijan Province, Iran", placetype = "province"}}, -- 1,970,000 (Agglomeration)
["Shiraz"] = {container = {key = "Fars Province, Iran", placetype = "province"}}, -- 1,950,000 (Agglomeration)
["Ahvaz"] = {container = {key = "Khuzestan Province, Iran", placetype = "province"}}, -- 1,550,000 (Agglomeration)
["Qom"] = {container = {key = "Qom Province, Iran", placetype = "province"}}, -- 1,450,000 (City)
["Kermanshah"] = {container = {key = "Kermanshah Province, Iran", placetype = "province"}}, -- 1,130,000 (City)
["Baghdad"] = {container = "Iraq"}, -- 7,800,000 (Administrative Area (urban population))
["Basra"] = {container = "Iraq"}, -- 1,710,000 (Administrative Area (urban population))
["Mosul"] = {container = "Iraq"}, -- 1,550,000 (Administrative Area (urban population))
["Erbil"] = {container = "Iraq"}, -- 1,220,000 (Administrative Area (urban population))
["Kirkuk"] = {container = "Iraq"}, -- 1,160,000 (Administrative Area (urban population))
["Najaf"] = {container = "Iraq"}, -- 1,050,000 (Administrative Area (urban population))
["Tel Aviv"] = {container = "Israel"}, -- 3,000,000 (Agglomeration)
-- Jerusalem is not recognized internationally as part of either Israel or Palestine, but as a
-- [[w:corpus separatum]], so put the container as "Asia" and list Israel and Palestine as additional parents for
-- categorization purposes.
["Jerusalem"] = {container = {key = "Asia", placetype = "continent"},
addl_parents = {"Israel", "Palestine"}}, -- 1,080,000 (Agglomeration)
["Amman"] = {container = "Jordan"}, -- 6,150,000 (unindicated)
["Irbid"] = {container = "Jordan"}, -- 1,070,000 (unindicated)
["Almaty"] = {container = "Kazakhstan"}, -- 2,700,000 (Agglomeration)
["Alma-Ata"] = {alias_of = "Almaty"}, -- former name, sometimes still used; don't display-canonicalize
["Astana"] = {container = "Kazakhstan"}, -- 1,600,000 (Agglomeration)
["Shymkent"] = {container = "Kazakhstan"}, -- 1,370,000 (Agglomeration)
["Kuwait City"] = {container = "Kuwait"}, -- 5,050,000 (Agglomeration)
["Bishkek"] = {container = "Kyrgyzstan"}, -- 1,540,000 (Agglomeration)
["Beirut"] = {container = "Lebanon"}, -- 1,930,000 (unindicated; population of low reliability)
-- Kuala Lumpur is a federal capital city, not in any state
["Kuala Lumpur"] = {container = "Malaysia"}, -- 9,550,000 (Agglomeration)
-- there are various George Towns and Georgetowns
["George Town, Malaysia"] = {container = {key = "Penang, Malaysia", placetype = "state"}, wp = "%l, %c"}, -- 2,075,000 (Agglomeration)
["George Town"] = {alias_of = "George Town, Malaysia"},
["Ulaanbaatar"] = {container = "Mongolia"}, -- 1,610,000 (City)
["Ulan Bator"] = {alias_of = "Ulaanbaatar", display = true},
["Yangon"] = {container = "Myanmar"}, -- 5,650,000 (Municipality (urban population))
["Rangoon"] = {alias_of = "Yangon", display = true},
["Mandalay"] = {container = "Myanmar"}, -- 1,600,000 (Municipality (urban population))
["Kathmandu"] = {container = "Nepal"}, -- 3,175,000 (Agglomeration)
-- Pyongyang is a directly governed city, not in any province
["Pyongyang"] = {container = "North Korea"}, -- 3,025,000 (Administrative Area (urban population))
["Muscat"] = {container = "Oman"}, -- 1,620,000 (Agglomeration)
["Gaza"] = {container = "Palestine", wp = "Gaza City"}, -- 2,275,000 (unindicated)
["Gaza City"] = {alias_of = "Gaza"},
["Doha"] = {container = "Qatar"}, -- 2,650,000 (Agglomeration)
["Colombo"] = {container = "Sri Lanka"}, -- 4,975,000 (unindicated)
["Damascus"] = {container = "Syria"}, -- 3,975,000 (unindicated; population of low reliability)
["Aleppo"] = {container = "Syria"}, -- 1,980,000 (unindicated; population of low reliability)
["Dushanbe"] = {container = "Tajikistan"}, -- 1,270,000 (City)
["Bangkok"] = {container = "Thailand"}, -- 21,800,000 (Agglomeration)
-- Chiang Mai not in citypopulation.de, but 1,198,000 urban population in 2021 per Wikipedia
-- [[w:List_of_municipalities_in_Thailand#Largest_cities_by_urban_population]]
["Chiang Mai"] = {container = {key = "Chiang Mai Province, Thailand", placetype = "province"}},
["Chonburi"] = {container = {key = "Chonburi Province, Thailand", placetype = "province"}}, -- 1,570,000 (Agglomeration; including Pattaya)
-- metro area population stats from https://www.statista.com/statistics/255483/biggest-cities-in-turkey/ as of 2021;
-- second source is citypopulation.de reference date 2025-01-01.
["Istanbul"] = {placetype = {"city", "province"}, divs = {"districts"}, container = "Turkey"}, -- 15.2 million; 16,000,000 (Agglomeration)
["İstanbul"] = {alias_of = "Istanbul", display = true},
["Ankara"] = {container = {key = "Ankara Province, Turkey", placetype = "province"}}, -- 5.15 million; 5,200,000 (Agglomeration)
["Izmir"] = {container = {key = "İzmir Province, Turkey", placetype = "province"}, wp = "İzmir"}, -- 2.95 million; 3,025,000 (Agglomeration)
["İzmir"] = {alias_of = "Izmir", display = true},
["Bursa"] = {container = {key = "Bursa Province, Turkey", placetype = "province"}}, -- 2.02 million; 2,200,000 (Agglomeration)
["Adana"] = {container = {key = "Adana Province, Turkey", placetype = "province"}}, -- 1.77 million; 1,780,000 (Agglomeration)
["Gaziantep"] = {container = {key = "Gaziantep Province, Turkey", placetype = "province"}}, -- 1.71 million; 1,750,000 (Agglomeration)
["Antalya"] = {container = {key = "Antalya Province, Turkey", placetype = "province"}}, -- 1.3 million; 1,400,000 (Agglomeration)
["Konya"] = {container = {key = "Konya Province, Turkey", placetype = "province"}}, -- 1.35 million; 1,390,000 (Agglomeration)
["Diyarbakır"] = {container = {key = "Diyarbakır Province, Turkey", placetype = "province"}}, -- 1.07 million; 1,100,000 (Agglomeration)
-- Diyarbakır is more common per Ngrams and Google Scholar, but Diyarbakir is the Kurdish form, so we should not
-- display-canonicalize to the Turkish form Diyarbakır.
["Diyarbakir"] = {alias_of = "Diyarbakır"},
["Mersin"] = {container = {key = "Mersin Province, Turkey", placetype = "province"}}, -- 1.03 million; 1,060,000 (Agglomeration)
["Ashgabat"] = {container = "Turkmenistan"}, -- 1,150,000 (Agglomeration)
["Dubai"] = {container = "United Arab Emirates"}, -- 6,050,000 (Agglomeration; including Sharjah)
["Abu Dhabi"] = {container = "United Arab Emirates"}, -- 1,850,000 (City)
["Sharjah"] = {container = "United Arab Emirates"}, -- 1,800,000 (Metro area 2022-2023 per Wikipedia; separate from Dubai)
["Tashkent"] = {container = "Uzbekistan"}, -- 3,850,000 (unindicated)
["Sanaa"] = {container = "Yemen"}, -- 3,275,000 (City; population of low reliability)
["Sana'a"] = {alias_of = "Sanaa", display = true},
["Aden"] = {container = "Yemen"}, -- 1,079,060 (?; 2023 estimate from World Population Review per Wikipedia)
------------------ Europe or Europe-like (Caucasus etc.) ---------------------
["Yerevan"] = {container = "Armenia"}, -- 1,520,000 (Agglomeration)
["Vienna"] = {container = "Austria"}, -- 2,375,000 (Agglomeration)
["Minsk"] = {container = "Belarus"}, -- 2,100,000 (unindicated)
["Brussels"] = {container = "Belgium"}, -- 2,800,000 (Consolidated Urban Area)
["Antwerp"] = {container = "Belgium"}, -- 1,270,000 (Consolidated Urban Area)
["Sofia"] = {container = "Bulgaria"}, -- 1,260,000 (Agglomeration)
["Zagreb"] = {container = "Croatia"},
["Prague"] = {container = "Czech Republic"}, -- 1,470,000 (Agglomeration)
["Brno"] = {container = "Czech Republic"}, -- 729,405 (metro area per Wikipedia as of 2024-01-01 Czech Statistical Office)
["Olomouc"] = {container = "Czech Republic"}, -- 102,293 (city; included only because someone went crazy creating Olomouc-related terms)
["Copenhagen"] = {container = "Denmark"}, -- 1,800,000 (Consolidated Urban Area)
["Helsinki"] = {container = {key = "Uusimaa, Finland", placetype = "region"}}, -- 1,560,000 (Consolidated Urban Area)
["Tbilisi"] = {container = "Georgia"}, -- 1,430,000 (Agglomeration)
["Athens"] = {container = "Greece"},
["Thessaloniki"] = {container = "Greece"},
["Budapest"] = {container = "Hungary"},
-- FIXME, per Wikipedia "County Dublin" is now the "Dublin Region"
["Dublin"] = {container = {key = "County Dublin, Ireland", placetype = "county"}},
["Riga"] = {container = "Latvia"},
["Amsterdam"] = {container = {key = "North Holland, Netherlands", placetype = "province"}},
["Rotterdam"] = {container = {key = "South Holland, Netherlands", placetype = "province"}},
["The Hague"] = {container = {key = "South Holland, Netherlands", placetype = "province"}},
-- Christchurch (metro 546,600) and Wellington (metro 439,800) are too small to make it.
["Auckland"] = {container = {key = "Auckland, New Zealand", placetype = "region"}},
["Oslo"] = {container = {key = "Oslo, Norway", placetype = "county"}},
["Warsaw"] = {container = {key = "Masovian Voivodeship, Poland", placetype = "voivodeship"}},
["Katowice"] = {container = {key = "Silesian Voivodeship, Poland", placetype = "voivodeship"}},
--- Ngrams (up through 2022) and Google Scholar (>= 2024) confirms the common form "Krakow" without accent.
["Krakow"] = {container = {key = "Lesser Poland Voivodeship, Poland", placetype = "voivodeship"}, wp = "Kraków"},
["Kraków"] = {alias_of = "Krakow", display = true},
["Cracow"] = {alias_of = "Krakow", display = true},
--- Ngrams (up through 2022) and Google Scholar (>= 2024) confirm "Gdańsk" and "Poznań" with accent.
["Gdańsk"] = {container = {key = "Pomeranian Voivodeship, Poland", placetype = "voivodeship"}},
["Gdansk"] = {alias_of = "Gdańsk", display = true},
["Poznań"] = {container = {key = "Greater Poland Voivodeship, Poland", placetype = "voivodeship"}},
["Poznan"] = {alias_of = "Poznań", display = true},
--- Ngrams (up through 2022) and Google Scholar (>= 2024) confirms the common form "Lodz" without accents.
["Lodz"] = {container = {key = "Lodz Voivodeship, Poland", placetype = "voivodeship"}, wp = "Łódź"},
["Łódź"] = {alias_of = "Lodz", display = true},
["Lisbon"] = {container = {key = "Lisbon District, Portugal", placetype = "district"}},
["Porto"] = {container = {key = "Porto District, Portugal", placetype = "district"}},
["Oporto"] = {alias_of = "Porto", display = true},
["Bucharest"] = {container = "Romania"},
["Belgrade"] = {container = "Serbia"},
["Stockholm"] = {container = "Sweden"},
["Zurich"] = {container = "Switzerland"},
--- Ngrams (up through 2022) and Google Scholar (>= 2024) confirms the common form "Zurich" without umlaut.
--- Even Wikipedia uses the form without umlaut.
["Zürich"] = {alias_of = "Zurich", display = true},
["Kyiv"] = {container = "Ukraine"}, -- not in Kyiv Oblast
-- Don't display-canonicalize Kiev -> Kyiv because in ancient contexts, Kiev is still more common.
["Kiev"] = {alias_of = "Kyiv"},
["Kharkiv"] = {container = {key = "Kharkiv Oblast, Ukraine", placetype = "oblast"}},
["Odessa"] = {container = {key = "Odesa Oblast, Ukraine", placetype = "oblast"}, wp = "Odesa"},
-- Don't display-canonicalize Odesa -> Odessa because it may be interpreted as a political statement.
["Odesa"] = {alias_of = "Odessa"},
------------------ North America, South America ---------------------
-- Primary figures from citypopulation.de retrieved on 2025-04-26 (reference date 2025-01-01);
-- Wikipedia metropolitan figures from [[w:List of metropolitan areas in the Americas]] based on per-country data;
-- Wikipedia city limits figures from [[w:List of largest cities in the Americas]].
["Buenos Aires"] = {container = "Argentina"}, -- 16,800,000 (Consolidated Urban Area; 13,985,794 metropolitan area per Wikipedia)
["Córdoba, Argentina"] = {container = "Argentina", wp = "%l, %c"}, -- 1,810,000 (Consolidated Urban Area; 1,505,25 city limits per Wikipedia)
-- to avoid confusion with Córdoba in Spain
["Córdoba"] = {alias_of = "Córdoba, Argentina"},
["Cordoba"] = {alias_of = "Córdoba, Argentina", display = "Córdoba"},
["Rosario"] = {container = "Argentina", wp = "%l, Santa Fe"}, -- 1,510,000 (Consolidated Urban Area; 1,348,725 metropolitan area per Wikipedia)
["Mendoza"] = {container = "Argentina", wp = "%l, %c"}, -- 1,180,000 (Consolidated Urban Area)
["San Miguel de Tucumán"] = {container = "Argentina"}, -- 1,110,000 (Consolidated Urban Area)
["Tucumán"] = {alias_of = "San Miguel de Tucumán"},
["Tucuman"] = {alias_of = "San Miguel de Tucumán", display = "Tucumán"},
["Santa Cruz de la Sierra"] = {container = "Bolivia"}, -- 1,960,000 (Consolidated Urban Area); 1,606,671 (city limits per Wikipedia)
["Santa Cruz"] = {alias_of = "Santa Cruz de la Sierra"},
["La Paz"] = {container = "Bolivia"}, -- 1,870,000 (Consolidated Urban Area; composed of El Alto, now slightly larger, and La Paz)
["El Alto"] = {container = "Bolivia"},
["Cochabamba"] = {container = "Bolivia"}, -- 1,280,000 (Consolidated Urban Area)
["Santiago"] = {container = "Chile"}, -- 8,400,000 (Consolidated Urban Area; 6,903,479 city limits? per Wikipedia)
["Valparaíso"] = {container = "Chile"}, -- 1,060,000 (Consolidated Urban Area)
["Valparaiso"] = {alias_of = "Valparaíso"}, -- 1,060,000 (Consolidated Urban Area)
["Bogotá"] = {container = "Colombia"}, -- 10,600,000 (Agglomeration; 12,772,828 metropolitan area per Wikipedia)
["Bogota"] = {alias_of = "Bogotá", display = true},
["Medellín"] = {container = "Colombia"}, -- 4,350,000 (Agglomeration; 4,068,000 metropolitan area per Wikipedia)
["Medellin"] = {alias_of = "Medellín", display = true},
["Cali"] = {container = "Colombia"}, -- 2,975,000 (Agglomeration; 2,837,000 metropolitan area per Wikipedia)
["Barranquilla"] = {container = "Colombia"}, -- 2,375,000 (Agglomeration; 1,341,160 city limits per Wikipedia)
["Bucaramanga"] = {container = "Colombia"}, -- 1,380,000 (Agglomeration)
["Cartagena, Colombia"] = {container = "Colombia", wp = "%l, %c"}, -- 1,250,000 (Agglomeration)
-- to avoid confusion with Cartagena, Spain
["Cartagena"] = {alias_of = "Cartagena, Colombia"},
["Cúcuta"] = {container = "Colombia"}, -- 1,130,000 (Agglomeration)
["Cucuta"] = {alias_of = "Cúcuta", display = true},
-- to avoid conflict with San Jose, California
["San José, Costa Rica"] = {container = "Costa Rica", wp = "%l, %c"}, -- 2,450,000 (Municipality (urban population); 3,160,000 metropolitan area per Wikipedia)
["San José"] = {alias_of = "San José, Costa Rica"},
["San Jose"] = {alias_of = "San José, Costa Rica"}, -- display = "San José"; causes error due to San Jose alias for California city; FIXME
["Havana"] = {container = "Cuba"}, -- 2,150,000 (City; 2,137,847 city limits? per Wikipedia)
["Santo Domingo"] = {container = "Dominican Republic"}, -- 3,900,000 (Municipality (urban population); 4,274,651 ??? per Wikipedia)
["Guayaquil"] = {container = "Ecuador"}, -- 3,350,000 (Agglomeration; 3,092,000 metro area? per Wikipedia)
["Quito"] = {container = "Ecuador"}, -- 2,875,000 (Agglomeration; 2,889,703 metro area? per Wikipedia)
["San Salvador"] = {container = "El Salvador"}, -- 1,580,000 (Municipality (urban population))
["Guatemala City"] = {container = "Guatemala"}, -- 3,375,000 (Municipality (urban population); 3,160,000 metro area? per Wikipedia)
["Port-au-Prince"] = {container = "Haiti"}, -- 3,050,000 (Agglomeration; population of low reliability; 2,915,000 metro area? per Wikipedia)
["San Pedro Sula"] = {container = "Honduras"}, -- 1,330,000 (Consolidated Urban Area)
["Tegucigalpa"] = {container = "Honduras"}, -- 1,220,000 (Urban Area)
["Managua"] = {container = "Nicaragua"}, -- 1,400,000 (Consolidated Urban Area)
["Panama City"] = {container = "Panama"}, -- 1,430,000 (Urban Area)
["Asunción"] = {container = "Paraguay"}, -- 2,350,000 (Municipality (urban population))
["Lima"] = {container = "Peru"}, -- 12,000,000 (Agglomeration; 11,283,787 ??? per Wikipedia)
["Arequipa"] = {container = "Peru"}, -- 1,210,000 (Agglomeration)
["San Juan"] = {container = {key = "Puerto Rico", placetype = "commonwealth"}, wp = "%l, %c"}, -- 1,910,000 (Consolidated Urban Area)
["Montevideo"] = {container = "Uruguay"}, -- 1,810,000 (Agglomeration; 1,302,954 ??? per Wikipedia)
["Caracas"] = {container = "Venezuela"}, -- 3,850,000 (Consolidated Urban Area; 5,243,301 ??? per Wikipedia)
["Maracaibo"] = {container = "Venezuela"}, -- 2,825,000 (Consolidated Urban Area; 5,278,448 ??? per Wikipedia)
-- to avoid confusion with Valencia (city and autonomous community of Spain)
["Valencia, Venezuela"] = {container = "Venezuela", wp = "%l, %c"}, -- 2,100,000 (Consolidated Urban Area)
["Valencia"] = {alias_of = "Valencia, Venezuela"},
["Maracay"] = {container = "Venezuela"}, -- 1,480,000 (Consolidated Urban Area)
["Barquisimeto"] = {container = "Venezuela"}, -- 1,360,000 (Consolidated Urban Area)
}
export.misc_cities_group = {
canonicalize_key_container = make_canonicalize_key_container(nil, "country"),
default_placetype = "city",
data = export.misc_cities,
}
--[==[ var:
List of all known locations, in groups. The first group lists continents and continental regions, followed by three
groups listing top-level locations: countries, "country-like entities" (de-facto/unrecognized/etc. countries and
dependent territories) and former polities (countries, empires, etc.). After that come first-level subpolities
(administrative divisions) of several, mostly large, countries, followed by groups of cities. China and the United
Kingdom include second-level subpolities (in the case of China, only the largest ones as the full list runs in the
hundreds).
]==]
export.locations = {
export.continents_group,
export.countries_group,
export.country_like_entities_group,
export.former_countries_group,
export.australia_group,
export.austria_group,
export.bangladesh_group,
export.brazil_group,
export.bulgaria_group,
export.canada_group,
export.china_group,
export.china_prefecture_level_cities_group,
export.china_prefecture_level_cities_group_2,
export.egypt_group,
export.finland_group,
export.france_group,
export.france_departments_group,
export.germany_group,
export.greece_group,
export.india_group,
export.indonesia_group,
export.iran_group,
export.ireland_group,
export.italy_group,
export.japan_group,
export.laos_group,
export.lebanon_group,
export.malaysia_group,
export.malta_group,
export.mexico_group,
export.moldova_group,
export.morocco_group,
export.netherlands_group,
export.new_zealand_group,
export.nigeria_group,
export.north_korea_group,
export.norway_group,
export.pakistan_group,
export.philippines_group,
export.poland_group,
export.portugal_group,
export.romania_group,
export.russia_group,
export.saudi_arabia_group,
export.south_africa_group,
export.south_korea_group,
export.spain_group,
export.taiwan_group,
export.thailand_group,
export.turkey_group,
export.ukraine_group,
export.united_kingdom_group,
export.united_states_group,
export.england_group,
export.northern_ireland_group,
export.scotland_group,
export.wales_group,
export.vietnam_group,
export.australia_cities_group,
export.brazil_cities_group,
export.canada_cities_group,
export.france_cities_group,
export.germany_cities_group,
export.india_cities_group,
export.indonesia_cities_group,
export.italy_cities_group,
export.japan_cities_group,
export.mexico_cities_group,
export.nigeria_cities_group,
export.pakistan_cities_group,
export.philippines_cities_group,
export.russia_cities_group,
export.saudi_arabia_cities_group,
export.south_korea_cities_group,
export.spain_cities_group,
export.taiwan_cities_group,
export.united_kingdom_cities_group,
export.united_states_cities_group,
export.new_york_boroughs_group,
export.vietnam_cities_group,
export.misc_cities_group,
}
return export
dws9ii25xu6ehccq914gw62b8c7d56n
Mòideal:place/placetypes
828
17897
88792
2026-07-31T01:41:42Z
en>Polomo
0
link separately to "state capital"
88792
Scribunto
text/plain
local export = {}
export.force_cat = false -- set to true for testing
local m_locations = require("Module:place/locations")
local m_links = require("Module:links")
local m_table = require("Module:table")
local m_strutils = require("Module:string utilities")
local debug_track_module = "Module:debug/track"
local en_utilities_module = "Module:en-utilities"
local dump = mw.dumpObject
local insert = table.insert
local concat = table.concat
local internal_error = m_locations.internal_error
export.internal_error = internal_error
local process_error = m_locations.process_error
export.process_error = process_error
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
local ucfirst = m_strutils.ucfirst
local ulower = m_strutils.lower
local rmatch = m_strutils.match
local split = m_strutils.split
--[==[ intro:
This module contains placetype data used by [[Module:place]] and {{tl|place}}, along with a significant amount of code
to work with both placetypes and locations, as well as some placename-related info (FIXME: Consider moving it to
[[Module:place/locations]]). See also [[Module:place/locations]], which has definitions of all known locations. You must
currently load this module using {{cd|require()}}, not using {{cd|mw.loadData()}}.
In particular, it contains two fundamental and tricky functions:
# `get_placetype_equivs`, which finds the equivalent placetypes to look under in order to find a given property, and in
the process correctly handles placetypes with qualifiers (including qualifiers that act similar to "type-raising"
operators in that they do something non-trivial to the placetype to their right) as well as form-of directives and
fallbacks.
# `find_matching_holonym_location`, which looks up a holonym to find a matching known location, but in the process
checks holonyms to the right to make sure there isn't a clash between the user-specified containing holonyms and the
containers of the known location being considered. This is done to prevent overcategorizing when either there are two
known locations with the same name (e.g. Birmingham in England and Birmingham, Alabama in the US), or more generally
two locations with the same name, one of which is a known location but where the other is not (e.g. we're processing
non-known-location Mérida, Spain and don't want it categorized like known location Mérida, Yucatán, Mexico).
Both of these functions are invoked repeatedly, and probably are invoked several times on the same inputs and as a
result are candidates for memoization to speed up the operation of {{tl|place}}.
]==]
------------------------------------------------------------------------------------------
-- Basic utilities --
------------------------------------------------------------------------------------------
--[==[
Return true if `force_cat` is set either in this module or in [[Module:place/locations]].
]==]
function export.get_force_cat()
return export.force_cat or m_locations.force_cat
end
-- Add the page to a tracking "category". To see the pages in the "category",
-- go to [[Wiktionary:Tracking/place/PAGE]] and click on "What links here".
local function track(page)
require(debug_track_module)("place/" .. page)
return true
end
function export.remove_links_and_html(text)
text = m_links.remove_links(text)
return text:gsub("<.->", "")
end
--[==[
Return the singular version of a maybe-plural placetype, or nil if not plural. This correctly handles placetypes with
irregular plurals such as `kibbutzim` plural of `kibbutz` by looking up in a table constructed from the `plural` values
specified in `placetype_data`. If a special plural value is not found, the regular singularization algorithm in
[[Module:en-utilities]] is invoked, which reverses the y -> ies change after vowels and the 'es' addition after sh/ch/x,
and otherwise just subtracts a final 's' (which will incorrectly generate 'passe' for plural 'passes'; FIXME: consider
changing this for words ending in '-sses'). If the generated singular is the same as the passed-in value, nil is
returned.
]==]
function export.maybe_singularize_placetype(placetype)
if not placetype then
return nil
end
if export.plural_placetype_to_singular[placetype] then
return export.plural_placetype_to_singular[placetype]
end
local retval = require(en_utilities_module).singularize(placetype)
if retval == placetype then
return nil
end
return retval
end
-- Return the correct plural of a placetype, and (if `do_ucfirst` is given) make the first letter uppercase. We first
-- look up the plural in `placetype_data`, falling back to pluralize() in [[Module:en-utilities]], which is almost
-- always correct.
function export.pluralize_placetype(placetype, do_ucfirst)
local ptdata = export.placetype_data[placetype]
if ptdata and ptdata.plural then
placetype = ptdata.plural
else
placetype = require(en_utilities_module).pluralize(placetype)
end
if do_ucfirst then
return ucfirst(placetype)
else
return placetype
end
end
--[==[
Get the data associated with a placetype, which may be in its singular or plural form. If `from_category` is specified,
we also look for category-only placetypes (generally plural) followed by `!`. Return three values: (a) the placetype
under which the data can be looked up (i.e. in its singular form if the passed-in `placetype` is plural and did not
match a category-only placetype followed by `!`); (b) the placetype data structure; (c) the type of `placetype` match
that occurred, one of `"direct"` if the canonical placetype is the same as the passed-in `placetype` and also the same
as the key under which `ptdata` was looked up, or `"direct-category"` if the `ptdata` was looked up under a key formed
from the passed-in `placetype` by adding `!`, or `"plural"` if the `ptdata` was looked up under the singularized version
of the plural passed-in `placetype`.
]==]
function export.get_placetype_data(placetype, from_category)
local ptdata = export.placetype_data[placetype]
if ptdata then
return placetype, ptdata, "direct"
end
if from_category then
ptdata = export.placetype_data[placetype .. "!"]
if ptdata then
return placetype .. "!", ptdata, "direct-category"
end
end
local sg_placetype = export.maybe_singularize_placetype(placetype)
if sg_placetype then
ptdata = export.placetype_data[sg_placetype]
if ptdata then
return sg_placetype, ptdata, "plural"
end
end
return nil
end
--[==[
Check for special pseudo-placetypes that should be ignored for categorization purposes.
]==]
function export.placetype_is_ignorable(placetype)
return placetype == "and" or placetype == "or" or placetype:find("^%(")
end
function export.resolve_placetype_aliases(placetype)
return export.placetype_aliases[placetype] or placetype
end
--[==[
Return a property from `placetype_data` for a given placetype. If the placetype isn't found in `placetype_data`, or the
key isn't found in the placetype's entry in `placetype_data`, return nil.
]==]
function export.get_placetype_prop(placetype, key)
-- Usually we are called on equivalent placetypes returned from `get_placetype_equivs`, in which case placetype
-- aliases have been resolved, but sometimes not, e.g. when fetching the indefinite article in
-- get_placetype_article(). `resolve_placetype_aliases` is just a simple lookup and it doesn't hurt to do it twice.
placetype = export.resolve_placetype_aliases(placetype)
if export.placetype_data[placetype] then
return export.placetype_data[placetype][key]
else
return nil
end
end
--[==[
Given a placetype, split the placetype into one or more potential ''splits'', each consisting of a three-element list
{ {``prev_qualifiers``, ``this_qualifier``, ``reduced_placetype``}}, i.e.
# the concatenation of zero or more previously-recognized qualifiers on the left, normally canonicalized (if there are
zero such qualifiers, the value will be nil);
# a single recognized qualifier, normally canonicalized (if there is no qualifier, the value will be nil);
# the "reduced placetype" on the right.
Splitting between the qualifier in (2) and the reduced placetype in (3) happens at each space character, proceeding from
left to right, and stops if a qualifier isn't recognized. All placetypes are canonicalized by checking for aliases
in `placetype_aliases`, but no other checks are made as to whether the reduced placetype is recognized. Canonicalization
of qualifiers does not happen if `no_canon_qualifiers` is specified.
For example, given the placetype `"small beachside unincorporated community"`, the return value will be
{ {
{nil, nil, "small beachside unincorporated community"},
{nil, "small", "beachside unincorporated community"},
{"small", "[[beachfront]]", "unincorporated community"},
{"small [[beachfront]]", "[[unincorporated]]", "community"},
}}
Here, `"beachside"` is canonicalized to `"[[beachfront]]"` and `"unincorporated"` is canonicalized to
`"[[unincorporated]]"`, in both cases according to the entry in `placetype_qualifiers`.
On the other hand, if given `"small former haunted community"`, the return value will be
{ {
{nil, nil, "small former haunted community"},
{nil, "small", "former haunted community"},
{"small", "former", "haunted community"},
}}
because `"small"` and `"former"` but not `"haunted"` are recognized as qualifiers.
Finally, if given `"former adr"`, the return value will be
{ {
{nil, nil, "former adr"},
{nil, "former", "administrative region"},
}}
because `"adr"` is a recognized placetype alias for `"administrative region"`.
]==]
function export.split_qualifiers_from_placetype(placetype, no_canon_qualifiers)
local splits = {{nil, nil, export.resolve_placetype_aliases(placetype)}}
local prev_qualifier = nil
while true do
local qualifier, reduced_placetype = placetype:match("^(.-) (.*)$")
if qualifier then
local canon = export.placetype_qualifiers[qualifier]
if canon == nil then
break
end
local new_qualifier = qualifier
if type(canon) == "table" then
canon = canon.link
end
if not no_canon_qualifiers and canon ~= false then
if canon == true then
new_qualifier = "[[" .. qualifier .. "]]"
else
new_qualifier = canon
end
end
insert(splits, {prev_qualifier, new_qualifier, export.resolve_placetype_aliases(reduced_placetype)})
prev_qualifier = prev_qualifier and prev_qualifier .. " " .. new_qualifier or new_qualifier
placetype = reduced_placetype
else
break
end
end
return splits
end
--[==[
Given a `placetype` (which may be pluralized), return an ordered list of equivalent placetypes to look under to find the
placetype's properties (such as the category or categories to be inserted). The return value is actually an ordered list
of objects of the form `{qualifier=``qualifier``, placetype=``equiv_placetype``}` where ``equiv_placetype`` is a
placetype whose properties to look up, derived from the passed-in placetype or from a contiguous subsequence of the
words in the passed-in placetype (always including the rightmost word in the placetype, i.e. we successively chop off
qualifier words from the left and use the remainder to find equivalent placetypes). ``qualifier`` is the remaining words
not part of the subsequence used to find ``equiv_placetype``; or nil if all words in the passed-in placetype were used
to find ``equiv_placetype``. (FIXME: This qualifier is not currently used anywhere.) Only placetypes for which there is
an entry in `placetype_data` are included. The placetype passed in is always checked first, and will form the first
entry if it exists in `placetype_data`.
'''NOTE:''' This is a tricky function as it implements handling of (a) qualifiers, (b) fallback logic, (c)
"type-raising" qualifiers such as `former`/`ancient`/etc. as well as `fictional` and `mythological`, and (d) form-of
directives, which act somewhat similarly to `former`, and allows interaction between more than one of these
simultaneously (e.g. official names of former places, which have their own categorization).
If {{tl|place}} gets too slow, one potential speedup is to memoize the results of this function, as it appears to be
getting called more than once on the same inputs. Another similar potential speedup is to memoize the results of
`iterate_matching_holonym_location()`.
For example, given the placetype `left tributary`, the following placetype/qualifier combinations are checked in turn:
```
{qualifier = nil, placetype="left tributary"}
{qualifier = "left", placetype="tributary"}
{qualifier = "left", placetype="river"}
```
and the return value will be
{ {
{qualifier = "left", placetype="tributary"},
{qualifier = "left", placetype="river"},
}}
The algorithm first enters the placetype itself into the list, then checks for `left tributary` as a recognized
placetype in `placetype_data` and doesn't find it, so it doesn't enter it into the returned list (if it found it, it
would add it as well as any fallbacks directly after it). It then splits off the recognized qualifier `left` to form the
''reduced placetype'' `tributary`, which is entered into the list because it is found in `placetype_data`. Then, because
it has a fallback `river`, which exists in `placetype_data`, the fallback is entered next.
Another example is `small rural fraziones` (where a ''frazione'' is type of subdivision of a ''comune'' or municipality,
often specifically an outlying hamlet). the placetype/qualifier combinations checked are:
```
{qualifier = nil, placetype="small rural fraziones"}
{qualifier = nil, placetype="small rural frazione"}
{qualifier = "small", placetype="rural fraziones"}
{qualifier = "small", placetype="rural frazione"}
{qualifier = "small [[rural]]", placetype="fraziones"}
{qualifier = "small [[rural]]", placetype="frazione"}
{qualifier = "small [[rural]]", placetype="hamlet"}
{qualifier = "small [[rural]]", placetype="village"}
```
The return value ends up as
{qualifier = "small [[rural]]", placetype="frazione"},
{qualifier = "small [[rural]]", placetype="hamlet"},
{qualifier = "small [[rural]]", placetype="village"},
}}
Here, because the result of singularizing `fraziones` returns a different value from the placetype itself, that
singularized value is checked after the original plural value. Also, in the process of splitting off qualifiers,
they are canonicalized if the entry in `placetype_qualifiers` says to do so; in this case, links are placed around
`rural`. Finally, `frazione` has `hamlet` as its fallback, which in turn has `village` as its fallback, so both
fallbacks end up being returned.
`no_fallback`, if set, disables returning equivalent placetypes based on the `fallback` setting for a placetype. This is
used in the first of two loops in find_placetype_cat_specs() in [[Module:place]] to prefer exact matches for placetypes
such as barangays with later holonyms to matches based on a fallback such as `neighborhood` with an earlier holonym.
See the comment in that function in [[Module:place]] for a more detailed explanation of why this is needed. Only the
placetype itself, and any reduced placetypes created by chopping off recognized qualifiers at the beginning, are
returned; but we do not return reduced placetypes if a containing placetype exists in `placetype_data`. (For example,
`"overseas territory"` has a fallback `"dependent territory"`, and `"overseas"` is also a recognized qualifier. When
`no_fallback` is in place, without the above proviso, we would return `"overseas territory"` followed by `"territory"`
with the incorrect effect of classifying an `"overseas territory"` of the United Kingdom such as `"Gibraltar"` under
[[:Category:Territories of the United Kingdom]] instead of [[:Category:Dependent territories of the United Kingdom]].)
As an exception, if `historical`, `ancient`, `former` or the like are found, they proceed ignoring `no_fallback`,
because it seems tricky to handle them correctly in the presence of `no_fallback`, and historical/former placetypes
rarely occur with exact match category specs anyway.
`no_split_qualifiers` prevents splitting off recognized qualifiers and returning the remainder of the placetype as an
equivalent placetype. Only the passed-in placetype, and any fallbacks, will be returned. This is used in
[[Module:category tree/topic cat/data/Places]] when looking up placetypes found in categories. Such placetypes won't
have qualifiers and so it doesn't make sense to try and look for them.
`from_category`, if set, causes category-only placetypes (those ending in `!`) to also be checked.
`form_of_directive`, if set, causes the specified form-of directive (e.g. `FORMER_NAME_OF`) to be prepended to checked
placetypes, their directive-specific type (e.g. `FORMER_NAME_OF_type`), and their classes (`class`) to get the
appropriate placetypes to check for form-of-directive categories. It falls back to the prepended generic `place` as a
placetype, e.g. `FORMER_NAME_OF place`, if nothing else matches.
`no_check_for_inherently_former` is used internally to prevent an infinite loop when checking for `inherently_former`.
`register_former_as_non_former` is a major hack used in `get_bare_categories` to deal with the mismatch between e.g.
known location `Yugoslavia` declaring itself a `country` but definitions of it declaring it a `former country`. It
causes the non-former version of the specified placetype to be included in the returned equivalents along with the
former placetypes. [FIXME: This should apply only to the entries in `former_countries` but it's tricky to do that now;
fix this in the known-location refactor. -- The known-location refactor is already done but we haven't yet fixed this.]
]==]
function export.get_placetype_equivs(placetype, props)
local no_fallback, no_split_qualifiers, no_check_for_inherently_former, from_category, register_former_as_non_former
local form_of_directive
if props then
no_fallback, no_split_qualifiers, no_check_for_inherently_former, from_category, register_former_as_non_former =
props.no_fallback, props.no_split_qualifiers, props.no_check_for_inherently_former, props.from_category,
props.register_former_as_non_former
form_of_directive = props.form_of_directive
end
local equivs = {}
-- Insert `placetype` into `equivs`, along with any fallback placetypes listed in `placetype_data`. `qualifier` is
-- the preceding qualifier to insert into `equivs` along with the placetype (see comment at top of function). If
-- `from_category` is given, we also check for a category-specific entry consisting of the placetype followed by
-- `!`, and in all cases we also check to see if `placetype` is plural, and if so, insert the singularized version
-- along with its fallbacks (if any) in `placetype_data`. `form_of_prefix` is a form-of prefix such as
-- `OFFICIAL_NAME_OF`. If specified, we check the fallbacks of `placetype` without the prefix but then insert into
-- `equivs` the prefixed placetype. This way, if the user says e.g. {{tl|place|pt|@official name of:Cuba|island country|r/Caribbean}},
-- we will correctly categorize into [[:Category:Official names of countries]], rather than only trying to look up
-- `OFFICIAL_NAME_OF island country` and failing, falling back ultimately to [[:Category:Official names of places]].
local function insert_placetype_and_fallbacks(qualifier, placetype, form_of_prefix)
local function insert_equiv(pt)
if form_of_prefix then
-- Let's say the user says {{tl|place|pt|@official name of:Cuba|island country|r/Caribbean}} and we have
-- no entry for `OFFICIAL_NAME_OF island country` but we do for `OFFICIAL_NAME_OF country` (which we end
-- up processing because `island country` falls back to `country`), and that entry in turn is defined
-- using a fallback. We have to insert that fallback-of-fallback, and the easiest/cleanest way of
-- handling this is by calling ourselves recursively.
insert_placetype_and_fallbacks(qualifier, form_of_prefix .. " " .. pt)
else
insert(equivs, {qualifier=qualifier, placetype=pt})
end
end
-- Insert the placetype, along with any fallbacks.
local canon_placetype, ptdata, ptmatch = export.get_placetype_data(placetype, from_category)
if ptdata then
insert_equiv(canon_placetype)
if no_fallback then
return
end
local first_placetype = #equivs + 1
local prev_placetype = nil
while true do
local pt_value = export.placetype_data[canon_placetype]
if not pt_value then
internal_error("Fallback value %s specified for placetype %s but is not in `placetype_data`",
canon_placetype, prev_placetype)
end
if pt_value.fallback then
insert_equiv(pt_value.fallback)
local last_placetype = #equivs
if last_placetype - first_placetype >= 10 then
local fallback_loop = {}
for i = first_placetype, last_placetype do
insert(fallback_loop, equivs[i].placetype)
end
internal_error("Apparent loop in fallback chain: %s", table.concat(fallback_loop, " -> "))
end
prev_placetype = canon_placetype
canon_placetype = pt_value.fallback
else
break
end
end
end
end
-- Insert `placetype` into `equivs`, along with any fallback placetypes listed in `placetype_data`. This is a
-- wrapper around the more basic `insert_placetype_and_fallbacks()` which handles form-of directives. If there is no
-- form-of directive, this function directly calls `insert_placetype_and_fallbacks()`. We do things this way so that
-- form-of directives correctly combine with `former`-type qualifiers. Note that we also have special backups for
-- form-of directives that check `DIRECTIVE place` (and before that, `DIRECTIVE FORMER/ANCIENT place` is there's a
-- `former`-type directive); these backups live outside this function because we want them done once, late, rather
-- than in each invocation of `process_and_insert_placetype()`.
local function process_and_insert_placetype(qualifier, reduced_placetype)
if form_of_directive then
-- First check for e.g. `OFFICIAL_NAME_OF island country` and its fallbacks; then we look for fallbacks of
-- `island country` and check e.g. `OFFICIAL_NAME_OF country` and its fallbacks. All of this is handled by
-- `insert_placetype_and_fallbacks()` with appropriate parameters. After that, check the general class of
-- the directive, e.g. `subpolity` if something like `district` is given. (Eventually, we check for
-- `OFFICIAL_NAME_OF place` as a backup, but this happens at the end outside the loop over qualifiers.)
insert_placetype_and_fallbacks(qualifier, reduced_placetype, form_of_directive)
if not no_fallback then
local reduced_placetype_equivs = export.get_placetype_equivs(reduced_placetype)
local directive_type = export.get_equiv_placetype_prop_from_equivs(reduced_placetype_equivs,
function(pt) return export.get_placetype_prop(pt, form_of_directive .. "_type") or
export.get_placetype_prop(pt, "class") end
)
if not directive_type then
local pt_data = export.get_equiv_placetype_prop_from_equivs(reduced_placetype_equivs,
function(pt) return export.placetype_data[pt] end
)
if pt_data then
internal_error("For placetype %s in conjunction with form-of directive %s, placetype data " ..
'located but directive-specific type property %s missing, and so is "class"; ' ..
"placetypes searched are %s", reduced_placetype, form_of_directive,
form_of_directive .. "_type", reduced_placetype_equivs)
else
-- This should be allowed, as we allow unrecognized placetypes in general.
end
elseif directive_type ~= "!" then
insert_placetype_and_fallbacks(qualifier, directive_type, form_of_directive)
end
end
else
insert_placetype_and_fallbacks(qualifier, reduced_placetype)
end
end
-- Successively split off recognized qualifiers and loop over successively greater sets of qualifiers from the left
-- (unless `no_split_qualifiers` is specified, in which case we don't check for qualifiers).
local splits
if no_split_qualifiers then
splits = {{nil, nil, export.resolve_placetype_aliases(placetype)}}
else
splits = export.split_qualifiers_from_placetype(placetype)
end
for _, split in ipairs(splits) do
local prev_qualifier, this_qualifier, reduced_placetype = unpack(split, 1, 3)
-- If a special "former" qualifier like `former` or `historical` isn't present, and
-- `no_check_for_inherently_former` is not given (this flag is used to avoid infinite loops), check for
-- "inherently former" placetypes like `satrapy` and `treaty port` that always refer to no-longer-existing
-- placetypes, and handle accordingly.
local unlinked_this_qualifier
if this_qualifier and this_qualifier:find("%[") then
unlinked_this_qualifier = export.remove_links_and_html(this_qualifier)
else
unlinked_this_qualifier = this_qualifier
end
local former_qualifiers = this_qualifier and export.former_qualifiers[unlinked_this_qualifier] or nil
if not former_qualifiers and not no_check_for_inherently_former then
former_qualifiers = export.get_equiv_placetype_prop(reduced_placetype,
function(pt) return export.get_placetype_prop(pt, "inherently_former") end,
{no_check_for_inherently_former = true})
end
-- If a special "former" qualifier like `former` or `historical` is present, map it to the appropriate internal
-- qualifiers (`ANCIENT` and/or `FORMER`, which are written in all-caps to distinguish them from user-specified
-- qualifiers), fetch the `former_type` property, and treat the placetype as if a concatenation of the mapped
-- qualifier(s) and the value of `former_type`. For example, if `medieval village` is given, we map `medieval`
-- to `ANCIENT` and `FORMER`, and `village` to its `former_type` of `settlement`, and enter the placetypes
-- `ANCIENT settlement` and `FORMER settlement` (in that order) into `equivs`. If the placetype following the
-- "former" qualifier is recognized in `placetype_data` but has no `former_type` and no fallback with a
-- `former_type` specified, it is an internal error; but if the placetype isn't recognized (e.g. something like
-- `former greenhouse` is specified and we don't have an entry for `greenhouse`), just track the occurrence and
-- don't enter anything into `equivs`.
if former_qualifiers then
-- FIXME: Should we respect `no_fallback` here? My instinct says no.
local reduced_placetype_equivs = export.get_placetype_equivs(reduced_placetype, {
no_check_for_inherently_former = true
})
local former_type = export.get_equiv_placetype_prop_from_equivs(reduced_placetype_equivs,
function(pt) return export.get_placetype_prop(pt, "former_type") or
export.get_placetype_prop(pt, "class") end
)
if not former_type then
local pt_data = export.get_equiv_placetype_prop_from_equivs(reduced_placetype_equivs,
function(pt) return export.placetype_data[pt] end
)
if pt_data then
internal_error("For placetype %s, placetype data located but `former_type` missing; " ..
"placetypes searched are %s", reduced_placetype, reduced_placetype_equivs)
else
-- Enable error when we've verified there aren't any examples.
track("bad-former-placetype")
track("bad-former-placetype/" .. reduced_placetype)
--process_error("For placetype '%s', unrecognized placetype following 'former'-type " ..
-- "qualifier; searched placetype(s) %s", reduced_placetype, dump(reduced_placetype_equivs))
end
elseif former_type ~= "!" then
-- First check directly for `ANCIENT/FORMER` + the original following placetype. This makes it possible
-- for (e.g.) former provinces of the Roman empire to be categorized specially.
for _, former_qualifier in ipairs(former_qualifiers) do
process_and_insert_placetype(prev_qualifier, former_qualifier .. " " .. reduced_placetype)
end
for _, former_qualifier in ipairs(former_qualifiers) do
process_and_insert_placetype(prev_qualifier, former_qualifier .. " " .. former_type)
end
-- HACK! See explanation above for `register_former_as_non_former`.
if register_former_as_non_former then
process_and_insert_placetype(prev_qualifier, reduced_placetype)
end
-- If we're processing a form-of directive, after doing everything else we do
-- `DIRECTIVE ANCIENT/FORMER place` e.g. `OFFICIAL_NAME_OF FORMER place` as a backup.
if form_of_directive and not no_fallback then
for _, former_qualifier in ipairs(former_qualifiers) do
insert_placetype_and_fallbacks(prev_qualifier, form_of_directive .. " " .. former_qualifier ..
" place")
end
end
-- Don't continue processing equivs. The reason is probably the same as the `break` below for
-- qualifier_to_placetype_equivs[]; categories for `former BLAH` are set using `default`, and
-- non-former equivs will otherwise take precedence.
break
end
end
-- Then see if the rightmost split-off qualifier is in qualifier_to_placetype_equivs
-- (e.g. 'fictional *' -> 'fictional location'). If so, add the mapping.
if this_qualifier and export.qualifier_to_placetype_equivs[unlinked_this_qualifier] then
insert(equivs, {
qualifier=prev_qualifier,
placetype=export.qualifier_to_placetype_equivs[unlinked_this_qualifier]
})
-- Don't continue processing equivs; otherwise, if we specify 'mythological city', even though the
-- equivalent entry for 'mythological location' gets inserted ahead of the entry for 'city', the
-- latter ends up generating the category because the category for 'mythological location' is set as
-- the default value, which is used only when no non-default category can be found.
break
end
-- Finally, join the rightmost split-off qualifier to the previously split-off qualifiers to form a combined
-- qualifier, and add it along with reduced_placetype and any mapping in placetype_data for reduced_placetype.
-- NOTE: The first time through this loop, both `prev_qualifier` and `this_qualifier` are nil, and this inserts
-- the full placetype into `equivs`.
local qualifier = prev_qualifier and prev_qualifier .. " " .. this_qualifier or this_qualifier
process_and_insert_placetype(qualifier, reduced_placetype)
-- If `no_fallback` and there's an entry in `placetype_data` for this placetype, don't include any reduced
-- placetypes to avoid the "overseas territory treated as a territory" issue describe above.
if no_fallback then
local canon_placetype, ptdata, ptmatch = export.get_placetype_data(reduced_placetype, from_category)
if canon_placetype then
break
end
end
end
-- If we're processing a form-of directive, after doing everything else we do `DIRECTIVE place` e.g.
-- `OFFICIAL_NAME_OF place` as a backup; but only if either the placetype as a whole is recognized or the placetype
-- begins with a recognized qualifier. This latter check is to avoid categorizing into e.g.
-- [[Category:en:Former names of places]] in an invocation like
-- {{place|en|@former name of:Democratic Republic of the Congo|country|r/Central Africa|;|used from 1971–1997}};
-- the `used from 1971–1997` gets treated as a placetype and we're called on it.
if form_of_directive and not no_fallback and (splits[2] or export.get_placetype_data(placetype, from_category)) then
insert_placetype_and_fallbacks(nil, form_of_directive .. " place")
end
return equivs
end
function export.get_equiv_placetype_prop_from_equivs(equivs, fun, continue_on_nil_only)
for _, equiv in ipairs(equivs) do
local retval = fun(equiv.placetype)
if continue_on_nil_only and retval ~= nil or not continue_on_nil_only and retval then
return retval, equiv
end
end
return nil, nil
end
--[==[
Given a placetype `placetype` and a function `fun` of one argument, iteratively call the function on equivalent
placetypes fetched from `get_placetype_equivs` until the function returns a non-falsy value (i.e. not {nil} or {false});
but if `continue_on_nil_only` is specified, the iterations continue until the function returns non non-{nil} value.
FIXME: We should make `continue_on_nil_only` the default; but this requires changing some callers.) When `fun` returns a
non-falsy or non-{nil} value, `get_equiv_placetype_prop` returns two values: the value returned by `fun` and the
equivalent placetype that triggered the non-falsy (or non-{nil}) return value. If `fun` never returns a non-falsy (or
non-{nil}) value, `get_equiv_placetype_prop` returns {nil} for both return values. If `placetype` is passed in as {nil},
the return value is the result of calling `fun` on {nil} (whatever it is) with {nil} for the second return value.
]==]
function export.get_equiv_placetype_prop(placetype, fun, props)
if not placetype then
return fun(nil), nil
end
return export.get_equiv_placetype_prop_from_equivs(export.get_placetype_equivs(placetype, props), fun,
props and props.continue_on_nil_only)
end
--[==[
Return the article that is used with an entry placetype. We proceed as follows:
# See if there is a recognized qualifier at the beginning that specifies an article (including `false` for no article).
This takes precedence over anything else, so that e.g. `various capitals` gets no article rather than "`the"`.
# Then check the placetype or any equivalent placetype for the `entry_placetype_use_the` property, indicating that
`"the"` should be used.
# Otherwise we look to see if the placetype itself (not any equivalents, even those involving deleting a qualifier from
the beginning) has an entry in `placetype_data` that specifies the indefinite article using `entry_placetype_use_the`
(principally for use with placetypes like `union territory`).
# Otherwise, we use [[Module:en-utilities]] to apply the standard algorithm to generate `"an"` for words beginning with
a vowel and `"a"` otherwise.
If `ucfirst` is true, the first letter of the article is made upper-case.
]==]
function export.get_placetype_article(placetype, ucfirst)
local art
local qualifier, reduced_placetype = placetype:match("^(.-) (.*)$")
if qualifier then
local canon = export.placetype_qualifiers[qualifier]
if type(canon) == "table" then
art = canon.article
end
end
if art == false then
return art
end
if art == nil then
local placetype_use_the = export.get_equiv_placetype_prop(placetype,
function(pt) return export.get_placetype_prop(pt, "entry_placetype_use_the") end)
if placetype_use_the then
art = "the"
else
art = export.get_placetype_prop(placetype, "entry_placetype_indefinite_article")
if not art then
art = require(en_utilities_module).get_indefinite_article(placetype)
end
end
end
if ucfirst then
art = m_strutils.ucfirst(art)
end
return art
end
--[==[
Return the preposition that should be used after `placetype` when occurring as an entry placetype or in categories
(e.g. `city >in< France` but `country >of< South America`). The preposition defaults to `"in"` if not specified.
]==]
function export.get_placetype_entry_preposition(placetype)
local pt_prep = export.get_equiv_placetype_prop(placetype,
function(pt) return export.get_placetype_prop(pt, "preposition") end
)
return pt_prep or "in"
end
--[==[
Given a place desc (see top of file) and a holonym object (see top of file), add a key/value into the place desc's
`holonyms_by_placetype` field corresponding to the placetype and placename of the holonym. For example, corresponding
to the holonym "c/Italy", a key "country" with the list value {"Italy"} will be added to the place desc's
`holonyms_by_placetype` field. If there is already a key with that place type, the new placename will be added to the
end of the value's list.
]==]
function export.key_holonym_into_place_desc(place_desc, holonym)
if not holonym.placetype then
return
end
-- Key in equivalent placetypes, so that e.g. `cities/San Francisco` gets keyed under `city`; but don't do
-- fallbacks, as it doesn't seem correct for the "do other holonyms of the same placetype" algorithm to do holonyms
-- of different types just because they have the same fallback.
local equiv_placetypes = export.get_placetype_equivs(holonym.placetype, {no_fallback = true})
local unlinked_placename = holonym.unlinked_placename
for _, equiv in ipairs(equiv_placetypes) do
local placetype = equiv.placetype
if not place_desc.holonyms_by_placetype then
place_desc.holonyms_by_placetype = {}
end
if not place_desc.holonyms_by_placetype[placetype] then
place_desc.holonyms_by_placetype[placetype] = {unlinked_placename}
else
insert(place_desc.holonyms_by_placetype[placetype], unlinked_placename)
end
end
end
--[=[
Construct a formatted link from the raw link spec `link` given the canonical singular placetype `sg_placetype`. If the
placetype was originally plural, `orig_placetype` should contain this plural value; otherwise it should be nil. This
will construct the appropriate type of link that displays as `orig_placetype` (or otherwise `sg_placetype`) but links to
whatever the `link` spec specifies (which may be `sg_placetype`, a Wikipedia article, etc.). `ptdata` is the placetype
data structure for the placetype, and `from_category` indicates that we are generating the description of a category
(otherwise we are generating the display form of an entry placetype).
]=]
local function make_placetype_link(link, sg_placetype, orig_placetype, ptdata, from_category, noerror)
if not from_category and ptdata.disallow_in_entries then
if noerror then
return "[not meant to be specified directly, with warning: " .. ptdata.disallow_in_entries .. "]"
else
process_error("Placetype %s is not meant to be specified directly: " .. ptdata.disallow_in_entries, sg_placetype)
end
end
if link == nil then
internal_error("Placetype data present for placetype %s but no link= setting given", sg_placetype)
elseif link == true then
if orig_placetype then
return ("[[%s|%s]]"):format(sg_placetype, orig_placetype)
else
return ("[[%s]]"):format(sg_placetype)
end
elseif link == false then
process_error("Placetype %s is not meant to be specified directly, but is only for internal use", sg_placetype)
elseif link == "w" then
return ("[[w:%s|%s]]"):format(sg_placetype, orig_placetype or sg_placetype)
elseif link == "separately" then
if orig_placetype then
local sg_words = split(sg_placetype, " ")
local orig_words = split(orig_placetype, " ")
if #sg_words ~= #orig_words then
internal_error("Can't construct 'separately' link for plural placetype %s as original placetype %s " ..
"has different number of words", orig_placetype, sg_placetype)
else
for i = 1, #sg_words do
if sg_words[i] == orig_words[i] then
sg_words[i] = ("[[%s]]"):format(sg_words[i])
else
sg_words[i] = ("[[%s|%s]]"):format(sg_words[i], orig_words[i])
end
end
return concat(sg_words, " ")
end
else
return (sg_placetype:gsub("([^ ]+)", "[[%1]]"))
end
elseif link:find("^%+") then
link = link:sub(2) -- discard initial +
return ("[[%s|%s]]"):format(link, orig_placetype or sg_placetype)
elseif not orig_placetype then
return link
else
return require(en_utilities_module).pluralize(link)
end
end
--[==[
Get the display form of a placetype by looking it up in `placetype_data`. If the placetype is recognized, or is the
plural of a recognized placetype, the corresponding linked display form is returned (with plural placetypes displaying
as plural but linked to the singular form of the placetype). Otherwise, return nil. If we're generating the description
of a category, `category_type` should be set to one of `"top-level"` (for top-level categories like
[[:Category:Neighborhoods]]), `"noncity"` (for non-city categories like [[:Category:Neighborhoods in Illinois, USA]]) or
`"city"` (for city categories like [[:Category:Neighborhoods of Chicago]]). Otherwise, we're generating the description
for use in formatting a {{tl|place}} call, and category-only placetypes ending in `!` will be ignored, along with
special `category_link*` settings. `return_full` is used along with `category_type` and will preferably return the
"full" variant of category link settings, i.e. `full_category_link*`; if they don't exist, the `category_link*` value is
prepended with `"names of"`. `noerror` says to not throw an error when encountering entry placetypes that would be
disallowed.
]==]
function export.get_placetype_display_form(placetype, category_type, return_full, noerror)
local from_category = not not category_type
local canon_placetype, ptdata, ptmatch = export.get_placetype_data(placetype, from_category)
if canon_placetype then
local raw_link
local function is_linked_string(str)
return type(str) == "string" and str:find("%[%[")
end
if category_type then
local fetched_full
local function fetch_maybe_full(prop)
local retval = ptdata["full_" .. prop]
if retval ~= nil then
if return_full then
return retval, true
else
internal_error("Saw full_" .. prop .. "=%s but `return_full` not set, can't handle", retval)
end
end
return ptdata[prop], false
end
local function maybe_prefix(str)
if return_full and not fetched_full then
return "names of " .. str
else
return str
end
end
-- Careful with `false` as possible value.
if category_type == "top-level" then
raw_link, fetched_full = fetch_maybe_full("category_link_top_level")
elseif category_type == "noncity" then
raw_link, fetched_full = fetch_maybe_full("category_link_before_noncity")
elseif category_type == "city" then
raw_link, fetched_full = fetch_maybe_full("category_link_before_city")
else
internal_error('Unrecognized value for `category_type` %s, should be "top-level", "noncity" or "city"',
category_type)
end
if type(raw_link) == "string" then
return maybe_prefix(raw_link), ptdata
elseif raw_link ~= nil then
return raw_link, ptdata
end
raw_link, fetched_full = fetch_maybe_full("category_link")
if raw_link == false then
return raw_link, ptdata
end
if is_linked_string(raw_link) then
return maybe_prefix(raw_link), ptdata
end
if ptmatch == "plural" then
raw_link, fetched_full = fetch_maybe_full("plural_link")
if raw_link == false then
return raw_link, ptdata
end
if is_linked_string(raw_link) then
return maybe_prefix(raw_link), ptdata
end
end
if raw_link == nil then
raw_link, fetched_full = fetch_maybe_full("link")
end
if raw_link == false then
return raw_link, ptdata
end
return maybe_prefix(make_placetype_link(raw_link, canon_placetype,
placetype ~= canon_placetype and placetype or nil, ptdata, from_category, noerror)), ptdata
else
if ptmatch == "plural" then
raw_link = ptdata.plural_link
if raw_link == false then
process_error("Placetype %s cannot appear plural", placetype)
end
if is_linked_string(raw_link) then
return raw_link, ptdata
end
end
if raw_link == nil then
raw_link = ptdata.link
end
return make_placetype_link(raw_link, canon_placetype,
placetype ~= canon_placetype and placetype or nil, ptdata, from_category, noerror), ptdata
end
end
return nil
end
local function resolve_unlinked_placename_display_aliases(placetype, placename)
local equiv_placetypes = export.get_placetype_equivs(placetype)
for i, equiv in ipairs(equiv_placetypes) do
equiv_placetypes[i] = equiv.placetype
end
local all_display_aliases_found = {}
local all_others_found = {}
for group, key, spec in m_locations.iterate_matching_location {
placetypes = equiv_placetypes,
placename = placename,
alias_resolution = "display",
} do
if spec.alias_of and spec.display then
insert(all_display_aliases_found, {group, key, spec, spec.display_as_full})
else
insert(all_others_found, {group, key, spec})
end
end
if not all_display_aliases_found[1] then
return placename
elseif all_display_aliases_found[2] then
internal_error("Found multiple matching display aliases for placename %s, placetype %s: " ..
"all_display_aliases_found=%s, all_others_found=%s", placename, placetype, all_display_aliases_found,
all_others_found)
elseif all_others_found[1] then
internal_error("Found a display alias along with other possible meanings for placename %s, placetype %s: " ..
"all_display_aliases_found=%s, all_others_found=%s", placename, placetype, all_display_aliases_found,
all_others_found)
else
local group, key, spec, as_full = unpack(all_display_aliases_found[1])
local full, elliptical = m_locations.key_to_placename(group, key)
return as_full and full or elliptical
end
end
--[==[
If `placename` of type `placetype` is a display alias, convert it to its canonical form; otherwise, return unchanged.
Display aliases transform certain placenames into canonical displayed forms. For example, if any of `country/US`,
`country/USA` or `country/United States of America` (or `c/US`, etc.) are given, the result will be displayed as
`United States`.
'''NOTE''': Display aliases change what is displayed from what the editor wrote in the Wikitext. As a result, they
should (a) be non-political in nature, and (b) not involve a change where the word `the` needs to be added or removed.
For example, normalizing `US` and `USA` to `United States` for display purposes is OK but normalizing `Burma` to
`Myanmar` is not (instead a cat alias should be used) because the terms `Burma` and `Myanmar` have clear political
connotations. Similarly, we have a display alias that maps the old name of `Macedonia` as a country (but not a region!)
to `North Macedonia`, but `Republic of Macedonia` is mapped to `North Macedonia` only as a cat alias because the two
terms differ in their use of `the`. (For example, if we had a display alias mapping `Republic of Macedonia` to
`North Macedonia`, the call {{tl|place|en|the <<capital city>> of the <<c/Republic of Macedonia>>}} would wrongly
display as `the [[capital city]] of the [[North Macedonia]]`.) Generally, display normalizations tend to involve
alternative forms (e.g. abbreviations, ellipses, foreign spellings) where the normalization improves clarity and
consistency.
]==]
function export.resolve_placename_display_aliases(placetype, placename)
-- If the placename is a link, apply the alias inside the link.
-- This pattern matches both piped and unpiped links. If the link is not piped, the second capture (linktext) will
-- be empty.
local link, linktext = rmatch(placename, "^%[%[([^|%[%]]+)|?([^|%[%]]-)%]%]$")
if link then
if linktext ~= "" then
local alias = resolve_unlinked_placename_display_aliases(placetype, linktext)
return "[[" .. link .. "|" .. alias .. "]]"
else
local alias = resolve_unlinked_placename_display_aliases(placetype, link)
return "[[" .. alias .. "]]"
end
else
return resolve_unlinked_placename_display_aliases(placetype, placename)
end
end
--[==[
Generate the "prefixed" version of a bare key, i.e. prefix it with `the` if correct for this key.
]==]
function export.get_prefixed_key(key, spec)
if spec.the then
return "the " .. key
else
return key
end
end
-- Necessary for use by [[Module:place]]. FIXME: Reorganize the modules so this isn't necessary.
export.iterate_matching_location = m_locations.iterate_matching_location
--[=[
Iterator that iterates over holonyms in `place_desc`. If `first_holonym_index` is given, start iterating at the
specified holonym and stop either when there are no more holonyms or a holonym with modifier `:also` is found. If
`first_holonym_index` is nil or omitted, iterate over all holonyms regardless. If `include_raw_text_holonyms` is
specified, raw text holonyms (those not of the form `placetype/placename`) are returned as well; they can be identified
by the fact that the `placetype` field in the holonym structure is nil. Two values are returned at each iteration, the
holonym index and holonym structure, similar to `ipairs()`.
]=]
function export.get_holonyms_to_check(place_desc, first_holonym_index, include_raw_text_holonyms)
local stop_at_also = not not first_holonym_index
return function(place_desc, index)
while true do
index = index + 1
local this_holonym = place_desc.holonyms[index]
-- If we were passed in a starting holonym index, go up to but not including a holonym marked with `:also`
-- (continue_cat_loop); the categorization code will then restart the loop at that holonym. That holonym
-- will have `:also` marked on it, so make sure not to stop immediately if the first holonym is marked with
-- `:also`.
if not this_holonym or stop_at_also and index > first_holonym_index and this_holonym.continue_cat_loop then
return nil
end
-- If not placetype, we're processing raw text, which we normally want to skip.
if include_raw_text_holonyms or this_holonym.placetype then
return index, this_holonym
end
end
end, place_desc, first_holonym_index and first_holonym_index - 1 or 0
end
--[==[
If the holonym in `data` (in the format as passed to a category handler) refers to a known location, iterate over all
such known locations, returning for each location the corresponding key, spec and group as well as the trail of
ancestral containers. Unlike `iterate_matching_location()`, this specifically checks that there is no mismatch between
the location's containers at any level and any of the following holonyms in the {{tl|place}} spec. The fields in `data`
are:
* `holonym_placetype`: The placetype of the holonym. It can actually be a list of possible placetypes, as with
`iterate_matching_location()`.
* `holonym_placename`: The placename of the holonym.
* `holonym_index`: The index of the holonym among the holonyms in `place_desc`, or nil if the holonym is not among the
holonyms in `place_desc`. (If a holonym index is given, we check for container mismatches among the holonyms
following the specified index, stopping either when encountering a holonym marked with modifier `:also` or, if none
exist, when we run out of holonyms. If no holonym index is given, we check all holonyms for container mismatches.)
* `place_desc`: Description of the place; used for the holonyms, to check for container mismatches.
Returns four values: the location group, the canonical key by which the location is known, the spec object describing
the location and the trail of ancestral containers for the location. The first three values are the same as for
`iterate_matching_location`.
]==]
function export.iterate_matching_holonym_location(data)
local holonym_placetype, holonym_placename, holonym_index, place_desc =
data.holonym_placetype, data.holonym_placename, data.holonym_index, data.place_desc
local matching_location_iterator = m_locations.iterate_matching_location {
placetypes = holonym_placetype,
placename = holonym_placename,
}
return function()
while true do
local group, key, spec = matching_location_iterator()
if not group then
return nil
end
local container_trail = {}
-- For each level of container, check that there are no mismatches (i.e. other location of the same
-- placetype) mentioned. We allow a mismatch at a given level if there's also a match with the container
-- at that level. For example, in the case of Kansas City, defined in [[Module:place/locations]] as a city
-- in Missouri, if we define it as {{tl|place|city|s/Missouri,Kansas}}, we ignore the mismatching state of
-- Kansas because the correct state of Missouri was also mentioned. But imagine we are defining Newark,
-- Delaware as {{tl|place|city|s/Delaware|c/US}} and (as is the case) we have an entry for Newark, New
-- Jersey in [[Module:place/locations]]. Just because the containing location `US` matches isn't enough,
-- because Newark, NJ also has New Jersey as a containing location and there's a mismatch at that level. If
-- there are no mismatches at any level we assume we're dealing with the right known location.
--
-- If at a given level there are multiple containing locations, we count a match if any holonym matches any
-- containing location, and a mismatch only if a holonym exists of the same placetype that doesn't match any
-- containing location.
local containers_mismatch = false
for containers in m_locations.iterate_containers(group, key, spec) do
insert(container_trail, containers)
local match_at_level = false
local mismatch_at_level = false
for other_holonym_index, other_holonym in export.get_holonyms_to_check(place_desc,
holonym_index and holonym_index + 1 or nil) do
local other_source_holonym = other_holonym.augmented_from_holonym
if other_source_holonym and other_source_holonym.placetype == holonym_placetype and
other_source_holonym.unlinked_placename ~= holonym_placename then
-- Ignore holonyms added during the augmentation process for other holonyms of the same
-- placetype as the placetype of the holonym we're considering. See comment in
-- augment_holonyms_with_container() for why we do this.
-- continue; grrr, no 'continue' in Lua
else
local holonym_matches_at_level = false
local holonym_exists_with_same_placetype = false
for _, container in ipairs(containers) do
if not container.spec.no_check_holonym_mismatch then
local full_container_placename, elliptical_container_placename =
m_locations.key_to_placename(container.group, container.key)
local placetypes = container.spec.placetype
if type(placetypes) ~= "table" then
placetypes = {placetypes}
end
local placetype_equivs = {}
for _, pt in ipairs(placetypes) do
m_table.extend(placetype_equivs, export.get_placetype_equivs(pt))
end
local this_holonym_matches = export.get_equiv_placetype_prop_from_equivs(
placetype_equivs, function(placetype)
return other_holonym.placetype == placetype and
(other_holonym.unlinked_placename == full_container_placename or
other_holonym.unlinked_placename == elliptical_container_placename)
end
)
if this_holonym_matches then
holonym_matches_at_level = true
break
end
local this_holonym_exists_with_same_placetype = export.get_equiv_placetype_prop_from_equivs(
placetype_equivs, function(placetype)
return other_holonym.placetype == placetype
end
)
if this_holonym_exists_with_same_placetype then
-- We seem to have a mismatch at this level. But before we decide conclusively that this
-- is the case, check to see whether the putative mismatch is an alias and matches when
-- we resolve the alias.
for oh_group, oh_key, oh_spec, oh_container_trail in
export.iterate_matching_holonym_location {
holonym_placetype = other_holonym.placetype,
holonym_placename = other_holonym.unlinked_placename,
holonym_index = other_holonym_index,
place_desc = place_desc,
} do
local oh_full_placename, oh_elliptical_placename =
m_locations.key_to_placename(oh_group, oh_key)
if oh_full_placename == full_container_placename or
oh_elliptical_placename == elliptical_container_placename then
-- Alias matched when resolved.
this_holonym_matches = true
break
end
end
if this_holonym_matches then
-- Alias matched above when resolved.
holonym_matches_at_level = true
break
else
-- Not an alias, or doesn't match when resolved. We have a true mismatch.
holonym_exists_with_same_placetype = true
end
end
end
end
if holonym_matches_at_level then
match_at_level = true
break
end
if holonym_exists_with_same_placetype then
mismatch_at_level = true
end
end
end
if not match_at_level and mismatch_at_level then
containers_mismatch = true
break
end
end
if not containers_mismatch then
return group, key, spec, container_trail
end
end
end
end
--[==[
If the holonym in `data` (in the format as passed to a category handler) refers to a known location, find and return the
corresponding key, spec and group as well as the trail of ancestral containers. This is like
`iterate_matching_holonym_location()` but throws an error if more than one location matches. (An example where this
would happen is {{tl|place|en|neighborhood|city/Newcastle}}, because there are two known locations named Newcastle. To
fix this, specify additional following disambiguating holonyms, e.g.
{{tl|place|en|neighborhood|city/Newcastle|s/New South Wales}}.
]==]
function export.find_matching_holonym_location(data)
local all_found = {}
for group, key, spec, container_trail in export.iterate_matching_holonym_location(data) do
insert(all_found, {group, key, spec, container_trail})
end
if not all_found[1] then
return nil
elseif all_found[2] then
local holonym_placetype = data.holonym_placetype
if type(holonym_placetype) == "table" then
holonym_placetype = concat(holonym_placetype, ",")
end
local found_keys = {}
for _, found in ipairs(all_found) do
local _, key, _, _ = unpack(found)
insert(found_keys, key)
end
error(("Found multiple matching locations for holonym '%s/%s'; specify disambiguating context in the " ..
"containing holonyms: %s"):format(holonym_placetype, data.holonym_placename, dump(found_keys)))
else
return unpack(all_found[1])
end
end
------------------------------------------------------------------------------------------
-- Placename and placetype data --
------------------------------------------------------------------------------------------
--[==[ var:
This is a map from aliases to their canonical forms. Any placetypes appearing as keys here will be mapped to their
canonical forms in all respects, including the display form. Contrast entries in 'placetype_data' with a fallback, which
applies to categorization and other processes but not to display.
The most important aliases are for holonym placetypes, particularly those that occur often such as "country", "state",
"province" and the like. Particularly long placetypes that mostly occur as entry placetypes (e.g.
"census-designated place") can be given abbreviations, but it is generally preferred to spell out the entry placetype.
Note also that we purposely avoid certain abbreviations that would be ambiguous (e.g. "d", which could variously be
interpreted as "department", "district" or "division").
]==]
export.placetype_aliases = {
["acomm"] = "autonomous community",
["adr"] = "administrative region",
["adterr"] = "administrative territory", -- Pakistan
["aobl"] = "autonomous oblast",
["aokr"] = "autonomous okrug",
["ap"] = "autonomous province",
["apref"] = "autonomous prefecture",
["aprov"] = "autonomous province",
["ar"] = "autonomous region",
["arch"] = "archipelago",
["arep"] = "autonomous republic",
["aterr"] = "autonomous territory",
["atu"] = "autonomous territorial unit",
["bor"] = "borough",
["c"] = "country",
["can"] = "canton",
["carea"] = "council area",
["cc"] = "constituent country",
["cdblock"] = "community development block",
["cdep"] = "Crown dependency",
["CDP"] = "census-designated place",
["cdp"] = "census-designated place",
["clcity"] = "county-level city",
["co"] = "county",
["cobor"] = "county borough",
["colcity"] = "county-level city",
["coll"] = "collectivity",
["comm"] = "community",
["cont"] = "continent",
["contr"] = "continental region",
["contregion"] = "continental region",
["cpar"] = "civil parish",
["damun"] = "direct-administered municipality",
["dep"] = "dependency",
["department capital"] = "departmental capital",
["dept"] = "department",
["depterr"] = "dependent territory",
["dist"] = "district",
["distmun"] = "district municipality",
["div"] = "division",
["emp"] = "empire",
["fpref"] = "French prefecture",
["gov"] = "governorate",
["govnat"] = "governorate",
["home-rule city"] = "home rule city",
["home-rule municipality"] = "home rule municipality",
["inner-city area"] = "inner city area",
["ires"] = "Indian reservation",
["isl"] = "island",
["lbor"] = "London borough",
["lga"] = "local government area",
["lgarea"] = "local government area",
["lgd"] = "local government district",
["lgdist"] = "local government district",
["metbor"] = "metropolitan borough",
["metcity"] = "metropolitan city",
["metmun"] = "metropolitan municipality",
["mtn"] = "mountain",
["mun"] = "municipality",
["mundist"] = "municipal district",
["nonmetropolitan county"] = "non-metropolitan county",
["obl"] = "oblast",
["okr"] = "okrug",
["p"] = "province",
["par"] = "parish",
["parmun"] = "parish municipality",
["pen"] = "peninsula",
["plcity"] = "prefecture-level city",
["plcolony"] = "Polish colony",
["pref"] = "prefecture",
["prefcity"] = "prefecture-level city",
["preflcity"] = "prefecture-level city",
["prov"] = "province",
["r"] = "region",
["range"] = "mountain range",
["rcm"] = "regional county municipality",
["rcomun"] = "regional county municipality",
["rdist"] = "regional district",
["rep"] = "republic",
["rhrom"] = "rural hromada",
["riv"] = "river",
["rmun"] = "regional municipality",
["robor"] = "royal borough",
["romp"] = "Roman province",
["runit"] = "regional unit",
["rurmun"] = "rural municipality",
["s"] = "state",
["sar"] = "special administrative region",
["shrom"] = "settlement hromada",
["spref"] = "subprefecture",
["sprefcity"] = "sub-prefectural city",
["sprovcity"] = "subprovincial city",
["submet city"] = "sub-metropolitan city",
["submetropolitan city"] = "sub-metropolitan city",
["sub-prefecture-level city"] = "sub-prefectural city",
["sub-provincial city"] = "subprovincial city",
["sub-provincial district"] = "subprovincial district",
["terr"] = "territory",
["terrauth"] = "territorial authority",
["twp"] = "township",
["twpmun"] = "township municipality",
["uauth"] = "unitary authority",
["ucomm"] = "unincorporated community",
["udist"] = "unitary district",
["uhrom"] = "urban hromada",
["uterr"] = "union territory",
["utwpmun"] = "united township municipality",
["val"] = "valley",
["vdc"] = "village development committee",
["vil"] = "village",
["voi"] = "voivodeship",
["wcomm"] = "Welsh community",
}
local no_link_def_article = {link = false, article = "the"}
local no_link_no_article = {link = false, article = false}
--[==[ var:
These qualifiers can be prepended onto any placetype and will be handled correctly. For example, the placetype
`large city` will be displayed as `large <nowiki>[[city]]</nowiki>` and categorized as if `city` were specified. If the
value in the following table is a string, the qualifier will display according to the string. If the value is `true`,
the qualifier will be linked to its corresponding Wiktionary entry. If the value is `false`, the qualifier will not be
linked but will appear as-is. Note that these qualifiers do not override placetypes with entries elsewhere that contain
those same qualifiers. For example, the entry for `inland sea` in `placetype_data` will apply in preference to treating
`inland sea` as equivalent to `sea`.
]==]
export.placetype_qualifiers = {
-- generic qualifiers
["huge"] = false,
["tiny"] = false,
["large"] = false,
["big"] = false,
["mid-size"] = false,
["mid-sized"] = false,
["small"] = false,
["sizable"] = false,
["important"] = false,
["long"] = false,
["short"] = false,
["major"] = false,
["minor"] = false,
["high"] = false,
["tall"] = false,
["low"] = false,
["left"] = false, -- left tributary
["right"] = false, -- right tributary
["modern"] = false, -- for use in opposition to "ancient" in another definition
-- "former" qualifiers
["abandoned"] = true,
["ancient"] = true,
["deserted"] = true,
["extinct"] = true,
["former"] = false,
["historic"] = "historical",
["historical"] = true,
["medieval"] = true,
["mediaeval"] = true,
["ruined"] = true,
["traditional"] = true,
-- sea qualifiers
["coastal"] = true,
["inland"] = true, -- note, we also have an entry in placetype_data for 'inland sea' to get a link to [[inland sea]]
["maritime"] = true,
["overseas"] = true,
["seaside"] = true,
["beachfront"] = true,
["beachside"] = true,
["riverside"] = true,
-- lake qualifiers
["freshwater"] = true,
["saltwater"] = true,
["endorheic"] = true,
["oxbow"] = true,
["ox-bow"] = "[[oxbow]]", -- [[ox-bow]] is a red link
["tidal"] = true,
-- land qualifiers
["hilltop"] = true,
["hilly"] = true,
["insular"] = true,
["peninsular"] = true,
["chalk"] = true,
["karst"] = true,
["limestone"] = true,
["mountainous"] = true,
["mountaintop"] = true,
["alpine"] = true,
["volcanic"] = true, -- for an island
-- political status qualifiers
["autonomous"] = true,
["incorporated"] = true,
["special"] = true,
["unincorporated"] = true,
["coterminous"] = true,
-- monetary status/etc. qualifiers
["fashionable"] = true,
["wealthy"] = true,
["affluent"] = true,
["declining"] = true,
-- city vs. rural qualifiers
["urban"] = true,
["suburban"] = true,
["exurban"] = true,
["outlying"] = true,
["remote"] = true,
["rural"] = true,
["outback"] = true,
["inner"] = false,
["inner-city"] = true,
["central"] = false,
["outer"] = false,
-- land use qualifiers
["residential"] = true,
["agricultural"] = true,
["business"] = true,
["commercial"] = true,
["industrial"] = true,
-- business use qualifiers
["railroad"] = true,
["railway"] = true,
["farming"] = true,
["fishing"] = true,
["mining"] = true,
["logging"] = true,
["cattle"] = true,
-- tourism use qualifiers
["resort"] = true, -- note, we also have 'resort city' and 'resort town', that take precedecne
["spa"] = true, -- note, we also have 'spa city' and 'spa town', that take precedecne
["ski"] = true, -- note, we also have 'ski resort city' and 'ski resort town', that take precedecne
-- religious qualifiers
["holy"] = true,
["sacred"] = true,
["religious"] = true,
["secular"] = true,
-- qualifiers for nonexistent places
["claimed"] = false,
["fictional"] = true,
["legendary"] = true,
["mythical"] = true,
["mythological"] = true,
-- directional qualifiers
["northern"] = false,
["southern"] = false,
["eastern"] = false,
["western"] = false,
["north"] = false,
["south"] = false,
["east"] = false,
["west"] = false,
["northeastern"] = false,
["southeastern"] = false,
["northwestern"] = false,
["southwestern"] = false,
["northeast"] = false,
["southeast"] = false,
["northwest"] = false,
["southwest"] = false,
-- seasonal qualifiers
["summer"] = true, -- e.g. for 'summer capital'
["winter"] = true,
-- legal status qualifiers
-- FIXME: Two-word qualifiers don't work yet. But you can enter "de-facto" and it's canonicalized to [[de facto]].
["official"] = true,
["unofficial"] = true,
["de facto"] = true, -- 'de facto capital'
["de-facto"] = "[[de facto]]", -- [[de-facto]] is a red link
["de jure"] = true, -- 'de jure capital'
["de-jure"] = "[[de jure]]", -- [[de-jure]] is a red link
-- NOTE: 'unrecognized/unrecognised' are handled as placetypes 'unrecognized country', 'unrecognized state'
-- misc. qualifiers
["planned"] = true,
["chartered"] = true,
["landlocked"] = true,
["uninhabited"] = true,
-- superlative qualifiers
["first"] = no_link_def_article,
["second"] = no_link_def_article, -- for "second largest" etc.
["third"] = no_link_def_article,
["fourth"] = no_link_def_article,
["last"] = no_link_def_article,
["only"] = no_link_def_article,
["sole"] = no_link_def_article,
["main"] = no_link_def_article,
["largest"] = no_link_def_article,
["biggest"] = no_link_def_article,
["smallest"] = no_link_def_article,
["shortest"] = no_link_def_article,
["longest"] = no_link_def_article,
["tallest"] = no_link_def_article,
["highest"] = no_link_def_article,
["lowest"] = no_link_def_article,
["leftmost"] = no_link_def_article,
["rightmost"] = no_link_def_article,
["innermost"] = no_link_def_article,
["outermost"] = no_link_def_article,
["northernmost"] = no_link_def_article,
["southernmost"] = no_link_def_article,
["westernmost"] = no_link_def_article,
["easternmost"] = no_link_def_article,
["northwesternmost"] = no_link_def_article,
["southwesternmost"] = no_link_def_article,
["northeasternmost"] = no_link_def_article,
["southeasternmost"] = no_link_def_article,
-- several/various
["several"] = no_link_no_article,
["various"] = no_link_no_article,
["numerous"] = no_link_no_article,
["multiple"] = no_link_no_article,
["many"] = no_link_no_article,
["other"] = no_link_no_article,
}
--[==[ var:
In this table, the key qualifiers should be treated the same as the value qualifiers for categorization purposes. This
is overridden by `placetype_data` and `qualifier_to_placetype_equivs`.
]==]
export.former_qualifiers = {
["abandoned"] = {"FORMER"},
["ancient"] = {"ANCIENT", "FORMER"},
["former"] = {"FORMER"},
["extinct"] = {"FORMER"},
["historic"] = {"FORMER"},
["historical"] = {"FORMER"},
["medieval"] = {"ANCIENT", "FORMER"},
["mediaeval"] = {"ANCIENT", "FORMER"},
["ruined"] = {"ANCIENT", "FORMER"},
["traditional"] = {"FORMER"},
}
--[==[ var:
In this table, any placetypes containing these qualifiers that do not occur in `placetype_data` should be mapped to the
specified placetypes for categorization purposes. Entries here are overridden by `placetype_data`.
]==]
export.qualifier_to_placetype_equivs = {
["fictional"] = "fictional location",
["legendary"] = "mythological location",
["mythical"] = "mythological location",
["mythological"] = "mythological location",
-- For e.g. Taiwan as a "claimed province" of China; parts of Belize as claimed by Guatemala; various islands
-- claimed by various parties in East Asia. FIXME: We should conditionalize on what is being claimed since there are
-- also claimed capitals, e.g. Israel and Palestine claim Jerusalem as their capital.
["claimed"] = "claimed political division",
}
--[==[ var:
Mapping from placetypes to the corresponding plural category-only placetype for a capital of that placetype. The reverse
mapping also exists.
]==]
export.placetype_to_capital_cat = {
["autonomous community"] = "autonomous community capitals",
["canton"] = "cantonal capitals",
["comarca"] = "comarca capitals",
["country"] = "national capitals",
-- The following are not obviously different from 'county seats' but the latte terminology is used in the US.
["county"] = "county capitals",
["department"] = "departmental capitals",
["district"] = "district capitals",
["division"] = "division capitals",
["emirate"] = "emirate capitals",
["governorate"] = "governorate capitals",
["hromada"] = "hromada capitals",
["krai"] = "krai capitals",
["metropolitan city"] = "metropolitan city capitals",
["municipality"] = "municipal capitals",
["oblast"] = "oblast capitals",
["okrug"] = "okrug capitals",
["prefecture"] = "prefectural capitals",
["province"] = "provincial capitals",
["raion"] = "raion capitals",
["regency"] = "regency capitals",
["region"] = "regional capitals",
["regional unit"] = "regional unit capitals",
["republic"] = "republic capitals",
["state"] = "state capitals",
["territory"] = "territorial capitals",
["voivodeship"] = "voivodeship capitals",
}
--[==[ var:
This contains placenames that should be preceded by an article (almost always "the"). '''NOTE''': There are multiple
ways that placenames can come to be preceded by "the":
# Listed here.
# Given in [[Module:place/locations]] with an initial "the". All such placenames are added to this map by the code
just below the map.
# The placetype of the placename has `holonym_use_the = true` in its placetype_data.
# A regex in placename_the_re matches the placename.
Note that "the" is added only before the first holonym in a place description.
]==]
export.placename_article = {
-- This should only contain info that can't be inferred from [[Module:place/locations]].
["archipelago"] = {
["Cyclades"] = "the",
["Dodecanese"] = "the",
},
["country"] = {
["Holy Roman Empire"] = "the",
},
["empire"] = {
["Holy Roman Empire"] = "the",
},
["island"] = {
["North Island"] = "the",
["South Island"] = "the",
},
["region"] = {
["Balkans"] = "the",
["Russian Far East"] = "the",
["Caribbean"] = "the",
["Caucasus"] = "the",
["Middle East"] = "the",
["New Territories"] = "the",
["North Caucasus"] = "the",
["South Caucasus"] = "the",
["West Bank"] = "the",
["Gaza Strip"] = "the",
},
["valley"] = {
["San Fernando Valley"] = "the",
},
}
--[==[ var:
Regular expressions to apply to determine whether we need to put 'the' before a holonym. The key "*" applies to all
holonyms, otherwise only the regexes for the holonym's placetype apply.
]==]
export.placename_the_re = {
-- We don't need entries for peninsulas, seas, oceans, gulfs or rivers
-- because they have holonym_use_the = true.
["*"] = {"^Isle of ", " Islands$", " Mountains$", " Empire$", " Country$", " Region$", " District$", "^City of "},
["bay"] = {"^Bay of "},
["lake"] = {"^Lake of "},
["country"] = {"^Republic of ", " Republic$"},
["republic"] = {"^Republic of ", " Republic$"},
["region"] = {" [Rr]egion$"},
["river"] = {" River$"},
["local government area"] = {"^Shire of "},
["county"] = {"^Shire of "},
["Indian reservation"] = {" Reservation", " Nation"},
["tribal jurisdictional area"] = {" Reservation", " Nation"},
}
--[==[ var:
If any of the following holonyms are present, the associated holonyms are automatically added to the end of the list of
holonyms for categorization (but not display) purposes.
]==]
export.cat_implications = {
["region"] = {
["Eastern Europe"] = {"continent/Europe"},
["Central Europe"] = {"continent/Europe"},
["Western Europe"] = {"continent/Europe"},
["South Europe"] = {"continent/Europe"},
["Southern Europe"] = {"continent/Europe"},
["Northern Europe"] = {"continent/Europe"},
["Northeast Europe"] = {"continent/Europe"},
["Northeastern Europe"] = {"continent/Europe"},
["Southeast Europe"] = {"continent/Europe"},
["Southeastern Europe"] = {"continent/Europe"},
["North Caucasus"] = {"continent/Europe"},
["South Caucasus"] = {"continent/Asia"},
["South Asia"] = {"continent/Asia"},
["Southern Asia"] = {"continent/Asia"},
["East Asia"] = {"continent/Asia"},
["Eastern Asia"] = {"continent/Asia"},
["Central Asia"] = {"continent/Asia"},
["West Asia"] = {"continent/Asia"},
["Western Asia"] = {"continent/Asia"},
["Southeast Asia"] = {"continent/Asia"},
["North Asia"] = {"continent/Asia"},
["Northern Asia"] = {"continent/Asia"},
["Anatolia"] = {"continent/Asia"},
["Asia Minor"] = {"continent/Asia"},
["Mesopotamia"] = {"continent/Asia"},
["North Africa"] = {"continent/Africa"},
["Central Africa"] = {"continent/Africa"},
["West Africa"] = {"continent/Africa"},
["East Africa"] = {"continent/Africa"},
["Southern Africa"] = {"continent/Africa"},
["Central America"] = {"continent/Central America"},
["Caribbean"] = {"continent/North America"},
["Polynesia"] = {"continent/Oceania"},
["Micronesia"] = {"continent/Oceania"},
["Melanesia"] = {"continent/Oceania"},
["Siberia"] = {"country/Russia", "continent/Asia"},
["Russian Far East"] = {"country/Russia", "continent/Asia"},
["South Wales"] = {"constituent country/Wales", "continent/Europe"},
["Balkans"] = {"continent/Europe"},
["West Bank"] = {"country/Palestine", "continent/Asia"},
["Gaza"] = {"country/Palestine", "continent/Asia"},
["Gaza Strip"] = {"country/Palestine", "continent/Asia"},
}
}
------------------------------------------------------------------------------------------
-- Category and display handlers --
------------------------------------------------------------------------------------------
local function city_type_cat_handler(data)
local entry_placetype = data.entry_placetype
local generic_before_non_cities = export.get_placetype_prop(entry_placetype, "generic_before_non_cities")
if not generic_before_non_cities then
internal_error("city_type_cat_handler called on placetype %s that doesn't have a `generic_before_non_cities`" ..
" setting", entry_placetype)
end
local plural_entry_placetype = export.pluralize_placetype(entry_placetype)
local group, key, spec, container_trail = export.find_matching_holonym_location(data)
if group and not spec.is_former_place and not spec.is_city then
-- Categorize both in key, and in the larger polity that the key is part of, e.g. [[Hirakata]] goes in both
-- "Cities in Osaka Prefecture" and "Cities in Japan". (But don't do the latter if no_container_cat is set.)
local cap_plural_entry_placetype = ucfirst(plural_entry_placetype)
local retcats = {("%s %s %s"):format(cap_plural_entry_placetype, generic_before_non_cities,
export.get_prefixed_key(key, spec))}
if container_trail[1] and not spec.no_container_cat then
for _, container in ipairs(container_trail[1]) do
insert(retcats, ("%s %s %s"):format(cap_plural_entry_placetype, generic_before_non_cities,
export.get_prefixed_key(container.key, container.spec)))
end
end
return retcats
end
end
local function capital_city_cat_handler(data, non_city)
local holonym_placetype, holonym_placename, holonym_index, place_desc =
data.holonym_placetype, data.holonym_placename, data.holonym_index, data.place_desc
-- The first time we're called we want to return something; otherwise we will be called for later-mentioned
-- holonyms, which can result in wrongly classifying into e.g. `National capitals`. Simulate the loop in
-- find_placetype_cat_specs() over holonyms so we get the proper `Cities in ...` categories as well as the capital
-- category/categories we add below.
local retcats
if not non_city and place_desc.holonyms then
for h_index, holonym in export.get_holonyms_to_check(place_desc, holonym_index) do
local h_placetype, h_placename = holonym.placetype, holonym.unlinked_placename
retcats = city_type_cat_handler {
entry_placetype = "city",
holonym_placetype = h_placetype,
holonym_placename = h_placename,
holonym_index = h_index,
place_desc = place_desc,
}
if retcats then
break
end
end
end
if not retcats then
retcats = {}
end
-- Now find the appropriate capital-type category for the placetype of the holonym, e.g. 'State capitals'. If we
-- recognize the holonym among the known holonyms in [[Module:place/locations]], also add a category like 'State
-- capitals of the United States'. Truncate e.g. 'autonomous region' to 'region', 'union territory' to 'territory'
-- when looking up the type of capital category, if we can't find an entry for the holonym placetype itself (there's
-- an entry for 'autonomous community').
local capital_cat = export.placetype_to_capital_cat[holonym_placetype]
if not capital_cat then
capital_cat = export.placetype_to_capital_cat[holonym_placetype:gsub("^.* ", "")]
end
if capital_cat then
capital_cat = ucfirst(capital_cat)
local inserted_specific_variant_cat = false
if holonym_index then
-- Now find the first recognized holonym location. We don't stop when :also is seen because of the common pattern
-- where we use :also to specify that a given city is the capital at multiple surrounding levels.
local matching_group, matching_key, matching_spec, matching_container_trail, matching_holonym_index
for h_index = holonym_index, #place_desc.holonyms do
if place_desc.holonyms[h_index].placetype then
matching_group, matching_key, matching_spec, matching_container_trail = export.find_matching_holonym_location {
holonym_placetype = place_desc.holonyms[h_index].placetype,
holonym_placename = place_desc.holonyms[h_index].unlinked_placename,
holonym_index = h_index,
place_desc = place_desc,
}
if matching_group then
matching_holonym_index = h_index
break
end
end
end
if matching_holonym_index == holonym_index then
if matching_container_trail[1] and not matching_spec.no_container_cat then
for _, container in ipairs(matching_container_trail[1]) do
insert(retcats, ("%s of %s"):format(capital_cat, export.get_prefixed_key(container.key,
container.spec)))
inserted_specific_variant_cat = true
end
end
elseif matching_holonym_index then
-- Check to make sure that the holonym placetype we were called on is listed among the
-- divtypes of the location we found.
local function insert_specific_variant_if_possible(key, spec)
return export.get_equiv_placetype_prop(holonym_placetype, function(pt)
local plural_holonym_placetype = export.pluralize_placetype(pt)
local saw_matching_div
if spec.divs then
local divs = spec.divs
if type(divs) ~= "table" then
divs = {divs}
end
for _, div in ipairs(divs) do
if type(div) ~= "table" then
div = {type = div}
end
if plural_holonym_placetype == div.type then
saw_matching_div = true
break
end
end
end
if saw_matching_div then
insert(retcats, ("%s of %s"):format(capital_cat, export.get_prefixed_key(key, spec)))
return true
end
return false
end)
end
if insert_specific_variant_if_possible(matching_key, matching_spec) then
inserted_specific_variant_cat = true
elseif not matching_spec.no_container_cat then
for _, containers in ipairs(matching_container_trail) do
local saw_no_container_cat = false
for _, container in ipairs(containers) do
if insert_specific_variant_if_possible(container.key, container.spec) then
inserted_specific_variant_cat = true
break
end
saw_no_container_cat = saw_no_container_cat or container.spec.no_container_cat
end
if inserted_specific_variant_cat or saw_no_container_cat then
break
end
end
end
end
else
-- This happens when in an invocation like {{place|en|capital city|s/Haryana,Punjab}} for
-- [[Chandigarh]]. We fall back to older code that doesn't depend on the holonym index existing.
-- FIXME: This may not be necessary. In the example just given, when processing Haryana we add to
-- [[:Category:en:State capitals of India]], and nothing extra gets added when processing Punjab.
-- Possibly we can just skip this case entirely.
local group, key, spec, container_trail = export.find_matching_holonym_location(data)
if group and container_trail[1] and not spec.no_container_cat then
for _, container in ipairs(container_trail[1]) do
insert(retcats, ("%s of %s"):format(capital_cat, export.get_prefixed_key(container.key,
container.spec)))
inserted_specific_variant_cat = true
end
end
end
if not inserted_specific_variant_cat then
insert(retcats, capital_cat)
end
else
-- We didn't recognize the holonym placetype; just put in 'Capital cities'.
insert(retcats, "Capital cities")
end
return retcats
end
--[=[
This is invoked specially for all placetypes (see the `*` placetype key at the bottom of `placetype_data`). This is used
in two ways:
# To add pages to generic holonym categories like [[:Category:en:Places in Merseyside, England]] (and
[[:Category:en:Places in England]]) for any pages that have `co/Merseyside` as their holonym.
# To categorize demonyms in bare placename categories like [[:Category:en:Merseyside, England]] if the demonym
description mentions `co/Merseyside` and doesn't mention a more specific placename that also has a category. (In this
case there are none, but we can have demonyms at multiple levels, e.g. in France for individual villages, departments,
administrative regions, and for the entire country, and for example we only want to categorize a demonym into
[[:Category:France]] if no more specific category applies.) Unlike when invoked from {{tl|place}}, a demonym
invocation only adds the most specific holonym category and not the category of any containing polity (hence if we
add [[:Category:en:Merseyside, England]] we won't also add [[:Category:England]]).
This code also handles cities; e.g. for the first use case above, it would be used to add a page that has `city/Boston`
as a holonym to [[:Category:en:Places in Boston]], along with [[:Category:en:Places in Massachusetts, USA]] and
[[:Category:en:Places in the United States]]. The city handler tries to deal with the possibility of multiple cities
having the same name. For example, the code in [[Module:place/locations]] knows about the city of [[Columbus]],
[[Ohio]], which has containing polities `Ohio` (a state) and `the United States` (a country). If either containing
polity is mentioned, the handler proceeds to return the key `Columbus` (along with `Ohio, USA` and `the United States`).
Otherwise, if any other state or country is mentioned, the handler returns nothing, and otherwise it assumes the
mentioned city is the one we're considering and returns `Columbus` etc. This works correctly if the place only mentions
Ohio and a holonym for a Columbus in a different country is encountered, because of the function
`augment_holonyms_with_container`, which adds the US as a holonym when Ohio is encountered.
The single parameter `data` is as in category handlers. The return value is a list of categories (without the preceding
language code).
]=]
local function generic_place_cat_handler(data)
local from_demonym = data.from_demonym
local retcats = {}
local function insert_retkey(key, spec)
if from_demonym then
insert(retcats, key)
else
insert(retcats, ("Places in %s"):format(export.get_prefixed_key(key, spec)))
end
end
local group, key, spec, container_trail = export.find_matching_holonym_location(data)
if group then
if not spec.no_generic_place_cat then
-- This applies to continents and continental regions.
insert_retkey(key, spec)
end
-- Categorize both in key, and in the larger location(s) that the key is part of, e.g. [[Hirakata]] goes in
-- both [[Category:Places in Osaka Prefecture, Japan]] and [[Category:Places in Japan]]. But not when
-- no_container_cat is set (e.g. for 'United Kingdom').
if not spec.no_container_cat then
for _, container_set in ipairs(container_trail) do
local stop_adding_containers = false
for _, container in ipairs(container_set) do
if not container.spec.no_generic_place_cat then
insert_retkey(container.key, container.spec)
end
if container.spec.no_container_cat then
stop_adding_containers = true
end
end
if stop_adding_containers then
break
end
end
end
return retcats
end
end
--[==[
Special category handler run for all placetypes that checks for specified division placetypes of known locations and
categorizes appropriately.
]==]
function export.political_division_cat_handler(data)
if data.from_demonym then
return
end
local group, key, spec, container_trail = export.find_matching_holonym_location(data)
if group then
local divlists = {}
if spec.divs then
insert(divlists, spec.divs)
end
if spec.addl_divs then
insert(divlists, spec.addl_divs)
end
for _, divlist in ipairs(divlists) do
if type(divlist) ~= "table" then
divlist = {divlist}
end
for _, div in ipairs(divlist) do
if type(div) == "string" then
div = {type = div}
end
local sgdiv = export.maybe_singularize_placetype(div.type) or div.type
local prep = div.prep or "of"
local cat_as = div.cat_as or div.type
if type(cat_as) ~= "table" then
cat_as = {cat_as}
end
if not export.placetype_data[sgdiv] then
internal_error("Placetype %s associated with known location key %s and data %s not found in " ..
"`placetype_data`", sgdiv, key, spec)
end
if sgdiv == data.entry_placetype then
local retcats = {}
for _, pt_cat in ipairs(cat_as) do
if type(pt_cat) == "string" then
pt_cat = {type = pt_cat}
end
local pt_prep = pt_cat.prep or prep
insert(retcats, ucfirst(pt_cat.type) .. " " .. pt_prep .. " " ..
export.get_prefixed_key(key, spec))
end
return retcats
end
end
end
end
end
--[==[
This is used to add pages to "bare" categories like [[:Category:en:Georgia, USA]] for `[[Georgia]]` and any
foreign-language terms that are translations of the state of Georgia. We look at the page title (or its overridden value
in {{para|pagename}}) as well as the glosses in {{para|t}}/{{para|t2}} etc., various extra-info values such as the
modern names in {{para|modern}}, and any values specified using a form-of directive. We need to pay attention to the
entry placetypes specified so we don't overcategorize; e.g. the US state of Georgia is `[[Джорджия]]` in Russian but the
country of Georgia is `[[Грузия]]`, and if we just looked for matching names, we'd get both Russian terms categorized
into both [[:Category:ru:Georgia, USA]] and [[:Category:ru:Georgia]]. We also need to check the containing holonyms to
make sure there isn't a mismatch (so we don't e.g. categorize Newark, Delaware in [[:Category:en:Newark]], which is
intended for Newark, New Jersey).
]==]
function export.get_bare_categories(args, overall_place_spec)
local bare_cats = {}
local place_descs = overall_place_spec.descs
local possible_placetypes_by_place_desc = {}
for i, place_desc in ipairs(place_descs) do
possible_placetypes_by_place_desc[i] = {}
for _, placetype in ipairs(place_desc.placetypes) do
if not export.placetype_is_ignorable(placetype) then
local equivs = export.get_placetype_equivs(placetype, {register_former_as_non_former = true})
for _, equiv in ipairs(equivs) do
insert(possible_placetypes_by_place_desc[i], equiv.placetype)
end
end
end
end
local function check_term(term)
-- Treat Wikipedia links like local ones.
term = term:gsub("%[%[w:", "[["):gsub("%[%[wikipedia:", "[[")
term = export.remove_links_and_html(term)
term = term:gsub("^the ", "")
for i, place_desc in ipairs(place_descs) do
-- Iterate over all matching locations in case there are multiple, as with Delhi defined as
-- {{place|en|megacity/and/union territory|c/India|containing the national capital [[New Delhi]]}}.
for group, key, spec, container_trail in export.iterate_matching_holonym_location {
holonym_placetype = possible_placetypes_by_place_desc[i],
holonym_placename = term,
place_desc = place_desc,
} do
insert(bare_cats, key)
end
end
end
-- FIXME: Should we only do the following if the language is English (requires that the lang is passed in)?
-- We should always do it if `pagename` is given (as it is with {{tcl}}) but maybe not otherwise unless 1=en. There
-- are cases like [[Ankara]] = English name for capital of Turkey, but also the name in various languages for the
-- capital of Ghana (= English [[Accra]]). But this should get caught by mismatching the containing country. The
-- advantage of checking when the language isn't English is we catch those places that fail to give an English
-- translation but where the translation happens to be the same as the other-language spelling. However, I don't
-- know how often this situation occurs.
check_term(args.pagename or mw.loadData("Module:headword/data").pagename)
for _, t in ipairs(args.t) do
check_term(t)
end
local function check_termobj_list(terms)
for _, term in ipairs(terms) do
if term.eq then
check_term(term.eq)
end
if term.alt or term.term then
check_term(term.alt or term.term)
end
end
end
for _, extra_info_terms in ipairs(overall_place_spec.extra_info) do
local arg = extra_info_terms.arg
if arg == "modern" or arg == "now" or arg == "full" or arg == "short" then
check_termobj_list(extra_info_terms.terms)
end
end
for _, directive in ipairs(overall_place_spec.directives) do
check_termobj_list(directive.terms)
end
return bare_cats
end
--[==[
This is used to augment the holonyms associated with a place description with the containing polities. For example,
given the following:
`# {{tl|place|en|subprefecture|pref/Hokkaido}}.`
We auto-add Japan as another holonym so that the term gets categorized into [[:Category:Subprefectures of Japan]].
To avoid over-categorizing we need to check to make sure no other countries are specified as holonyms.
]==]
function export.augment_holonyms_with_container(place_descs)
for _, place_desc in ipairs(place_descs) do
if place_desc.holonyms then
-- This ends up containing a copy of the original holonyms, with the augmented holonyms inserted in their
-- appropriate position. We don't just put them at the end because some holonyms have use the `:also`
-- modifier, which causes category processing to restart at that point after generating categories for a
-- preceding holonym, and we don't want the preceding holonym's augmented holonyms interfering with
-- categorization of a later holonym. We proceed from right to left, and each time we augment, we copy
-- the holonyms with the augmented holonym(s) inserted appropriately and replace the place description's
-- holonyms with the augmented ones before the next iteration. The reason for this is so that e.g.
-- {{place|neighborhood|city/Birmingham|co/West Midlands|cc/England}} doesn't throw an error during the
-- augmentation process due to 'Birmingham' referring to two known locations (in England and Alabama). If
-- we go left to right, we will throw an ambiguity error on `city/Birmingham` because code to exclude
-- Birmingham, Alabama needs `c/United Kingdom` present (to cause a mismatch with `c/United States`),
-- which isn't yet present as the augmentation code hasn't gotten to `cc/England` yet. For similar
-- reasons, we need to include the augmented holonyms in the holonyms considered in the next iteration
-- rather than modifying the place description once at athe end.
for i = #place_desc.holonyms, 1, -1 do
local holonym = place_desc.holonyms[i]
if holonym.placetype and not export.placetype_is_ignorable(holonym.placetype) then
local group, key, spec, container_trail = export.find_matching_holonym_location {
holonym_placetype = holonym.placetype,
holonym_placename = holonym.unlinked_placename,
holonym_index = i,
place_desc = place_desc,
}
if group and container_trail[1] and not spec.no_auto_augment_container then
local augmented_holonyms = {}
for j = 1, i do
insert(augmented_holonyms, place_desc.holonyms[j])
end
for _, containers in ipairs(container_trail) do
local any_no_auto_augment_container = false
for _, container in ipairs(containers) do
any_no_auto_augment_container = any_no_auto_augment_container or
container.spec.no_auto_augment_container
local containing_type = container.spec.placetype
if type(containing_type) == "table" then
-- If the containing type is a list, use the first element as the canonical variant.
containing_type = containing_type[1]
end
local full_container_placename, elliptical_container_placename =
m_locations.key_to_placename(container.group, container.key)
-- Don't side-effect holonyms while processing them.
local new_holonym = {
-- By the time we run, the display has already been generated so we don't need to
-- set display_placename.
placetype = containing_type,
-- placename_to_key() for the group should correctly handle both full and elliptical
-- placenames, but the full placename seems less likely to be ambiguous. FIXME: We
-- should just store the key directly and use it when available to avoid having to
-- convert key to placename and back to key.
unlinked_placename = full_container_placename,
-- Indicate that this is an augmented holonym, and was derived from the specified
-- holonym. In iterate_matching_holonym_location(), we ignore augmented holonyms
-- derived from holonyms that are different from the holonym we're searching for but
-- of the same placetype. This is to correctly handle a situation like
-- {{place|river|dept/Ardèche,Gard,Vaucluse,Bouches-du-Rhône|c/France}}. Here,
-- `Ardèche` is in `r/Auvergne-Rhône-Alpes`, while `Gard` is in `r/Occitania` and
-- the other two are in `r/Provence-Alpes-Côte d'Azur`. Augmenting proceeds from
-- right to left, so after it adds `r/Provence-Alpes-Côte d'Azur` to
-- `Bouches-du-Rhône`, Vaucluse gets augmented correctly but `Gard` fails to match
-- in find_matching_holonym_location() because of the mismatch between augmented
-- `r/Provence-Alpes-Côte d'Azur` and actual `r/Occitania`. Similarly, all later
-- calls to find_matching_holonym_location() fail to match `Gard` (and likewise
-- `Ardèche`) against any known location. To deal with this, we mark augmented
-- holoynms as being augmented due to a source holonym, and when processing a given
-- holonym, ignore augmented holonyms from other holonyms of the same placetype.
-- The restriction to the same placetype is so that `Birmingham` still gets
-- correctly disambiguated to Birmingham, England in the example given above near
-- the top of this function, using the augmented holonym `c/United Kingdom` added by
-- the specified `cc/England` (whose placetype `constituent country` differs from
-- the placetype `city` of Birmingham).
augmented_from_holonym = holonym,
}
insert(augmented_holonyms, new_holonym)
-- But it is safe to modify other parts of the place_desc.
export.key_holonym_into_place_desc(place_desc, new_holonym)
end
if any_no_auto_augment_container then
break
end
end
for j = i + 1, #place_desc.holonyms do
insert(augmented_holonyms, place_desc.holonyms[j])
end
place_desc.holonyms = augmented_holonyms
end
end
end
end
end
end
-- Cat handler for district, areas, neighborhoods and suburbs. Districts are tricky because they can either be political
-- divisions or city neighborhoods. Areas similarly can be political divisions (rarely; specifically, in Kuwait), city
-- neighborhoods or larger geographical areas/regions. We handle this as follows:
-- (1) `placetype_data` cat entries for specific countries or country divisions take precedence over cat_handlers, so if
-- the user says {{tl|place|district|s/Maharashtra|c/India}}, we won't even be called because there is an entry that
-- categorizes into [[:Category|Districts of Maharashtra, India]].
-- (2) If we're called, we check the holonym we're called on to see if it is a recognized city, e.g. if we're called
-- using {{tl|place|district|city/Mumbai|s/Maharashtra|c/India}}. If so, we categorize under e.g.
-- [[:Category:Neighbourhoods of Mumbai]]. (Choosing the spelling "neighbourhoods" because we're in India.)
-- (3) If we're called and the holonym is not a recognized city, we check if the placetype has has_neighborhoods set.
-- If so, it's "city-like" and we categorize under the first containing polity that we recognize. For example, if
-- we're called using {{tl|place|district|town/Northampton|co/Hampshire|s/Massachusetts|c/US}}, we should recognize
-- town as "city-like" and categorize under [[:Category:Neighborhoods in Massachusetts]]. (Note "in" not "of", and
-- note the spelling "neighborhoods" because we're in the US.)
-- (4) If the holonym is not city-like, we do nothing. If there's a city or city-like placetype farther up (e.g. we're
-- called as {{tl|place|district|ward/Foo|mun/Bar|...}}), we will handle the city-like entity according to (2) or
-- (3) when called on that holonym. Otherwise either the categorization in (1) takes place or there's no
-- categorization.
local function district_neighborhood_cat_handler(data)
local function get_plural_entry_placetype(location_spec, container_trail)
if data.entry_placetype == "suburb" then
return "Suburbs"
else
-- Check for `british_spelling` setting on the spec itself or any container.
local uses_british_spelling = location_spec.british_spelling
if uses_british_spelling == nil and container_trail then
for _, container_set in ipairs(container_trail) do
local must_outer_break = false
for _, container in ipairs(container_set) do
if container.spec.british_spelling ~= nil then
uses_british_spelling = container.spec.british_spelling
must_outer_break = true
break
end
end
if must_outer_break then
break
end
end
end
return uses_british_spelling and "Neighbourhoods" or "Neighborhoods"
end
end
-- First check the immediate holonym to see if it's a city or a city-like top-level entity (Hong Kong, Bonaire,
-- etc.)
local group, key, spec, container_trail = export.find_matching_holonym_location(data)
if group and not spec.is_former_place and spec.is_city then
return {get_plural_entry_placetype(spec, container_trail) .. " of " .. export.get_prefixed_key(key, spec)}
end
-- If the entry placetype is neighbo(u)rhood, assume it is a neighborhood even if there isn't a city-like
-- entity father up the chain. (E.g. due to a mistaken use of m/ instead of mun/ for municipality.)
local has_neighborhoods
local entry_placetype = data.entry_placetype
if entry_placetype == "neighborhood" or entry_placetype == "neighbourhood" or entry_placetype == "suburb" then
has_neighborhoods = true
else
-- Otherwise, make sure the current holonym is city-like.
has_neighborhoods = export.get_equiv_placetype_prop(data.holonym_placetype, function(pt)
return export.get_placetype_prop(pt, "has_neighborhoods")
end, {continue_on_nil_only = true})
end
if has_neighborhoods then
-- Loop up the holonyms, looking for city and city-like entities in case of e.g. [[Sepulveda]] written
-- {{place|en|neighborhood|valley/San Fernando Valley|city/Los Angeles|s/California|c/USA}}
-- but also look for a recognizable poldiv, and if so categorize as "Neighborhoods in POLDIV". We need
-- to start with the current holonym, which is especially important for neighborhoods and suburbs that
-- may have the first holonym be a recognizable province, etc. but can't hurt otherwise. (Previously
-- we skipped the first/current holonym.)
for other_holonym_index, other_holonym in export.get_holonyms_to_check(data.place_desc,
data.holonym_index) do
local other_holonym_data = {
holonym_placetype = other_holonym.placetype,
holonym_placename = other_holonym.unlinked_placename,
holonym_index = other_holonym_index,
place_desc = data.place_desc,
}
local group, key, spec, container_trail = export.find_matching_holonym_location(other_holonym_data)
if group and not spec.is_former_place then
return {get_plural_entry_placetype(spec, container_trail) .. (spec.is_city and " of " or " in ") ..
export.get_prefixed_key(key, spec)}
end
end
end
end
function export.check_already_seen_string(holonym_placename, already_seen_strings)
local canon_placename = ulower(m_links.remove_links(holonym_placename))
if type(already_seen_strings) ~= "table" then
already_seen_strings = {already_seen_strings}
end
for _, already_seen_string in ipairs(already_seen_strings) do
if canon_placename:find(already_seen_string) then
return true
end
end
return false
end
-- Prefix display handler that adds a prefix such as "Metropolitan Borough of " to the display
-- form of holonyms. We make sure the holonym doesn't contain the prefix or some variant already.
-- We do this by checking if any of the strings in ALREADY_SEEN_STRINGS, either a single string or
-- a list of strings, or the prefix if ALREADY_SEEN_STRINGS is omitted, are found in the holonym
-- placename, ignoring case and links. If the prefix isn't already present, we create a link that
-- uses the raw form as the link destination but the prefixed form as the display form, unless the
-- holonym already has a link in it, in which case we just add the prefix.
local function prefix_display_handler(prefix, holonym_placename, already_seen_strings)
if export.check_already_seen_string(holonym_placename, already_seen_strings or ulower(prefix)) then
return holonym_placename
end
if holonym_placename:find("%[%[") then
return prefix .. " " .. holonym_placename
end
return prefix .. " [[" .. holonym_placename .. "]]"
end
-- Suffix display handler that adds a suffix such as " parish" to the display form of holonyms.
-- Works identically to prefix_display_handler but for suffixes instead of prefixes.
local function suffix_display_handler(suffix, holonym_placename, already_seen_strings, include_suffix_in_link)
if export.check_already_seen_string(holonym_placename, already_seen_strings or ulower(suffix)) then
return holonym_placename
end
if holonym_placename:find("%[%[") then
return holonym_placename .. " " .. suffix
end
if include_suffix_in_link then
return "[[" .. holonym_placename .. " " .. suffix .. "]]"
else
return "[[" .. holonym_placename .. "]] " .. suffix
end
end
-- Display handler for boroughs. New York City boroughs are display as-is. Others are suffixed
-- with "borough".
local function borough_display_handler(holonym_placetype, holonym_placename)
local unlinked_placename = m_links.remove_links(holonym_placename)
if m_locations.new_york_boroughs[unlinked_placename] then
-- Hack: don't display "borough" after the names of NYC boroughs
return holonym_placename
end
return suffix_display_handler("borough", holonym_placename)
end
local function county_display_handler(holonym_placetype, holonym_placename)
local unlinked_placename = m_links.remove_links(holonym_placename)
-- Display handler for Irish counties. Irish counties are displayed as e.g. "County [[Cork]]".
if m_locations.ireland_counties["County " .. unlinked_placename .. ", Ireland"] or
m_locations.northern_ireland_counties["County " .. unlinked_placename .. ", Northern Ireland"] then
return prefix_display_handler("County", holonym_placename)
end
-- Display handler for Taiwanese counties. Taiwanese counties are displayed as e.g. "[[Chiayi]] County".
if m_locations.taiwan_counties[unlinked_placename .. " County, Taiwan"] then
return suffix_display_handler("County", holonym_placename)
end
-- Display handler for Romanian counties. Romanian counties are displayed as e.g. "[[Cluj]] County".
if m_locations.romania_counties[unlinked_placename .. " County, Romania"] then
return suffix_display_handler("County", holonym_placename)
end
-- FIXME, we need the same for US counties but need to key off the country, not the specific county.
-- Others are displayed as-is.
return holonym_placename
end
-- Display handler for prefectures. Japanese prefectures are displayed as e.g. "[[Fukushima]] Prefecture".
-- Others are displayed as e.g. "[[Fthiotida]] prefecture".
local function prefecture_display_handler(holonym_placetype, holonym_placename)
local unlinked_placename = m_links.remove_links(holonym_placename)
local suffix = m_locations.japan_prefectures[unlinked_placename .. " Prefecture, Japan"] and "Prefecture" or "prefecture"
return suffix_display_handler(suffix, holonym_placename)
end
-- Display handler for provinces of Bulgaria, Iran, Laos, North and South Korea, Thailand, Turkey and Vietnam. Recognized
-- provinces are displayed as e.g. "[[Gyeonggi]] Province" or "[[Antalya]] Province". Others are displayed as-is.
local function province_display_handler(holonym_placetype, holonym_placename)
local unlinked_placename = m_links.remove_links(holonym_placename)
if
m_locations.bulgaria_provinces[unlinked_placename .. "Province, Bulgaria"] or
m_locations.iran_provinces[unlinked_placename .. " Province, Iran"] or
m_locations.laos_provinces[unlinked_placename .. " Province, Laos"] or
m_locations.north_korea_provinces[unlinked_placename .. " Province, North Korea"] or
m_locations.south_korea_provinces[unlinked_placename .. " Province, South Korea"] or
m_locations.thailand_provinces[unlinked_placename .. " Province, Thailand"] or
m_locations.turkey_provinces[unlinked_placename .. " Province, Turkey"] or
m_locations.vietnam_provinces[unlinked_placename .. " Province, Vietnam"] then
return suffix_display_handler("Province", holonym_placename)
end
return holonym_placename
end
-- Display handler for Nigerian states. Nigerian states are display as "[[Kano]] State". Others are displayed as-is.
local function state_display_handler(holonym_placetype, holonym_placename)
local unlinked_placename = m_links.remove_links(holonym_placename)
if m_locations.nigeria_states[unlinked_placename .. " State, Nigeria"] then
return suffix_display_handler("State", holonym_placename)
end
return holonym_placename
end
-- Display handler for voivodeships. Display as e.g. [[Subcarpathian Voivodeship]].
local function voivodesip_display_handler(holonym_placetype, holonym_placename)
return suffix_display_handler("Voivodeship", holonym_placename, nil, "include_suffix_in_link")
end
------------------------------------------------------------------------------------------
-- Placetype data --
------------------------------------------------------------------------------------------
--[==[ var:
Main placetype data structure. This specifies, for each canonicalized placetype, various properties. The keys are
placetypes (in the singular, except for category-only placetypes, which are plural and followed by `!`), and the value
is a table of properties. The `"*"` key is special and is used for adding "generic" categories of the form
`Places in ``location`` `; it runs for all entry placetypes. Keys in the form of plural placetypes followed by `!` are
used only in [[Module:category tree/topic cat/data/Places]] for specifying the properties of categories containing the
specified placetype, esp. bare categories like [[:Category:States and territories]] (rather than qualified categories
like [[:Category:States and territories of Australia]]).
Keys under the value table for a given placetype of are two types: ''property keys'' (which specify the value of
specific properties) and ''categorization keys'' (which tell how to categorize certain sorts of holonyms if the
placetype in question occurs as an entry placetype). Categorization keys are either the special value `default` or are
wildcard strings with a slash in them, such as `"country/*"`. Note that only wildcard strings are currently allowed
directly in the placetype data; everything else is handled through category handlers, either per-placetype or special
(such as `political_division_cat_handler`). The algorithm for how category keys and handlers are used to generate
categories is described at the top of [[Module:place]].
There are several recognized property keys, of various types:
1. The following link-related property keys are recognized:
* `link`: '''Required''' except in category-only placetypes ending in `!`. Describes how to link and display the
placetype in the formatted description when occurring as an entry placetype. Also used for formatting pluralized
placetypes (which may occur in entry placetypes, esp. new-format ones, such as `two <<islands>>`) and may occur in
categories). The possible values are:
*# `true`: Link to the same-named Wiktionary entry. This creates a raw link, e.g. `<nowiki>[[city]]</nowiki>`, which is
converted to an English-specific link by JavaScript postprocessing. If the placetype is plural, this creates a
two-part raw link e.g. `<nowiki>[[city|cities]]</nowiki>`.
*# `"w"`: Link to the same-named Wikipedia entry. This creates a two-part link, e.g.
`<nowiki>[[w:census town|census town]]</nowiki>`, or `<nowiki>[[w:census town|census towns]]</nowiki>` if the
placetype is given plural.
*# `"+..."`: Create a two-part link to the entry following the `+` sign. For example, if `cercle` specifies
`"+w:cercles of Mali"`, a two-part link `<nowiki>[[w:cercles of Mali|cercle]]</nowiki>` will be generated, or
`<nowiki>[[w:cercles of Mali|cercles]]</nowiki>` if plural `cercles` is specified.
*# `"separately"`: Link each word separately. For example, if `administrative territory` specifies `"separately"`, it
will be linked as `<nowiki>[[administrative]] [[territory]]</nowiki>`, or as
`<nowiki>[[administrative]] [[territory|territories]]</nowiki>` if plural `administrative territories` is given.
*# another string: Use that string directly. If the placetype is plural, `pluralize()` in [[Module:en-utilities]] is
called on the string, which will correctly pluralize most strings, including those with links in them. (If there
are multiple links, the display form of the last link is pluralized.)
*# `false`: This placetype is not allowed as an entry placetype. An error will be thrown if this placetype is given as
an entry placetype. This is specified for internal-use placetypes, especially placetypes used in conjunction with
the qualifiers `former`, `ancient`, `historical` and such.
* `plural_link`: If specified and the placetype is plural, use the value in place of generating a pluralized version of
the link spec in `link`. Most commonly, this is either a string with links in it (which is used directly) or the
value `false`, indicating that the placetype cannot occur plural. (This is used for example by `caplc`, which displays
as `<nowiki>[[capital]] and [[large]]st [[city]]</nowiki>`, where a plural version doesn't make sense.) Generally if
this is specified, `plural` also needs to be specified to give a special placetype plural; this situation occurs
especially with multiword placetypes where something other than the last word is pluralized. An example is
`town with bystatus`, whose plural is `towns with bystatus`, which needs to be explicitly given. This example uses
`link = <nowiki>"[[town]] with [[bystatus#Norwegian Bokmål|bystatus]]"</nowiki>` ({{m|nb|bystatus}}) is a Norwegian
Bokmål word, and template calls aren't currently permitted in link strings), along with
`plural_link = <nowiki>"[[town]]s with [[bystatus#Norwegian Bokmål|bystatus]]"</nowiki>`.
* `category_link`: Spec indicating how to display the placetype when occurring in category descriptions. Defaults to
the value of `link`, and in turn is overridden by more specific `category_link_*` keys; see below. Category-only
placetypes (which are plural and end in `!`) usually use `category_link` in preference to `link`. The value of
`category_link` can be any of the types of specs given above, but most commonly is a plural string with links in it,
spelling out the description; in this case it is used directly. When both `category_link` and `link` are given, the
value in `category_link` is typically longer and more descriptive. For example, `polity` uses `link = true`, which
just generates a link `<nowiki>[[polity]]</nowiki>` or plural `<nowiki>[[polity|polities]]</nowiki>`, but specifies a
separate `category_link = <nowiki>"[[independent]] or [[semi-]][[independent]] [[polity|polities]]"</nowiki>`, which
clarifies in the category description what a polity is.
* `category_link_top_level`: Spec indicating how to display top-level (bare/unqualified) categories, i.e. categories
where the placetype is not followed by `in ``location`` ` or `of ``location`` `. If given, this overrides
`category_link` for this type of category.
* `category_link_before_noncity`: Spec indicating how to display qualified categories of the form
` ``placetypes`` in/of ``location`` ` where ``location`` does not refer to a city. If given, this overrides
`category_link` for this type of category.
* `category_link_before_city`: Spec indicating how to display qualified categories of the form
` ``placetypes`` in/of ``location`` ` where ``location`` refer to a city. If given, this overrides `category_link` for
this type of category. An example where this is given is `neighborhood`, which uses the following specs:<ol>
<li>`link = true`</li>
<li>`category_link = <nowiki>"[[neighborhood]]s, [[district]]s and other subportions of [[city|cities]]"</nowiki>`</li>
<li>`category_link_before_city = <nowiki>"[[neighborhood]]s, [[district]]s and other subportions"</nowiki>`</li>
</ol> This has the effect of making the entry placetype `neighborhood` display as just
`<nowiki>[[neighborhood]]</nowiki>`, while e.g. a category like `Neighborhoods of Chicago` displays as
`<nowiki>[[neighborhood]]s, [[district]]s and other subportions of [[Chicago]], ...</nowiki>` and a category like
`Neighborhoods in Illinois, USA` displays as
`<nowiki>[[neighborhood]]s, [[district]]s and other subportions of [[city|cities]] in [[Illinois]], ...</nowiki>`.
* `disallow_in_entries`: If specified, this placetype cannot occur as an entry placetype, and the specified value
(a message indicating what to use instead) is displayed in the error message.
* `disallow_in_holonyms`: If specified, this placetype cannot occur as a holonym placetype, and the specified value
(a message indicating what to use instead) is displayed in the error message.
2. There is currently one fallback-related property key recognized:
* `fallback`: If specified, its value is a placetype which will be used for categorization purposes if no categories
get added using the placetype itself. As an example, `branch` sets a fallback of `river` but also sets
`preposition = "of"`, meaning that {{tl|place|en|branch|riv/Mississippi}} displays as `a branch of the Mississippi`
(whereas `river` itself uses the preposition `in`), but otherwise categorizes the same as `river`. A more complex
example is `area`, which sets a fallback of `geographic and cultural area` and also sets a category handler that
checks for cities or city-like entities (e.g. boroughs) occurring as holonyms and categorizes the toponym under
[[:Category:Neighborhoods of CITY]] (for recognized cities) or otherwise [[:Category:Neighborhoods of POLDIV]] (for
the nearest containing recognized location). In addition, `area` is set as a political division of Kuwait, meaning if
`c/Kuwait` occurs as holonym, the toponym is categorized under [[:Category:Areas of Kuwait]]. If none of these
categories trigger, the fallback of `geographic and cultural area` will take effect, and the toponym will be
categorized as e.g. [[:Category:Geographic and cultural areas of England]].
3. There is currently one property to control irregular plurals of placetypes:
* `plural`: If specified, its value is the plural of the placetype. Otherwise, the default pluralization algorithm in
[[Module:en-utilities]] applies (which correctly pluralizes most words, including those ending in `-y`, `-ch`, `-sh`,
`-x`, etc.). The value of `plural` is also used when converting a pluralized placetype into its singular equivalent;
for example, since the placetype `kibbutz` has `plural = "kibbutzim"`, the placetype `kibbutzim` will be recognized
as a plural and singularized to `kibbutz`. For this reason, it's occasionally necessary to specify a `plural` value
even when the default pluralization algorithm works correctly, if the default singularization algorithm won't
correctly reverse the pluralization (as with `pass` and other terms ending in `-ss`).
4. The following property keys relate to generating categories for entry placetypes and specifying the parents of those
categories:
* `class`: The general class of placetype. This is used for various purposes: (a) to categorize placetypes preceded by
a qualifier such as `former`, `ancient`, `medieval` or `historical` (note that these placetypes are not all treated
alike); (b) to determine the parent category of bare placetype categories (e.g. [[:Category:Villages]] for placetype
`village`); (c) to determine whether to add a parent category `political divisions of specific countries` to
qualified placetype categories (e.g. [[:Category:Villages in Mali]]). The possible values are:
*# `polity`: a more-or-less sovereign/independent polity, such as a country, kingdom or empire.
*# `subpolity`: a non-sovereign division of a polity, above the level of an individual settlement.
*# `settlement`: a city or smaller equivalent, such as a village. This also includes administrative divisions of a
settlement, such as wards and barangays.
*# `non-admin settlement`: similar to a settlement but without administrative or political significance, such as an
unincorporated community, farm or neighborhood.
*# `capital`: a settlement that is a capital. A former capital is generally still in existence, just not the capital
any more.
*# `natural feature`: any non-man-made feature, such as a lake, mountain, island, ocean, etc.
*# `man-made structure`: a man-made feature below the level of a neighborhood, such as a house, airport, university,
metro station, park or the like.
*# `geographic region`: a geographic or cultural region or area that has no administrative significance. These may vary
greatly in size but typically have some sort of cultural significance (possibly historical). The `former`, `ancient`,
etc. qualifier has no effect on the category of these placetypes.
*# `generic place`: a place that isn't further qualified into any specific subtype.
* `former_type`: The class of placetype used for categorizing placetypes preceded by a qualifier such as `former`,
`ancient`, `medieval` or `historical`. The possible values are the same as for `class` but with the addition of
`dependent territory` (for colonies, protectorates and the like) and `!` (ignore the historical/former/ancient/etc.
qualifier; used e.g. with `fictional location` and `mythological location`). If not specified, the value of `class`
is used. When a qualifier such as `former`, `ancient`, `medieval` or `historical` is encountered (specifically, those
in `former_qualifiers`), it is mapped using `former_qualifiers` to the appropriate internal qualifier or qualifiers
(one or both of `ANCIENT` and/or `FORMER`, which are written in all-caps to distinguish them from user-specified
qualifiers), which is prepended to the value of `former_type` or `class` to form a placetype whose properties are
looked up to determine how to categorize the toponym in question. For example, if `medieval village` is given, we map
`medieval` to `ANCIENT` and `FORMER`, and `village` to its `class` of `settlement`, and enter the placetypes
`ANCIENT settlement` and `FORMER settlement` (in that order) into the list of equivalent placetypes returned by
`get_placetype_equivs`. In this case, there is an entry in `placetype_data` for `ANCIENT settlement`, so its default
category spec `Ancient settlements` is used as the category. If on the other hand `medieval kingdom` is given, where
`kingdom` has a `class` value `polity`, we first look up `ANCIENT polity`, see there is no entry in `placetype_data`
for it, and then look up `FORMER polity`, which exists and has a default category spec `Former polities`, which is
used as the category. Note that if the placetype following the "former" qualifier is recognized in `placetype_data`
but has no `former_type` or `class` and no fallback with a `former_type` or `class` specified, it is an internal
error; but if the placetype isn't recognized (e.g. something like `former greenhouse` is specified and we don't have
an entry for `greenhouse`), we just track the occurrence and end up not categorizing.
* `bare_category_parent`: This specifies the first parent category of a bare placetype category named according to the
placetype in question (e.g. [[:Category:Atolls]] for placetype `atoll`, or [[:Category:Named buildings]] for
placetype `named buildings!`). If not specified, the first parent category is determined by the value of `class`,
using the mapping `class_to_bare_category_parent` in [[Module:category tree/topic cat/data/Places]].
* `addl_bare_category_parents`: Extra parent categories to add a bare placetype category to (see `bare_category_parent`
just above).
* `bare_category_breadcrumb`: Breadcrumb for bare placetype categories. Also used as the sort key of
`bare_category_parent` if it is a string.
* `inherently_former`: If specified and the given placetype is used as an entry placetype, act as if `former` or
`ancient` (depending on the value of `inherently_former`) were prefixed to the placetype. This is for placetypes that
always refer to no-longer-existing entities, such as `satrapy` and `treaty port`. The value of `inherently_former` is
a list of internal qualifiers (one or more of `ANCIENT` and/or `FORMER`), just as for `former_qualifiers`, and the
implementation is the same.
* `cat_handler`: Handler used to generate the categories to add a given toponym to, if its entry placetype is the
placetype in question. Generally the `cat_handler` function checks the holonyms specified in order to determine which
category or categories to generate. For example, `district_neighborhood_cat_handler` handles placetypes `district`,
`neighborhood`, `subdivision`, `suburb` and the like, and either adds the toponym to a category like
`Neighborhoods of ``city`` ` (if a recognized city is given as a holonym), or otherwise a category like
`Neighborhoods in ``location`` ` (for the first recognized non-city location given as a holonym, if an unrecognized
city or city-like entity is given before the recognized non-city). The algorithm that runs the category handlers
iterates over holonyms from left to right, running the `cat_handler` function on each holonym in turn until one or
more categories are returned; see below for more specifics. (Note that countries for which e.g. a `district` is a
political division do not get the corresponding category added by the `district_neighborhood_cat_handler` function but
by `political_division_cat_handler`.) `cat_handler` functions are called with one argument, `data`, describing the
resolved entry placetype (i.e. after resolving placetype aliases and fallbacks) and the holonym being processed. The
return value should be a list of category specs (categories minus the langcode prefix, with `+++` standing for the
holonym key, or the value `true`, which stands for ` ``Placetypes`` in/of ``Holonym`` `, i.e. the pluralized placetype
with the appropriate preposition as specified in `placetype_data`). `data` contains the following fields:
** `entry_placetype`: the resolved entry placetype for the entry placetype being processed (i.e. it will always have an
entry in `placetype_data` but may not be the original placetype given by the user);
** `holonym_placetype` and `holonym_placename`: the holonym placetype and placename being processed;
** `holonym_index`: the index of the holonym being processed, or {nil} if we're handling an overriding holonym (FIXME:
we will change the overriding holonym algorithm so there will be an index even when processing overriding holonyms);
** `place_desc`: a full description of the {{tl|place}} call, as specified at the top of [[Module:place]];
** `from_demonym`: If set, we are called from [[Module:demonym]], triggered by {{tl|demonym-adj}} or
{{tl|demonym-noun}}, instead of being triggered by {{tl|place}}.
* `has_neighborhoods`: If `true`, the specified placetype is city-like. This is used in the
`district_neighborhood_cat_handler` to determine whether to add a category such as `Neighborhoods in ``location`` `;
see the section just above on `cat_handler`.
5. The following preposition-related property keys are recognized:
* `preposition`: The preposition used after this placetype when it occurs as an entry placetype. Defaults to `"in"`.
* `generic_before_non_cities`: If specified, the appropriate category description handler in
[[Module:category tree/topic cat/data/Places]] will recognize categories of the form
` ``Placetype`` in/of ``location`` ` for the specified placetype and preposition, if ``location`` is a non-city. This
is used to generate descriptions for categories added by category handlers and by explicit category specs in the
placetype data. All placetypes that specify `generic_before_non_cities` or `generic_before_cities` *MUST* also specify
a value for `class` so that the category tree code can determine whether it's a political or non-political division.
* `generic_before_cities`: Like `generic_before_non_cities` but for locations referring to cities.
6. The following property keys control the auto-addition of affixes when formatting holonyms of a particular placetype:
* `affix_type`: If specified, add the placetype as an affix before or after holonyms of this placetype. Possible values
are:
*# `"pref"` (the holonym will display as `(the) placetype of Holonym`, where `the` appears when the holonym directly
follows an entry placetype);
*# `"Pref"` (same as `"pref"` but the placetype is capitalized; each word is capitalized if there are multiple);
*# `"suf"` (the holonym will display as `Holonym placetype`);
*# `"Suf"` (the holonym will display as `Holonym Placetype`, i.e. same as `"suf"` but the placetype is capitalized).
* `suffix`: String to use in place of the placetype itself when the placetype is displayed as a suffix after a holonym.
Note that `suffix` can be used independently of `affix_type` because the user can also request a suffix explicitly
using a syntax like `adr:suf/Occitania`, which will display as `Occitania region` because the placetype
`administrative region` specifies `suffix = "region"`.
* `prefix`: Like `suffix` but for use when the placetype is displayed as a prefix before the holonym.
* `affix`: Like `suffix` and `prefix` but for use when the placetype is displayed as an affix either before or after the
holonym. If both `suffix` or `prefix` and `affix` are given for a single placetype, `suffix` or `prefix` take
precedence.
* `no_affix_strings`: String or list of strings that, if they occur in the holonym, suppress the addition of any affix
requested using `affix_type`. Defaults to the placetype itself. For example, `autonomous okrug` specifies
`affix_type = "Suf"` so that `aokr/Nenets` displays as `Nenets Autonomous Okrug`, but also specifies
`no_affix_strings = "okrug"` so that `aokr/Nenets Okrug` or `aokr/Nenets Autonomous Okrug` displays as specified,
without a redundant `Autonomous Okrug` added. Matching is case-insensitive but whole-word.
* `display_handler`: A function of two arguments, `holonym_placetype` and `holonym_placename` (specifying a holonym).
Its return value is a string specifying the display form of the holonym.
7. The following property keys control the indefinite and definite articles used before entry placetypes and/or holonyms
of the specified placetype.
* `entry_placetype_use_the`: Use `"the"` before this placetype when it occurs as an entry placetype.
* `entry_placetype_indefinite_article`: Indefinite article used before this placetype when it occurs as an entry
placetype (usually `"a"`, specifically for placetypes beginning with u- that don't take the indefinite article
`"an"`). Defaults to the appropriate indefinite article (`"a"` or `"an"` depending on whether the placetype begins
with a vowel). Overridden by `entry_placetype_use_the`, and unlike for most properties, does not apply to equivalent
placetypes (i.e. fallbacks or those formed by removing a qualifier from the beginning); only to the exact placetype
specified.
* `holonym_use_the`: Use `"the"` before holonyms of this placetype.
'''NOTE:'''
# The `link` property must be specified on all placetypes, except those ending in `!` (category-only placetypes), which
must have either `link` or `category_link` specified.
# Either the `class` or `former_type` property must be specified on all placetypes not ending in `!` that do not have a
fallback (if a placetype has a fallback and omits the `class` and `former_type` properties, they are taken from the
fallback). An internal error will result if a placetype has no `class` or `former_type` property derivable either
directly or through a fallback, if an attempt is made to categorize a former/ancient/historical/etc. entity of this
placetype.
# It is possible to have multiple levels of fallback (e.g. `frazione` falls back to `hamlet`, which falls back
to `village`). Fallback loops will cause an internal error. All placetypes specified as fallbacks must exist in
`placetype_data` or an internal error occurs.
]==]
export.placetype_data = {
--[=[
If you need to sort the following, do this (using Vim):
1. Make sure all full-line comments are within the { ... } table, or are moved after and on the same line as single-line
entries.
2. Make sure the table uses tabs everywhere for indent, and not spaces.
3. Mark the top of the table with `ma`, go to the bottom and execute the following two lines in sequence:
:'a,.s/\n/\\n/g
:s/\\n\(\t\[\)/\r\1/g
The first command converts every newline to a literal `\n` sequence, so the whole thing becomes a single line, while
the second command restores the newlines before the beginning of each entry. The effect is to convert all entries to
a single line while not losing any information. (Potentially a negative lookahead could be used to do it all in one
command.)
4. Execute the following to sort:
:'a,.!perl -pe 's/^(\t\[")(.*?)(".*)$/$2 @@@ $1$2$3/' | sort -f | perl -pe 's/.*? @@@ //'
Note that a simple `sort -f` (where `-f` means case-insensitive) would almost work, but it would sort "hill station"
before "hill" and "county borough" before "county" because the space after e.g. "hill station" sorts before the
quotation mark after e.g. "hill". The above command deals with this by extracting the key, prepending it followed by
` @@@ `, sorting, and then removing key (the classic decorate-sort-undecorate pattern).
5. Put the table back to multi-line format by marking the top of the table with `ma`, going to the bottom and executing
:'a,.s/\\n/\r/g
Note that for some reason, in order to get a match a newline in the left side of a replacement, you must use \n, but
to insert a newline in the right sode of a replacement you must use \r.
]=]
["*"] = {
link = false,
cat_handler = generic_place_cat_handler,
},
["administrative atoll"] = {
-- Maldives
link = "+w:administrative divisions of the Maldives",
preposition = "of",
class = "subpolity",
},
["administrative capital"] = {
link = "w",
fallback = "capital city",
},
["administrative center"] = {
link = "w",
fallback = "non-city capital",
},
["administrative centre"] = {
link = "w",
fallback = "administrative center",
},
["administrative county"] = {
link = "w",
fallback = "county",
},
["administrative district"] = {
link = "w",
fallback = "district",
},
["administrative headquarters"] = {
link = "separately",
fallback = "administrative centre",
},
["administrative region"] = {
link = true,
preposition = "of",
suffix = "region", -- but prefix is still "administrative region (of)"
fallback = "region",
class = "subpolity",
},
["administrative seat"] = {
link = "w",
fallback = "administrative centre",
},
["administrative territory"] = {
link = "separately",
preposition = "of",
suffix = "territory", -- but prefix is still "administrative territory (of)"
fallback = "territory",
class = "subpolity",
},
["administrative unit"] = {
-- Grrr, it's difficult to generalize about "administrative units". In Albania, "administrative unit" is an
-- official term for a city-level division of municipalities; Wikipedia renders it using the more practical term
-- "commune". In Pakistan, "administrative unit" is a collective term used to refer to all the different types
-- of first-level divisions (four provinces, one federal territory, and two "disputed territories", i.e. Azad
-- Kashmir and Gilgit-Balistan, that are variously described). For this reason, we set no fallback, but we need
-- to include this so that it can be used as a placetype for Albania, categorizing as communes.
link = "w",
class = "subpolity",
},
["administrative village"] = {
link = "w",
preposition = "of",
has_neighborhoods = true,
class = "settlement",
},
["aimag"] = {
-- used in Mongolia, Russia and China (Inner Mongolia); in Mongolia, equivalent to a province;
-- in China, equivalent to a prefecture (below a province); in Russia, equivalent to a municipal district.
link = "w",
fallback = "prefecture",
},
["airport"] = {
link = true,
class = "man-made structure",
default = {true},
},
["alliance"] = {
link = true,
fallback = "confederation",
},
["archipelago"] = {
link = true,
fallback = "island",
},
["area"] = {
link = true,
preposition = "of",
fallback = "geographic and cultural area",
-- Areas can either be administrative divisions (specifically of Kuwait) or geographic areas. Assume the former
-- when categorizing 'Areas' but the latter when handling e.g. 'historical area'.
class = "subpolity",
former_type = "geographic region",
cat_handler = district_neighborhood_cat_handler,
},
["arm"] = {
link = true,
preposition = "of",
class = "natural feature",
default = {"Seas"},
},
["arrondissement"] = {
link = true,
preposition = "of",
-- FIXME!!! Grrrrr!!! In some countries, arrondissements are divisions of cities; in others, they are divisions
-- of departments or provinces. Need to conditionalize on the country for both of the following.
class = "subpolity",
has_neighborhoods = true,
},
["associated province"] = {
link = "separately",
fallback = "province",
},
["atoll"] = {
-- FIXME! Atolls are administrative divisions of the Maldives but natural features elsewhere. Need to
-- conditionalize `class` on the country. See also `administrative atoll`.
link = true,
class = "natural feature",
bare_category_parent = "islands",
default = {true},
},
["autonomous city"] = {
link = "w",
preposition = "of",
fallback = "city",
has_neighborhoods = true,
},
["autonomous community"] = {
-- Spain; refers to regional entities, not village-like entities, as might be expected from "community"
link = true,
preposition = "of",
class = "subpolity",
},
["autonomous island"] = {
-- Comoros; seems like an administrative atoll of the Maldives.
link = "+w:autonomous islands of Comoros",
preposition = "of",
class = "subpolity",
},
["autonomous oblast"] = {
link = true,
preposition = "of",
affix_type = "Suf",
no_affix_strings = "oblast",
class = "subpolity",
},
["autonomous okrug"] = {
link = true,
preposition = "of",
affix_type = "Suf",
no_affix_strings = "okrug",
class = "subpolity",
},
["autonomous prefecture"] = {
link = true,
fallback = "prefecture",
},
["autonomous province"] = {
link = "w",
fallback = "province",
},
["autonomous region"] = {
link = "w",
preposition = "of",
fallback = "administrative region",
-- "administrative region" sets an affix of "region" but we want to display as "Tibet Autonomous Region"
-- if the user writes 'ar:Suf/Tibet'.
affix = "autonomous region",
},
["autonomous republic"] = {
link = "w",
preposition = "of",
class = "subpolity",
},
["autonomous territorial unit"] = {
-- Moldova; only two of them, one for Gagauzia and one for Transnistria.
link = "w",
preposition = "of",
class = "subpolity",
},
["autonomous territory"] = {
link = "w",
fallback = "dependent territory",
},
["bailiwick"] = {
-- Jersey, etc.
link = true,
fallback = "polity",
},
["barangay"] = {
-- Philippines
link = true,
class = "settlement",
-- Barangays are formal administrative divisions of a city rather than informal neighborhoods, but can use
-- some of the properties of a neighborhood.
fallback = "neighborhood",
},
["barrio"] = {
-- Spanish-speaking countries; Philippines
link = true,
-- FIXME: Not completely correct, in some countries barrios are formal administrative divisions of a city.
-- `class` will need to conditionalize on the country to be completely correct.
fallback = "neighborhood",
},
["basin"] = {
link = true,
fallback = "lake",
},
["bay"] = {
link = true,
preposition = "of",
class = "natural feature",
addl_bare_category_parents = {"bodies of water"},
default = {true},
},
["beach"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"water"},
default = {true},
},
["beach resort"] = {
link = "w",
fallback = "resort town",
},
["bishopric"] = {
link = true,
fallback = "polity",
},
["bodies of water!"] = {
-- FIXME: This is (maybe?) a type category not a name category. There should be an option for this. We need to
-- straighten out the type vs. name vs. related-to issue.
category_link = "[[body of water|bodies of water]]",
class = "natural feature",
addl_bare_category_parents = {"landforms", "ecosystems", "water"},
},
["borough"] = {
link = true,
preposition = "of",
display_handler = borough_display_handler,
has_neighborhoods = true,
-- "former borough" could be a former settlement or a former part of a city but seems more likely to
-- be a former subpolity, particularly in England. FIXME, we really need a handler to take care of this
-- properly.
class = "subpolity",
-- Grr, some boroughs are city-like but some (e.g. in Britain) may be larger.
},
["borough seat"] = {
link = true,
entry_placetype_use_the = true,
preposition = "of",
has_neighborhoods = true,
class = "capital",
},
["branch"] = {
link = true,
preposition = "of",
fallback = "river",
},
["bridge"] = {
link = true,
class = "man-made structure",
default = {"Named bridges"},
},
["building"] = {
link = true,
class = "man-made structure",
default = {"Named buildings"},
},
["built-up area"] = {
link = "w",
fallback = "area",
},
["burgh"] = {
link = true,
fallback = "borough",
},
["business park"] = {
link = true,
fallback = "park",
},
["caliphate"] = {
link = true,
fallback = "polity",
},
["canton"] = {
link = true,
preposition = "of",
affix_type = "suf",
class = "subpolity",
},
["cape"] = {
link = true,
fallback = "headland",
},
["capital"] = {
link = true,
fallback = "capital city",
},
["capital city"] = {
link = true,
category_link = "[[capital city|capital cities]]: the [[seat of government|seats of government]] for a country or [[political]] [[division]] of a country",
entry_placetype_use_the = true,
preposition = "of",
has_neighborhoods = true,
class = "capital",
bare_category_parent = "cities",
cat_handler = capital_city_cat_handler,
default = {true},
-- The following is necessary so that e.g. [[Melbourne]] defined as {{place|en|capital city|s/Victoria|c/Australia}}
-- gets categorized in the bare category [[Category:en:Melbourne]]; otherwise placetype 'capital city' wouldn't
-- match against the placetype 'city' of Melbourne.
fallback = "city",
},
["caplc"] = {
link = "[[capital]] and [[large]]st [[city]]",
plural_link = false,
fallback = "capital city",
},
["captaincy"] = {
link = true,
preposition = "of",
class = "subpolity",
inherently_former = {"FORMER"},
},
["caravan city"] = {
link = "w",
fallback = "city",
class = "settlement",
inherently_former = {"ANCIENT", "FORMER"},
},
["castle"] = {
link = true,
fallback = "building",
},
["cathedral city"] = {
link = true,
fallback = "city",
},
["cattle station"] = {
-- Australia
link = true,
fallback = "farm",
},
["census area"] = {
link = true,
affix_type = "Suf",
has_neighborhoods = true,
class = "non-admin settlement",
},
["census-designated place"] = {
-- United States
link = true,
class = "non-admin settlement",
},
["census division"] = {
-- Canada
link = "w",
preposition = "of",
class = "subpolity",
},
["census town"] = {
link = "w",
fallback = "town",
},
["central business district"] = {
link = true,
fallback = "neighborhood",
},
["cercle"] = {
-- Mali
link = "+w:cercles of Mali",
preposition = "of",
class = "subpolity",
},
["ceremonial county"] = {
link = true,
fallback = "county",
},
["chain of islands"] = {
link = "[[chain]] of [[island]]s",
plural = "chains of islands",
plural_link = "[[chain]]s of [[island]]s",
fallback = "island",
},
["channel"] = {
link = true,
fallback = "strait",
},
["charter community"] = {
-- Northwest Territories, Canada
link = "w",
fallback = "village",
},
["chiwog"] = {
-- Bhutan
link = true,
preposition = "of",
class = "subpolity",
},
["city"] = {
link = true,
generic_before_non_cities = "in",
has_neighborhoods = true,
class = "settlement",
cat_handler = city_type_cat_handler,
default = {true},
},
["city-state"] = {
link = true,
category_link = "[[sovereign]] [[microstate]]s consisting of a single [[city]] and [[w:dependent territory|dependent territories]]",
has_neighborhoods = true,
class = "settlement",
["continent/*"] = {"City-states", "Cities in +++", "Countries in +++", "National capitals"},
default = {"City-states", "Cities", "Countries", "National capitals"},
},
["civil parish"] = {
-- Mostly England; similar to municipalities
link = true,
preposition = "of",
affix_type = "suf",
has_neighborhoods = true,
class = "subpolity",
},
["claimed political division"] = {
link = "[[claim]]ed [[political]] [[division]]",
class = "subpolity",
default = {true},
},
["co-capital"] = {
link = "[[co-]][[capital]]",
fallback = "capital city",
},
["coal city"] = {
link = "+w:coal town",
fallback = "city",
},
["coal town"] = {
link = "w",
fallback = "town",
},
["collectivity"] = {
link = "w",
preposition = "of",
-- No default; these are weird one-off governmental divisions in France (esp. for overseas collectivities)
class = "subpolity",
},
["colony"] = {
link = true,
fallback = "dependent territory",
},
["comarca"] = {
-- per Wikipedia: traditional region or local administrative division found in Portugal, Spain, and some of
-- their former colonies, like Brazil, Nicaragua, and Panama. In the Valencian Community, for example, it
-- sits between municipalities and provinces, something like a county or district.
link = true,
preposition = "of",
class = "subpolity",
},
["commandery"] = {
link = true,
preposition = "of",
class = "subpolity",
inherently_former = {"ANCIENT", "FORMER"},
},
["commonwealth"] = {
link = true,
preposition = "of",
-- No default; applies specifically to Puerto Rico
class = "subpolity",
},
["commune"] = {
link = true,
fallback = "municipality",
},
["community"] = {
link = true,
category_link = "[[community|communities]] of all sizes",
fallback = "village",
},
["community development block"] = {
-- in India; appears to be similar to a rural municipality; groups several villages, unclear if there will be
-- neighborhoods so I'm not setting `has_neighborhoods` for now
link = "w",
affix_type = "suf",
no_affix_strings = "block",
class = "subpolity",
},
["comune"] = {
-- Italy, Switzerland
link = true,
fallback = "municipality",
},
["condominium"] = {
link = true,
fallback = "polity",
},
["confederacy"] = {
link = true,
fallback = "confederation",
},
["confederation"] = {
link = true,
fallback = "polity",
},
["constituency"] = {
-- currently we have them as political divisions of Namibia but many countries have them
link = true,
preposition = "of",
class = "subpolity",
},
["constituent country"] = {
link = true,
preposition = "of",
class = "subpolity",
},
["constituent part"] = {
link = "separately",
preposition = "of",
class = "subpolity",
},
["constituent republic"] = {
-- Of Russia, Yugoslavia, etc.
link = "separately",
preposition = "of",
class = "subpolity",
},
["counties and county-level cities!"] = {
-- This is used when grouping counties and county-level cities under prefecture-level cities in China.
category_link = "[[county|counties]] and [[county-level city|county-level cities]]",
class = "subpolity",
},
["continent"] = {
link = true,
category_link = false, -- can't occur as a bare category
class = "natural feature",
default = {"Continents and continental regions"},
},
["continental region"] = {
link = "separately",
category_link = false, -- can't occur as a bare category
class = "geographic region",
fallback = "continent",
},
["continents and continental regions!"] = {
category_link = "[[continent]]s and [[continent]]-[[level]] [[region]]s (e.g. [[Polynesia]])",
class = "geographic region",
},
["council area"] = {
link = true,
-- in Scotland; similar to a county
preposition = "of",
affix_type = "suf",
class = "subpolity",
},
["country"] = {
link = true,
class = "polity",
["continent/*"] = {true, "Countries"},
default = {true},
},
["country-like entities!"] = {
category_link = "[[polity|polities]] not normally considered [[country|countries]] but treated similarly for categorization purposes; typically, [[unrecognized]] [[de-facto]] countries or [[w:dependent territory|dependent territories]]",
class = "polity",
},
["county"] = {
link = true,
preposition = "of",
display_handler = county_display_handler,
class = "subpolity",
},
["county borough"] = {
link = true,
-- in Wales; similar to a county
preposition = "of",
affix_type = "suf",
fallback = "borough",
class = "subpolity",
},
["county seat"] = {
link = true,
entry_placetype_use_the = true,
preposition = "of",
has_neighborhoods = true,
class = "capital",
},
["county town"] = {
link = true,
entry_placetype_use_the = true,
preposition = "of",
fallback = "town",
has_neighborhoods = true,
class = "capital",
},
["county-administered city"] = {
-- In Taiwan, per Wikipedia similar to a Taiwanese township or district, which is a small city.
-- NOT anything like a "county-level city" in PR China, which is a county masquerading as a city.
link = "w",
fallback = "city",
has_neighborhoods = true,
class = "settlement",
},
["county-controlled city"] = {
-- Taiwan
link = "w",
fallback = "county-administered city",
},
["county-level city"] = {
-- PR China
link = "w",
fallback = "prefecture-level city",
},
["crater lake"] = {
link = true,
fallback = "lake",
},
["creek"] = {
link = true,
fallback = "stream",
},
["Crown colony"] = {
link = "+crown colony",
fallback = "crown colony",
},
["crown colony"] = {
link = true,
fallback = "colony",
},
["Crown dependency"] = {
link = true,
fallback = "dependent territory",
},
["crown dependency"] = {
link = true,
fallback = "dependent territory",
},
["cultural area"] = {
link = "w",
fallback = "geographic and cultural area",
},
["cultural region"] = {
link = "w",
fallback = "geographic and cultural area",
},
["delegation"] = {
-- Tunisia
link = "+w:delegations of Tunisia",
preposition = "of",
class = "subpolity",
},
["department"] = {
link = true,
preposition = "of",
affix_type = "suf",
class = "subpolity",
},
["departmental capital"] = {
link = "separately",
fallback = "capital city",
},
["dependency"] = {
link = true,
fallback = "dependent territory",
},
["dependent territory"] = {
link = "w",
preposition = "of",
class = "subpolity",
former_type = "dependent territory",
bare_category_parent = "political divisions",
["country/*"] = {true},
default = {true},
},
["desert"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"ecosystems"},
default = {true},
},
["deserted mediaeval village"] = {
link = "w",
fallback = "deserted medieval village",
},
["deserted medieval village"] = {
link = "w",
fallback = "ANCIENT settlement",
},
["direct-administered municipality"] = {
-- China
link = "+w:direct-administered municipalities of China",
fallback = "municipality",
},
["direct-controlled municipality"] = {
-- several countries
link = "w",
fallback = "municipality",
},
["distributary"] = {
link = true,
preposition = "of",
fallback = "river",
},
["district"] = {
link = true,
preposition = "of",
affix_type = "suf",
-- Grrr! FIXME! Here is where we need handlers for `class`. Using similar logic to
-- district_neighborhood_cat_handler, we need to check if we're below or above a city to determine if the class
-- is "settlement" or "subpolity".
class = "subpolity",
cat_handler = district_neighborhood_cat_handler,
-- No default. Countries for which districts are political divisions will get entries.
},
["districts and autonomous regions!"] = {
-- This and other similar "combined placetypes" are for use in the plural when grouping first-level
-- administrative regions of certain countries, in this case Portugal.
category_link = "[[district]]s and [[autonomous region]]s",
class = "subpolity",
},
["districts and autonomous territorial units!"] = {
-- This and other similar "combined placetypes" are for use in the plural when grouping first-level
-- administrative regions of certain countries, in this case Moldova.
category_link = "[[district]]s and [[w:autonomous territorial unit|autonomous territorial unit]]s",
class = "subpolity",
},
["district capital"] = {
link = "separately",
fallback = "capital city",
},
["district headquarters"] = {
link = "separately",
fallback = "administrative centre",
},
["district municipality"] = {
-- In Canada, a district municipality is equivalent to a rural municipality and won't have neighborhoods; in
-- South Africa, district municipalities group local municipalities and hence won't have neighborhoods.
link = "w",
preposition = "of",
affix_type = "suf",
no_affix_strings = {"district", "municipality"},
fallback = "municipality",
class = "subpolity",
},
["division"] = {
link = true,
preposition = "of",
class = "subpolity",
},
["division capital"] = {
link = "separately",
fallback = "capital city",
},
["dome"] = {
link = true,
fallback = "mountain",
},
["dormant volcano"] = {
link = true,
fallback = "volcano",
},
["duchy"] = {
link = true,
fallback = "polity",
},
["emirate"] = {
link = true,
preposition = "of",
-- FIXME: Can be subpolities (of the United Arab Emirates).
fallback = "polity",
},
["empire"] = {
link = true,
fallback = "polity",
},
["enclave"] = {
link = true,
preposition = "of",
-- Enclaves can theoretically be any size but assume a subpolity.
class = "subpolity",
},
["entity"] = {
-- Bosnia and Herzegovina
link = "+w:entities of Bosnia and Herzegovina",
preposition = "of",
class = "subpolity",
},
["escarpment"] = {
link = true,
fallback = "mountain",
},
["ethnographic region"] = {
-- used in Lithuania
link = "+w:ethnographic regions of Lithuania",
fallback = "geographic and cultural area",
},
["exclave"] = {
link = true,
preposition = "of",
-- exclaves can theoretically be any size but assume a subpolity.
class = "subpolity",
},
["external territory"] = {
link = "separately",
fallback = "dependent territory",
},
["farm"] = {
link = true,
class = "non-admin settlement",
default = {"Farms and ranches"},
},
["farms and ranches!"] = {
category_link = "[[farm]]s and [[ranch]]es",
class = "non-admin settlement",
},
["federal city"] = {
link = "w",
preposition = "of",
fallback = "city",
},
["federal district"] = {
link = true,
preposition = "of",
-- Might have neighborhoods as federal districts are often cities (e.g. Mexico City)
has_neighborhoods = true,
class = "settlement",
},
["federal subject"] = {
-- In Russia; a generic term for first-level administrative divisions (republics, oblasts, okrugs, krais,
-- autonomous okrugs and autonomous oblasts).
link = "w",
preposition = "of",
class = "subpolity",
},
["federal territory"] = {
link = "w",
fallback = "territory",
},
["fictional location"] = {
link = "separately",
former_type = "!",
class = "hypothetical location",
bare_category_parent = "places",
default = {true},
},
["First Nations reserve"] = {
-- Canada
link = "[[First Nations]] [[w:Indian reserve|reserve]]",
-- Wikipedia uses "Indian reserve"; presumably that is the legal term
fallback = "Indian reserve",
class = "subpolity",
},
["fjord"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"bodies of water"},
default = {true},
},
["footpath"] = {
link = true,
fallback = "road",
},
["forest"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"ecosystems", "forestry"},
default = {true},
},
["fort"] = {
link = true,
fallback = "building",
},
["fortress"] = {
link = true,
-- The default plural algorithm gets this right but the singularization algorithm incorrectly converts
-- fortresses -> fortresse, so put an entry here to ensure we singularize correctly.
plural = "fortresses",
fallback = "building",
},
["frazione"] = {
link = "w",
fallback = "hamlet",
},
["freeway"] = {
link = true,
fallback = "road",
},
["French prefecture"] = {
link = "[[w:prefectures in France|prefecture]]",
entry_placetype_use_the = true,
preposition = "of",
has_neighborhoods = true,
class = "capital",
},
["geographic and cultural area"] = {
link = "+w:cultural area",
-- `generic_before_non_cities` is used when generating the category description of categories of the format
-- `Geographic and cultural areas of PLACE`. `preposition` is used when generating {{place}} description and
-- categories for any placetype that falls back to `geographic and cultural area`.
generic_before_non_cities = "of",
preposition = "of",
class = "geographic region",
bare_category_parent = "places",
["country/*"] = {true},
["constituent country/*"] = {true},
["continent/*"] = {true},
default = {true},
},
["geographic area"] = {
link = "+w:geographic region",
fallback = "geographic and cultural area",
},
["geographic region"] = {
link = "w",
fallback = "geographic and cultural area",
},
["geographical area"] = {
link = "w",
fallback = "geographic and cultural area",
},
["geographical region"] = {
link = "w",
fallback = "geographic and cultural area",
},
["geopolitical zone"] = {
-- Nigeria
link = true,
preposition = "of",
class = "subpolity",
},
["gewog"] = {
-- Bhutan
link = true,
preposition = "of",
class = "subpolity",
},
["ghost town"] = {
link = true,
generic_before_non_cities = "in",
class = "non-admin settlement",
bare_category_parent = "former settlements",
cat_handler = city_type_cat_handler,
default = {true},
},
["glen"] = {
link = true,
fallback = "valley",
},
["governorate"] = {
link = true,
preposition = "of",
affix_type = "suf",
class = "subpolity",
},
["greater administrative region"] = {
-- China (former division)
link = "w",
preposition = "of",
class = "subpolity",
inherently_former = {"FORMER"},
},
["gromada"] = {
-- Poland (former division)
link = "w",
preposition = "of",
affix_type = "Pref",
class = "subpolity",
inherently_former = {"FORMER"},
},
["group of islands"] = {
link = "[[group]] of [[island]]s",
plural = "groups of islands",
plural_link = "[[group]]s of [[island]]s",
fallback = "island group",
},
["gulf"] = {
link = true,
preposition = "of",
holonym_use_the = true,
class = "natural feature",
addl_bare_category_parents = {"bodies of water"},
default = {true},
},
["hamlet"] = {
link = true,
fallback = "village",
},
["harbor city"] = {
link = "separately",
fallback = "city",
},
["harbor town"] = {
link = "separately",
fallback = "town",
},
["harbour city"] = {
link = "separately",
fallback = "city",
},
["harbour town"] = {
link = "separately",
fallback = "town",
},
["headland"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"landforms"},
default = {true},
},
["headquarters"] = {
link = "w",
fallback = "administrative centre",
},
["heath"] = {
link = true,
fallback = "moor",
},
["hemisphere"] = {
link = true,
entry_placetype_use_the = true,
fallback = "continental region",
},
["highway"] = {
link = true,
fallback = "road",
},
["hill"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"landforms"},
default = {true},
},
["hill station"] = {
link = "w",
fallback = "town",
},
["hill town"] = {
link = "w",
fallback = "town",
},
["historic region"] = {
-- provided only for the link
link = "+w:historical region",
fallback = "FORMER geographic region",
},
["historical county"] = {
-- needed for historical counties of England/etc.
link = "+w:historic county",
fallback = "FORMER subpolity",
},
["historical region"] = {
-- provided only for the link
link = "w",
fallback = "FORMER geographic region",
},
["home rule city"] = {
link = "w",
fallback = "city",
},
["home rule municipality"] = {
link = "w",
fallback = "municipality",
},
["hot spring"] = {
link = true,
fallback = "spring",
},
["house"] = {
link = true,
fallback = "building",
},
["housing estate"] = {
-- not the same as a housing project (i.e. public housing)
link = true,
-- not exactly the case but approximately
fallback = "neighborhood",
},
["hromada"] = {
-- Ukraine
link = "w",
disallow_in_entries = "Use placetype 'urban hromada', 'rural hromada' or 'settlement hromada' in place of bare 'hromada'",
disallow_in_holonyms = "Use placetype 'urban hromada'/'uhrom', 'rural hromada'/'rhrom' or 'settlement hromada'/'shrom' in place of bare 'hromada'",
preposition = "of",
affix_type = "suf",
class = "subpolity",
},
["inactive volcano"] = {
link = "w",
fallback = "dormant volcano",
},
["independent city"] = {
link = true,
fallback = "city",
},
["independent town"] = {
link = "+independent city",
fallback = "town",
},
["Indian reservation"] = {
link = "w",
-- In the US. Also known as "Native American reservation" or "domestic dependent nation", and the reservations
-- themselves often use the term "nation" in their official name (e.g. the "Navajo Nation"). But Wikipedia puts
-- the article at [[w:Indian reservation]] and uses that term when describing e.g. what the Navajo Nation is,
-- so this must still be the legal term.
preposition = "of",
class = "subpolity",
default = {true},
},
["Indian reserve"] = {
link = "w",
-- In Canada. "First Nations reserve" sounds more modern/PC but Wikipedia uses "Indian reserve"; presumably that
-- is still the legal term.
preposition = "of",
class = "subpolity",
default = {true},
},
["inland sea"] = {
-- note, we also have 'inland' as a qualifier
link = true,
fallback = "sea",
},
["inner city area"] = {
link = "[[inner city]] [[area]]",
fallback = "neighborhood",
},
["island"] = {
link = true,
preposition = "of",
class = "natural feature",
addl_bare_category_parents = {"landforms"},
default = {true},
},
["island country"] = {
-- FIXME: The following should map to both 'island' and 'country'.
link = "w",
fallback = "country",
},
["island group"] = {
link = "separately",
fallback = "island",
},
["island municipality"] = {
link = "w",
fallback = "municipality",
},
["islet"] = {
link = "w",
fallback = "island",
},
["Israeli settlement"] = {
link = "w",
class = "settlement",
default = {true},
},
["judicial capital"] = {
link = "w",
fallback = "capital city",
},
["khanate"] = {
link = true,
fallback = "polity",
},
["kibbutz"] = {
link = true,
plural = "kibbutzim",
class = "non-admin settlement",
default = {true},
},
["kingdom"] = {
link = true,
fallback = "monarchy",
},
["krai"] = {
link = true,
preposition = "of",
affix_type = "Suf",
class = "subpolity",
},
["lake"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"bodies of water"},
default = {true},
},
["landforms!"] = {
category_link = "[[landform]]s",
bare_category_parent = "places",
addl_bare_category_parents = {"Earth"},
},
["largest city"] = {
link = "[[large]]st [[city]]",
entry_placetype_use_the = true,
fallback = "city",
has_neighborhoods = true,
},
["league"] = {
link = true,
fallback = "confederation",
},
["legislative capital"] = {
link = "separately",
fallback = "capital city",
},
["library"] = {
link = true,
fallback = "building",
},
["lieutenancy area"] = {
-- used in the United Kingdom; per Wikipedia:
-- In England, lieutenancy areas are colloquially known as the ceremonial counties, although this phrase does
-- not appear in any legislation referring to them. The lieutenancy areas of Scotland are subdivisions of
-- Scotland that are more or less based on the counties of Scotland, making use of the major cities as separate
-- entities.[2] In Wales, the lieutenancy areas are known as the preserved counties of Wales and are based on
-- those used for lieutenancy and local government between 1974 and 1996. The lieutenancy areas of Northern
-- Ireland correspond to the six counties and two former county boroughs.[3]
link = "w",
fallback = "ceremonial county",
},
["local authority district"] = {
link = "w",
fallback = "local government district",
},
["local government area"] = {
-- Australia
link = "w",
preposition = "of",
class = "subpolity",
},
["local council"] = {
-- Malta; similar to municipalities
link = "+w:local councils of Malta",
preposition = "of",
fallback = "municipality",
},
["local government district"] = {
link = "w",
preposition = "of",
affix_type = "suf",
affix = "district",
class = "subpolity",
},
["local government district with borough status"] = {
link = "[[w:local government district|local government district]] with [[w:borough status|borough status]]",
plural = "local government districts with borough status",
plural_link = "[[w:local government district|local government districts]] with [[w:borough status|borough status]]",
preposition = "of",
affix_type = "suf",
affix = "district",
class = "subpolity",
},
["local urban district"] = {
link = "w",
fallback = "unincorporated community",
},
["locality"] = {
link = "+w:locality (settlement)",
-- not necessarily true, but usually is the case
fallback = "village",
},
["London borough"] = {
link = "w",
preposition = "of",
affix_type = "pref",
affix = "borough",
fallback = "local government district with borough status",
has_neighborhoods = true,
},
["macroregion"] = {
link = true,
fallback = "region",
},
["man-made structures!"] = {
category_link = "[[w:geographical feature#Engineered constructs|man-made structures]] such as [[airport]]s, [[university|universities]] and [[metro station]]s",
bare_category_parent = "places",
},
["manor"] = {
-- FIXME: or is this more like a farm?
link = true,
fallback = "building",
},
["marginal sea"] = {
link = true,
preposition = "of",
fallback = "sea",
},
["market city"] = {
link = "+market town",
fallback = "city",
},
["market town"] = {
link = true,
fallback = "town",
},
["massif"] = {
link = true,
fallback = "mountain",
},
["megacity"] = {
link = true,
fallback = "city",
},
["metro station"] = {
link = true,
class = "man-made structure",
},
["metropolitan borough"] = {
link = true,
preposition = "of",
affix_type = "Pref",
no_affix_strings = {"borough", "city"},
fallback = "local government district",
has_neighborhoods = true,
},
["metropolitan city"] = {
-- These exist e.g. in Italy and are more like municipalities or even provinces than cities.
link = true,
preposition = "of",
affix_type = "Pref",
no_affix_strings = {"metropolitan", "city"},
class = "subpolity",
},
["metropolitan county"] = {
link = true,
fallback = "county",
},
["metropolitan municipality"] = {
-- In South Africa, metropolitan municipalities group local municipalities and are like districts, between
-- provinces and municipalities.
-- In Turkey, metropolitan municipalities are provinces-level.
link = "w",
preposition = "of",
affix_type = "Suf",
no_affix_strings = {"metropolitan", "municipality"},
fallback = "municipality",
class = "subpolity",
},
["microdistrict"] = {
-- residential complex in post-Soviet states
link = true,
fallback = "neighborhood",
},
["micronations!"] = {
-- FIXME, merge with microstate
category_link = "[[micronation]]s",
bare_category_parent = "countries",
},
["microstate"] = {
link = true,
fallback = "country",
},
["military base"] = {
link = "w",
class = "settlement", -- or "man-made structure"?
default = {true},
},
["minster town"] = {
-- England
link = "separately",
fallback = "town",
},
["monarchy"] = {
link = true,
fallback = "polity",
},
["moor"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"landforms", "ecosystems"},
default = {true},
},
["moorland"] = {
link = true,
fallback = "moor",
},
["motorway"] = {
link = true,
fallback = "road",
},
["mountain"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"landforms"},
default = {true},
},
["mountain indigenous district"] = {
-- Taiwan
link = "+w:district (Taiwan)",
fallback = "district",
},
["mountain indigenous township"] = {
-- Taiwan
link = "+w:township (Taiwan)",
fallback = "township",
},
["mountain pass"] = {
link = true,
-- The default plural algorithm gets this right but the singularization algorithm incorrectly converts
-- passes -> passe, so put an entry here to ensure we singularize correctly.
plural = "mountain passes",
class = "natural feature",
addl_bare_category_parents = {"mountains"},
default = {true},
},
["mountain range"] = {
link = true,
fallback = "mountain",
},
["mountainous region"] = {
link = "separately",
fallback = "region",
},
["mukim"] = {
-- Malaysia, Brunei, Indonesia, Singapore
link = true,
preposition = "of",
class = "subpolity",
},
["municipal district"] = {
link = "w",
-- meaning varies depending on the country; for now, assume no neighborhoods.
-- FIXME: has_neighborhoods might have to be a function that looks at the containing holonyms.
preposition = "of",
affix_type = "Pref",
no_affix_strings = "district",
fallback = "municipality",
},
["municipality"] = {
link = true,
preposition = "of",
has_neighborhoods = true,
class = "subpolity",
},
["municipality with city status"] = {
link = "[[municipality]] with [[w:city status|city status]]",
plural = "municipalities with city status",
plural_link = "[[municipality|municipalities]] with [[w:city status|city status]]",
fallback = "municipality",
},
["museum"] = {
link = true,
fallback = "building",
},
["mythological location"] = {
link = "separately",
former_type = "!",
class = "hypothetical location",
bare_category_parent = "places",
default = {true},
},
["named bridges!"] = {
category_link = "notable [[bridge]]s",
bare_category_parent = "man-made structures",
addl_bare_category_parents = {"bridges"},
},
["named buildings!"] = {
category_link = "notable [[house]]s, [[library|libraries]] and other [[building]]s",
bare_category_parent = "man-made structures",
addl_bare_category_parents = {"buildings"},
},
["named roads!"] = {
category_link = "notable [[road]]s, [[highway]]s, [[trail]]s and similar linear structures",
bare_category_parent = "man-made structures",
addl_bare_category_parents = {"roads"},
},
["national capital"] = {
link = "w",
fallback = "capital city",
},
["national park"] = {
link = true,
fallback = "park",
},
["natural features!"] = {
category_link = "[[w:geographical feature#Natural features|natural features]] such as [[lake]]s, [[mountain]]s, [[island]]s and [[ocean]]s",
bare_category_parent = "places",
},
["neighborhood"] = {
-- The majority of the properties here apply to both `neighborhoods` and `neighbourhoods`; the choice of which
-- one to use is made by district_neighborhood_cat_handler() based on the value of `british_spelling` for the
-- location (city, political division, etc.) of the holonym that follows the word "neighbo(u)hoods" in the
-- category name. It does *NOT* depend on whether the {{place}} call uses "neighborhoods" or "neighbourhoods".
-- (In general it can't, because other things like "urban areas", "districts", "subdivisions" and the like also
-- categorize as neighbo(u)rhoods.)
link = true,
-- See below. These are used by category handlers in [[Module:category tree/topic cat/data/Places]].
generic_before_non_cities = "in",
generic_before_cities = "of",
-- The following text is suitable for the top-level description of a neighborhood as well as categories of the
-- form `Neighborhoods in POLDIV` e.g. `Neighborhoods in Illinois, USA` but not for categories of the form
-- `Neighborhoods of Chicago`, where we'd get "... and other subportions of [[city|cities]] of [[Chicago]]".
category_link = "[[neighborhood]]s, [[district]]s and other subportions of [[city|cities]]",
category_link_before_city = "[[neighborhood]]s, [[district]]s and other subportions",
-- NOTE: This setting is needed for administrative divisions like barangays that fall back to `neighborhood`,
-- when set in [[Module:place/locations]] for a specific country (e.g. the Philippines). The above settings
-- for `generic_before_non_cities` and `generic_before_cities` are used by category handlers in
-- [[Module:category tree/topic cat/data/Places]] for `Neighborhoods in POLDIV` and `Neighborhoods of CITY`
-- categories. In fact, district_neighborhood_cat_handler() does not currently pay attention to them, but
-- generates "of" before cities and "in" before non-cities regardless. (FIXME: We should change that.)
preposition = "of",
class = "non-admin settlement",
cat_handler = district_neighborhood_cat_handler,
},
["neighbourhood"] = {
link = true,
category_link = "[[neighbourhood]]s, [[district]]s and other subportions of [[city|cities]]",
category_link_before_city = "[[neighbourhood]]s, [[district]]s and other subportions",
fallback = "neighborhood",
},
["new area"] = {
-- China (type of economic development zone, varying greatly in size)
link = "w",
preposition = "in",
class = "subpolity", --?
},
["new town"] = {
link = true,
fallback = "town",
},
["non-city capital"] = {
link = "[[capital]]",
entry_placetype_use_the = true,
preposition = "of",
has_neighborhoods = true,
class = "capital",
cat_handler = function(data)
return capital_city_cat_handler(data, "non-city")
end,
-- FIXME, do we need the following?
default = {true},
},
["non-metropolitan county"] = {
link = "w",
fallback = "county",
},
["non-metropolitan district"] = {
link = "w",
fallback = "local government district",
},
["non-sovereign kingdom"] = {
-- especially in Africa and Asia
link = "+w:non-sovereign monarchy",
generic_before_non_cities = "in",
class = "subpolity",
["country/*"] = {true},
["continent/*"] = {true},
default = {true},
},
["non-sovereign monarchy"] = {
link = "w",
fallback = "non-sovereign kingdom",
},
["oblast"] = {
link = true,
preposition = "of",
affix_type = "Suf",
class = "subpolity",
},
["oblasts and autonomous republics!"] = {
-- This and other similar "combined placetypes" are for use in the plural when grouping first-level
-- administrative regions of certain countries, in this case Ukraine.
category_link = "[[oblast]]s and [[w:autonomous republic|autonomous republic]]s",
class = "subpolity",
},
["ocean"] = {
link = true,
holonym_use_the = true,
class = "natural feature",
addl_bare_category_parents = {"seas", "bodies of water"},
default = {true},
},
["okrug"] = {
link = true,
preposition = "of",
affix_type = "Suf",
class = "subpolity",
},
["overseas collectivity"] = {
link = "w",
fallback = "collectivity",
},
["overseas department"] = {
link = "w",
fallback = "department",
},
["overseas territory"] = {
link = "w",
fallback = "dependent territory",
},
["parish"] = {
link = true,
preposition = "of",
affix_type = "suf",
class = "subpolity",
},
["parish municipality"] = {
-- in Quebec, often similar to a rural village; the famous [[Saint-Louis-du-Ha! Ha!]] is one of them.
link = "+w:parish municipality (Quebec)",
preposition = "of",
fallback = "municipality",
has_neighborhoods = true,
},
["parish seat"] = {
link = true,
entry_placetype_use_the = true,
preposition = "of",
class = "capital",
has_neighborhoods = true,
},
["park"] = {
link = true,
class = "man-made structure",
default = {true},
},
["pass"] = {
link = "+mountain pass",
-- The default plural algorithm gets this right but the singularization algorithm incorrectly converts
-- passes -> passe, so put an entry here to ensure we singularize correctly.
plural = "passes",
fallback = "mountain pass",
},
["path"] = {
link = true,
fallback = "road",
},
["peak"] = {
link = true,
fallback = "mountain",
},
["peninsula"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"landforms"},
default = {true},
},
["periphery"] = {
link = true,
preposition = "of",
class = "subpolity",
},
["places!"] = {
generic_before_non_cities = "in",
generic_before_cities = "in",
class = "generic place",
category_link = "[[place]]s of all sorts",
-- `category_link_top_level` control the description used in the top-level [[Category:Places]] and
-- language-specific variants such as [[Category:en:Places]]. The actual text for a language-spefic variant is
-- "{{{langname}}} names of [[geographical]] [[place]]s of all sorts; [[toponym]]s." where the "names of"
-- portion is automatically generated by the appropriate handler in
-- [[Module:category tree/topic cat/data/Places]].
category_link_top_level = "[[geographical]] [[place]]s of all sorts; [[toponym]]s",
bare_category_parent = "names",
},
["planned community"] = {
-- Include this so we don't categorize 'planned community' into villages, as 'community' does.
link = true,
class = "settlement",
has_neighborhoods = true,
},
["plateau"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"landforms"},
default = {true},
-- FIXME: Should generate both "Plateaus" and the appropriate 'geographic and cultural area' category
},
["Polish colony"] = {
link = "[[w:colony (Poland)|colony]]",
affix_type = "suf",
affix = "colony",
fallback = "village",
has_neighborhoods = true,
},
["political divisions!"] = {
category_link = "[[political]] [[division]]s and [[subdivision]]s, such as [[state]]s, [[province]]s, [[county|counties]] or [[district]]s",
bare_category_parent = "places",
},
["polity"] = {
link = true,
category_link = "[[independent]] or [[semi-]][[independent]] [[polity|polities]]",
class = "polity",
bare_category_parent = "places",
default = {true},
},
["populated place"] = {
link = "+w:populated place",
-- not necessarily true, but usually is the case
fallback = "village",
},
["port"] = {
link = true,
class = "man-made structure",
default = {true},
},
["port city"] = {
-- FIXME: should categorize into "Ports" as well as "Cities"
link = true,
fallback = "city",
},
["port town"] = {
-- FIXME: should categorize into "Ports" as well as "Towns"
link = "w",
fallback = "town",
},
["prefecture"] = {
-- FIXME! `prefecture` is like a county in Japan and elsewhere but a department capital city in France.
-- May need `has_neighborhoods` to be a function.
link = true,
preposition = "of",
display_handler = prefecture_display_handler,
class = "subpolity",
},
["prefecture-level city"] = {
-- China; they are huge entities with a central city; not cities themselves.
link = "w",
preposition = "of",
class = "subpolity",
},
["preserved county"] = {
-- In Wales; they are former counties enshrined in law; there are 8 of them and each consists of one or more
-- "principal areas" (styled as "counties" or "county boroughs"), of which there are 22.
link = "w",
preposition = "of",
class = "subpolity",
inherently_former = {"FORMER"},
},
["primary area"] = {
-- a grouping of "districts" (neighborhoods) in Gothenburg, Sweden
link = "+w:sv:primärområde",
fallback = "neighborhood",
},
["principality"] = {
link = true,
fallback = "monarchy",
},
["promontory"] = {
link = true,
fallback = "headland",
},
["protectorate"] = {
link = true,
fallback = "dependent territory",
},
["province"] = {
link = true,
preposition = "of",
display_handler = province_display_handler,
class = "subpolity",
},
["provinces and autonomous regions!"] = {
-- This and other similar "combined placetypes" are for use in the plural when grouping first-level
-- administrative regions of certain countries, in this case China.
category_link = "[[province]]s and [[autonomous region]]s",
class = "subpolity",
},
["provinces and territories!"] = {
-- This and other similar "combined placetypes" are for use in the plural when grouping first-level
-- administrative regions of certain countries, in this case Canada and Pakistan.
category_link = "[[province]]s and [[territory|territories]]",
class = "subpolity",
},
["provincial capital"] = {
link = "separately",
fallback = "capital city",
},
["raion"] = {
link = true,
preposition = "of",
affix_type = "Suf",
class = "subpolity",
},
["ranch"] = {
link = true,
fallback = "farm",
},
["range"] = {
-- FIXME: Where is this used? Is it a mountain range?
link = true,
holonym_use_the = true,
class = "natural feature",
},
["regency"] = {
link = true,
preposition = "of",
class = "subpolity",
},
["region"] = {
link = true,
preposition = "of",
-- If 'region' isn't a specific administrative division, fall back to 'geographic and cultural area'
fallback = "geographic and cultural area",
-- "former region" is a subpolity but traditional/historic(al)/ancient/medieval/etc. is a geographic region
class = "geographic region",
},
["regional capital"] = {
link = "separately",
fallback = "capital city",
},
["regional county municipality"] = {
-- Quebec
link = "w",
preposition = "of",
affix_type = "Suf",
no_affix_strings = {"municipality", "county"},
fallback = "municipality",
},
["regional district"] = {
link = "w",
preposition = "of",
affix_type = "Pref",
no_affix_strings = "district",
fallback = "district",
},
["regional municipality"] = {
link = "w",
preposition = "of",
affix_type = "Pref",
no_affix_strings = "municipality",
fallback = "municipality",
},
["regional unit"] = {
link = "w",
preposition = "of",
affix_type = "suf",
class = "subpolity",
},
["registration county"] = {
-- Used in Scotland for land registration purposes; formerly used in England, Wales and Ireland for statistical
-- purposes (registration of births, deaths and marriages, and for the output of census information).
link = "w",
fallback = "county",
},
["republic"] = {
-- Of Russia, Yugoslavia, etc. "Republics" in general are sovereign but we use "country" in that case.
link = true,
fallback = "constituent republic",
},
["research base"] = {
link = "+w:research station",
fallback = "research station",
},
["research station"] = {
link = "w",
class = "non-admin settlement", -- or "man-made structure"?
default = {true},
},
["reservoir"] = {
link = true,
fallback = "lake",
},
["residential area"] = {
link = "separately",
fallback = "neighborhood",
},
["resort city"] = {
link = "w",
fallback = "city",
},
["resort town"] = {
link = "w",
fallback = "town",
},
["river"] = {
link = true,
generic_before_non_cities = "in",
holonym_use_the = true,
class = "natural feature",
addl_bare_category_parents = {"bodies of water"},
cat_handler = city_type_cat_handler,
["continent/*"] = {true},
default = {true},
},
["river island"] = {
link = "w",
fallback = "island",
},
["road"] = {
link = true,
class = "man-made structure",
default = {"Named roads"},
},
["Roman province"] = {
-- FIXME! Eliminate this in favor of 'former province|emp/Roman Empire'
link = "w",
default = {"Provinces of the Roman Empire"},
class = "subpolity",
},
["royal borough"] = {
link = "w",
preposition = "of",
affix_type = "Pref",
no_affix_strings = {"royal", "borough"},
fallback = "local government district with borough status",
has_neighborhoods = true,
},
["royal burgh"] = {
link = true,
fallback = "borough",
},
["royal capital"] = {
link = "w",
fallback = "capital city",
},
["rural committee"] = {
-- Hong Kong; a group of villages
link = "w",
affix_type = "Suf",
has_neighborhoods = true,
class = "settlement",
},
["rural community"] = {
-- New Brunswick
link = "+w:list of municipalities in New_Brunswick#Rural communities",
fallback = "municipality",
},
["rural hromada"] = {
link = "[[rural]] [[w:hromada|hromada]]",
affix_type = "suf",
fallback = "hromada",
},
["rural municipality"] = {
link = "w",
preposition = "of",
affix_type = "Pref",
no_affix_strings = "municipality",
fallback = "municipality",
has_neighborhoods = true, --?
},
["rural township"] = {
-- Taiwan
link = "+w:rural township (Taiwan)",
fallback = "township",
},
["sanctuary"] = {
link = true,
fallback = "temple",
},
["satrapy"] = {
link = true,
preposition = "of",
class = "subpolity",
inherently_former = {"ANCIENT", "FORMER"},
},
["sea"] = {
link = true,
holonym_use_the = true,
class = "natural feature",
addl_bare_category_parents = {"bodies of water"},
default = {true},
},
["seaport"] = {
link = true,
fallback = "port",
},
["seat"] = {
link = true,
fallback = "administrative centre",
},
["self-administered area"] = {
-- Myanmar (groups self-administered divisions and zones)
link = "+w:self-administered zone",
preposition = "of",
class = "subpolity",
},
["self-administered division"] = {
-- Myanmar (only one of them: Wa Self-Administered Division)
link = "w",
fallback = "self-administered area",
},
["self-administered zone"] = {
-- Myanmar (five of them)
link = "w",
fallback = "self-administered area",
},
["separatist state"] = {
link = "separately",
fallback = "unrecognized country",
},
["settlement"] = {
link = true,
category_link = "[[settlement]]s such as [[city|cities]], [[village]]s and [[farm]]s",
bare_category_parent = "places",
-- not necessarily true, but usually is the case
fallback = "village",
},
["settlement hromada"] = {
link = "[[w:Populated places in Ukraine#Rural settlements|settlement]] [[w:hromada|hromada]]",
affix_type = "suf",
fallback = "hromada",
},
["sheading"] = {
-- Isle of Man
link = true,
fallback = "district",
},
["sheep station"] = {
-- Australia
link = true,
fallback = "farm",
},
["shire"] = {
link = true,
fallback = "county",
},
["shire county"] = {
link = "w",
fallback = "county",
},
["shire town"] = {
link = true,
fallback = "county seat",
},
["ski resort city"] = {
link = "[[ski resort]] [[city]]",
fallback = "city",
},
["ski resort town"] = {
link = "[[ski resort]] [[town]]",
fallback = "town",
},
["spa city"] = {
link = "+w:spa town",
fallback = "city",
},
["spa town"] = {
link = "w",
fallback = "town",
},
["space station"] = {
link = true,
fallback = "research station",
},
["special administrative region"] = {
-- in China; in practice they are city-like (Hong Kong, Macau); also [[Oecusse]] in East Timor is formally a
-- "special administrative region"; North Korea had one such region planned (Sinuiju) but abandoned; Indonesia
-- has similar "special regions" of Jakarta, Yogyakarta and Aceh; and South Sudan has three "special
-- administrative areas"
link = "+w:special administrative regions of China",
preposition = "of",
class = "subpolity",
has_neighborhoods = true, --?
-- no suffix since places in Hong Kong or Macau are listed without China, except Hong Kong and Macau themselves
-- they also contain regions (or areas), e.g. [[Kowloon]], so it would be confusing
suffix = "",
},
["special collectivity"] = {
link = "w",
fallback = "collectivity",
},
["special municipality"] = {
-- formerly linked to the Taiwan article but there are also special municipalities of the Netherlands
link = "w",
fallback = "municipality",
},
["special ward"] = {
-- Tokyo
link = true,
fallback = "municipality",
},
["spit"] = {
link = true,
fallback = "peninsula",
},
["spring"] = {
link = true,
class = "natural feature",
default = {true},
},
["star"] = {
link = true,
class = "natural feature",
default = {true},
},
["state"] = {
link = true,
preposition = "of",
class = "subpolity",
-- 'former/historical state' could refer either to a state of a country (a division) or a state = sovereign
-- entity. The latter appears more common (e.g. in various "ancient states" of East Asia).
former_type = "polity",
},
["states and territories!"] = {
-- This and other similar "combined placetypes" are for use in the plural when grouping first-level
-- administrative regions of certain countries, in this case Australia.
category_link = "[[state]]s and [[territory|territories]]",
class = "subpolity",
},
["states and union territories!"] = {
-- This and other similar "combined placetypes" are for use in the plural when grouping first-level
-- administrative regions of certain countries, in this case India.
category_link = "[[state]]s and [[union territory|union territories]]",
class = "subpolity",
},
["state capital"] = {
link = "separately",
fallback = "capital city",
},
["state park"] = {
link = true,
fallback = "park",
},
["state-level new area"] = {
-- China (type of economic development zone, varying greatly in size)
link = "w",
fallback = "new area",
},
["statistical region"] = {
-- Slovenia
link = true,
fallback = "administrative region",
},
["statutory city"] = {
link = "w",
fallback = "city",
},
["statutory town"] = {
link = "w",
fallback = "town",
},
["strait"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"bodies of water"},
default = {true},
},
["stream"] = {
link = true,
fallback = "river",
},
["street"] = {
link = true,
fallback = "road",
},
["strip"] = {
link = true,
fallback = "geographic region",
},
["strip of land"] = {
link = "[[strip]] of [[land]]",
plural = "strips of land",
plural_link = "[[strip]]s of [[land]]",
fallback = "geographic region",
},
["sub-metropolitan city"] = {
link = "+w:List of cities in Nepal#Sub-metropolitan cities",
fallback = "city",
},
["sub-prefectural city"] = {
link = "w",
fallback = "subprovincial city",
},
["subdistrict"] = {
link = true,
preposition = "of",
has_neighborhoods = true, --?
-- FIXME: subdistricts can be neighborhood-like (of Jakarta) or larger (in China); need a handler
class = "subpolity",
default = {true},
},
["subdivision"] = {
link = true,
preposition = "of",
affix_type = "suf",
-- FIXME: subdivisions can be neighborhood-like or larger; need a handler
class = "subpolity",
cat_handler = district_neighborhood_cat_handler,
},
["submerged ghost town"] = {
-- FIXME: Consider just having "submerged" as a qualifier.
link = "[[submerged]] [[ghost town]]",
fallback = "ghost town",
},
["subnational kingdom"] = {
link = "+w:subnational monarchy",
fallback = "non-sovereign kingdom",
},
["subnational monarchy"] = {
link = "w",
fallback = "non-sovereign kingdom",
},
["subprefecture"] = {
link = true,
affix_type = "suf",
preposition = "of",
class = "subpolity",
},
["subprovince"] = {
link = true,
preposition = "of",
class = "subpolity",
},
["subprovincial city"] = {
link = "w",
-- China; special status given to certain prefecture-level cities
fallback = "prefecture-level city",
},
["subprovincial district"] = {
link = "w",
-- China; special status given to Binhai New Area and Pudong New Area, which are county-level districts
preposition = "of",
class = "subpolity",
},
["subregion"] = {
link = true,
fallback = "geographic region",
},
["suburb"] = {
link = true,
-- The following text is suitable for the top-level description of a suburb as well as categories of the form
-- 'Suburbs in POLDIV' e.g. 'Suburbs in Illinois, USA' but not for categories of the form 'Suburbs of Chicago',
-- where we'd get "[[suburb]]s of [[city|cities]] of [[Chicago]]".
category_link = "[[suburb]]s of [[city|cities]]",
category_link_before_city = "[[suburb]]s",
-- See comments under "neighborhood" for the following three settings. They are used by
-- [[Module:category tree/topic cat/data/Places]] for generating the text of 'Suburbs in/of PLACE' categories
-- but currently ignored by district_neighborhood_cat_handler (which actually generates the categories for a
-- given page), which hardcodes "in" for non-cities and "of" for cities. (FIXME: Change this.)
generic_before_non_cities = "in",
generic_before_cities = "of",
preposition = "of",
has_neighborhoods = true, --?
class = "non-admin settlement", --?
cat_handler = district_neighborhood_cat_handler,
},
["suburban area"] = {
link = "w",
fallback = "suburb",
},
["subway station"] = {
link = "w",
fallback = "metro station",
},
["sum"] = {
-- In China, Mongolia, Russia; something like a county in Mongolia but a township in China (Inner Mongolia),
-- and equivalent to a [[selsoviet]] in the parts of Russia where it's in use (a rural council, below a raion).
link = "+w:sum (administrative division)",
-- This fallback is somewha arbitrary. We could use "county" but that has a display handler
-- which we don't want to be active (FIXME: If the display handler would be active, that's a bug).
fallback = "division",
},
["supercontinent"] = {
link = true,
fallback = "continent",
},
["tehsil"] = {
link = true,
affix_type = "suf",
no_affix_strings = {"tehsil", "tahsil"},
class = "subpolity",
},
["temple"] = {
link = true,
fallback = "building",
},
["territorial authority"] = {
link = "w",
fallback = "district",
},
["territory"] = {
link = true,
preposition = "of",
class = "subpolity",
},
["theme"] = {
link = "+w:theme (Byzantine district)",
preposition = "of",
class = "subpolity",
},
["town"] = {
link = true,
generic_before_non_cities = "in",
has_neighborhoods = true,
class = "settlement",
cat_handler = city_type_cat_handler,
default = {true},
},
["town with bystatus"] = {
-- can't use templates in links currently
link = "[[town]] with [[bystatus#Norwegian Bokmål|bystatus]]",
plural = "towns with bystatus",
plural_link = "[[town]]s with [[bystatus#Norwegian Bokmål|bystatus]]",
fallback = "town",
},
["township"] = {
link = true,
has_neighborhoods = true,
class = "settlement", --?
default = {true},
},
["township municipality"] = {
-- Quebec
link = "+w:township municipality (Quebec)",
preposition = "of",
fallback = "municipality",
has_neighborhoods = true, --?
},
["traditional county"] = {
link = true,
fallback = "county",
},
["traditional region"] = {
-- FIXME: Verify this works. Same for 'historic(al) region'.
-- provided only for the link
link = "w",
fallback = "FORMER geographic region",
},
["trail"] = {
link = true,
fallback = "road",
},
["treaty port"] = {
link = "w",
fallback = "city",
class = "settlement",
inherently_former = {"FORMER"},
},
["tributary"] = {
link = true,
preposition = "of",
fallback = "river",
},
["underground station"] = {
link = "w",
fallback = "metro station",
},
["unincorporated area"] = {
link = "w",
-- I don't know if this fallback makes sense everywhere.
fallback = "unincorporated community",
},
["unincorporated community"] = {
link = true,
generic_before_non_cities = "in",
class = "non-admin settlement",
},
["unincorporated territory"] = {
link = "w",
fallback = "territory",
},
["union territory"] = {
-- India
link = true,
preposition = "of",
entry_placetype_indefinite_article = "a",
class = "subpolity",
},
["unitary authority"] = {
-- UK, New Zealand
link = true,
entry_placetype_indefinite_article = "a",
fallback = "local government district",
},
["unitary district"] = {
link = "w",
entry_placetype_indefinite_article = "a",
fallback = "local government district",
},
["united township municipality"] = {
-- Quebec
link = "+w:united township municipality (Quebec)",
entry_placetype_indefinite_article = "a",
fallback = "township municipality",
has_neighborhoods = true, --?
},
["university"] = {
link = true,
entry_placetype_indefinite_article = "a",
class = "man-made structure",
default = {true},
},
["unrecognised country"] = {
link = "w",
fallback = "unrecognized country",
},
["unrecognized and nearly unrecognized countries!"] = {
category_link = "[[de facto]] [[independent]] [[state]]s with little or no {{w|international recognition}}",
bare_category_parent = "country-like entities",
},
["unrecognized country"] = {
link = "w",
class = "polity",
default = {"Unrecognized and nearly unrecognized countries"},
},
["unrecognised state"] = {
link = "w",
fallback = "unrecognized country",
},
["unrecognized state"] = {
link = "w",
fallback = "unrecognized country",
},
["urban area"] = {
link = "separately",
fallback = "neighborhood",
},
["urban hromada"] = {
link = "[[urban]] [[w:hromada|hromada]]",
affix_type = "suf",
fallback = "hromada",
},
["urban service area"] = {
-- A strange beast existing in Alberta; technically a type of hamlet but in practice used for much larger
-- cities and treated equivalent to a city. (There are only two of them, [[Fort McMurray]] and [[Sherwood Park]]).
link = "w",
fallback = "city",
},
["urban township"] = {
link = "w",
fallback = "township",
},
["urban-type settlement"] = {
-- appears to be a particular type of small urban settlement in post-Soviet states,
-- had an administrative function.
link = "w",
fallback = "town",
},
["valley"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"landforms", "water"},
default = {true},
},
["viceroyalty"] = {
-- in essence, a type of colony
link = true,
fallback = "dependent territory",
},
["village"] = {
link = true,
generic_before_non_cities = "in",
category_link = "[[village]]s, [[hamlet]]s, and other small [[community|communities]] and [[settlement]]s",
class = "settlement",
cat_handler = city_type_cat_handler,
default = {true},
},
["village development committee"] = {
-- former administrative structure in Nepal; also exists in India but not as a formal unit
link = "+w:village development committee (Nepal)",
inherently_former = {"FORMER"},
fallback = "village",
},
["village municipality"] = {
-- Quebec
link = "+w:village municipality (Quebec)",
preposition = "of",
fallback = "municipality",
has_neighborhoods = true, --?
},
["voivodeship"] = {
-- Poland
link = true,
display_handler = voivodeship_display_handler,
preposition = "of",
class = "subpolity",
},
["volcano"] = {
link = true,
plural = "volcanoes",
class = "natural feature",
addl_bare_category_parents = {"landforms"},
default = {true, "Mountains"},
},
["ward"] = {
link = true,
class = "settlement",
-- Wards are formal administrative divisions of a city but have some properties of neighborhoods.
fallback = "neighborhood",
},
["watercourse"] = {
link = true,
fallback = "channel",
},
["Welsh community"] = {
-- Wales
link = "[[w:community (Wales)|community]]",
preposition = "of",
affix_type = "suf",
affix = "community",
has_neighborhoods = true,
class = "settlement",
},
["zone"] = {
-- administrative division of Ethiopia, Qatar, Nepal, India
link = "+w:zone#Place names",
preposition = "of",
class = "subpolity",
},
----------------------------------------------------------------------------------------------
-- Categories for former places --
----------------------------------------------------------------------------------------------
["ANCIENT capital"] = {
link = false,
entry_placetype_use_the = true,
preposition = "of",
has_neighborhoods = true,
class = "capital",
-- FIXME: Consider removing 'ancient settlements' here. Ancient capitals, like former capitals, often still
-- exist but just aren't the capital any more. Maybe we should have an 'Ancient capitals' category.
default = {"Ancient settlements", "Former capitals"},
},
["ANCIENT non-admin settlement"] = {
link = false,
class = "non-admin settlement",
fallback = "ANCIENT settlement",
},
["ANCIENT settlement"] = {
link = false,
has_neighborhoods = true,
class = "settlement",
default = {"Ancient settlements"},
},
["ancient settlements!"] = {
category_link = "former [[city|cities]], [[town]]s and [[village]]s that existed in [[antiquity]]",
bare_category_parent = "former settlements",
},
["FORMER capital"] = {
link = false,
entry_placetype_use_the = true,
preposition = "of",
has_neighborhoods = true,
class = "capital",
default = {"Former capitals"},
},
["former capitals!"] = {
category_link = "former [[capital]] [[city|cities]] and [[town]]s",
bare_category_parent = "settlements",
},
["former counties and county-level cities!"] = {
-- For categorizing former counties and county-level cities of China
category_link = "no-longer existing [[county|counties]] and [[county-level city|county-level cities]]",
bare_category_breadcrumb = "counties and county-level cities",
bare_category_parent = "former political divisions",
},
["FORMER county"] = {
-- For categorizing former counties and county-level cities of China
link = false,
fallback = "FORMER subpolity",
},
["FORMER county-level city"] = {
-- For categorizing former counties and county-level cities of China
link = false,
fallback = "FORMER subpolity",
},
["former countries and country-like entities!"] = {
category_link = "[[country|countries]] and similar [[polity|polities]] that no longer exist",
bare_category_breadcrumb = "countries and country-like entities",
bare_category_parent = "former polities",
},
["FORMER country"] = {
link = false,
class = "polity",
default = {"Former countries and country-like entities"},
},
["former dependent territories!"] = {
category_link = "[[w:dependent territory|dependent territories]] (colonies, dependencies, protectorates, etc.) that no longer exist",
bare_category_breadcrumb = "dependent territories",
bare_category_parent = "former political divisions",
},
["FORMER dependent territory"] = {
link = false,
preposition = "of",
class = "subpolity",
default = {"Former dependent territories"},
},
["former districts!"] = {
-- For categorizing former districts of China
category_link = "no-longer-existing [[district]]s",
bare_category_breadcrumb = "districts",
bare_category_parent = "former political divisions",
},
["FORMER district"] = {
-- For categorizing former districts of China
link = false,
fallback = "FORMER subpolity",
},
["FORMER geographic region"] = {
link = false,
fallback = "geographic and cultural area",
},
["FORMER man-made structure"] = {
link = false,
class = "man-made structure",
default = {"Former man-made structures"},
},
["former man-made structures!"] = {
category_link = "man-made structures such as [[airport]]s and [[park]]s that no longer exist",
bare_category_breadcrumb = "man-made structures",
bare_category_parent = "former places",
},
["former municipalities!"] = {
-- For categorizing former municipalities of the Netherlands
category_link = "no-longer-existing [[municipality|municipalities]]",
bare_category_breadcrumb = "municipalities",
bare_category_parent = "former political divisions",
},
["FORMER municipality"] = {
-- For categorizing former municipalities of the Netherlands
link = false,
fallback = "FORMER subpolity",
},
["FORMER natural feature"] = {
link = false,
class = "natural feature",
default = {"Former natural features"},
},
["former natural features!"] = {
category_link = "natural features such as [[lake]]s, [[river]]s and [[island]]s that no longer exist",
bare_category_breadcrumb = "natural features",
bare_category_parent = "former places",
},
["FORMER non-admin settlement"] = {
link = false,
class = "non-admin settlement",
fallback = "FORMER settlement",
},
["former places!"] = {
category_link = "[[place]]s of all sorts that no longer exist",
bare_category_breadcrumb = "former",
bare_category_parent = "places",
},
["former political divisions!"] = {
category_link = "[[political]] [[division]]s (states, provinces, counties, etc.) that no longer exist",
bare_category_breadcrumb = "political divisions",
bare_category_parent = "former places",
},
["former polities!"] = {
category_link = "[[polity|polities]] (countries, kingdoms, empires, etc.) that no longer exist",
bare_category_breadcrumb = "polities",
bare_category_parent = "former places",
},
["FORMER polity"] = {
link = false,
class = "polity",
default = {"Former polities"},
},
["former prefectures!"] = {
-- For categorizing former prefectures of China
category_link = "no-longer-existing [[prefecture]]s",
bare_category_breadcrumb = "prefectures",
bare_category_parent = "former political divisions",
},
["FORMER prefecture"] = {
-- For categorizing former prefectures of China
link = false,
fallback = "FORMER subpolity",
},
["former provinces!"] = {
-- For categorizing former provinces of China, etc.
category_link = "no-longer-existing [[province]]s",
bare_category_breadcrumb = "provinces",
bare_category_parent = "former political divisions",
},
["FORMER province"] = {
-- For categorizing ancient/historical/former provinces of the Roman Empire
link = false,
fallback = "FORMER subpolity",
},
["former region"] = {
-- A former region is considered a former political division, but not a 'historical/traditional/etc.' region.
link = "separately",
preposition = "of",
inherently_former = {"FORMER"},
class = "subpolity",
},
["FORMER settlement"] = {
link = false,
has_neighborhoods = true,
class = "settlement",
default = {"Former settlements"},
},
["former settlements!"] = {
category_link = "[[city|cities]], [[town]]s and [[village]]s that no longer exist or have been merged or reclassified",
bare_category_breadcrumb = "settlements",
bare_category_parent = "former political divisions",
},
["FORMER subpolity"] = {
link = false,
preposition = "of",
class = "subpolity",
default = {"Former political divisions"},
},
----------------------------------------------------------------------------------------------
-- form-of categories --
----------------------------------------------------------------------------------------------
---------- Abbreviations ----------
["abbreviations of counties!"] = {
-- For categorizing abbreviations of counties of e.g. England
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[county|counties]]",
bare_category_breadcrumb = "counties",
bare_category_parent = "abbreviations of political divisions",
},
["abbreviations of countries!"] = {
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[country|countries]]",
bare_category_breadcrumb = "countries",
bare_category_parent = "abbreviations of places",
},
["abbreviations of departments!"] = {
-- For categorizing abbreviations of departments of e.g. France
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[department]]s",
bare_category_breadcrumb = "departments",
bare_category_parent = "abbreviations of political divisions",
},
["abbreviations of districts!"] = {
-- For categorizing abbreviations of districts of e.g. ???
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[district]]s",
bare_category_breadcrumb = "districts",
bare_category_parent = "abbreviations of political divisions",
},
["abbreviations of divisions!"] = {
-- For categorizing abbreviations of divisions of e.g. Bangladesh
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[division]]s",
bare_category_breadcrumb = "divisions",
bare_category_parent = "abbreviations of political divisions",
},
["abbreviations of former countries!"] = {
full_category_link = "{{glossary|abbreviation}}s of [[country|countries]] that no longer [[exist]]",
bare_category_breadcrumb = "countries",
bare_category_parent = "abbreviations of former places",
},
["abbreviations of former places!"] = {
full_category_link = "{{glossary|abbreviation}}s of [[place]]s that no longer [[exist]]",
bare_category_breadcrumb = "abbreviations",
bare_category_parent = "former places",
addl_bare_category_parents = {{name = "abbreviations of places", sort = "former"}},
},
["abbreviations of places!"] = {
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[place]]s",
bare_category_breadcrumb = "abbreviations",
bare_category_parent = "places",
},
["abbreviations of political divisions!"] = {
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[political]] [[division]]s",
bare_category_breadcrumb = "political divisions",
bare_category_parent = "abbreviations of places",
},
["abbreviations of prefectures!"] = {
-- For categorizing abbreviations of prefectures of e.g. Japan
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[prefecture]]s",
bare_category_breadcrumb = "prefectures",
bare_category_parent = "abbreviations of political divisions",
},
["abbreviations of provinces!"] = {
-- For categorizing abbreviations of provinces of e.g. Canada
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[province]]s",
bare_category_breadcrumb = "provinces",
bare_category_parent = "abbreviations of political divisions",
},
["abbreviations of provinces and territories!"] = {
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[province]]s and [[territory|territories]]",
bare_category_breadcrumb = "provinces and territories",
bare_category_parent = "abbreviations of political divisions",
},
["abbreviations of regions!"] = {
-- For categorizing abbreviations of regions of e.g. Italy
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[administrative region]]s",
bare_category_breadcrumb = "regions",
bare_category_parent = "abbreviations of political divisions",
},
["abbreviations of states!"] = {
-- For categorizing abbreviations of states of e.g. the United States
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[state]]s",
bare_category_breadcrumb = "states",
bare_category_parent = "abbreviations of political divisions",
},
["abbreviations of states and territories!"] = {
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[state]]s and [[territory|territories]]",
bare_category_breadcrumb = "states and territories",
bare_category_parent = "abbreviations of political divisions",
},
["abbreviations of states and union territories!"] = {
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[state]]s and [[union territory|union territories]]",
bare_category_breadcrumb = "states and union territories",
bare_category_parent = "abbreviations of political divisions",
},
["abbreviations of territories!"] = {
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[territory|territories]]",
bare_category_breadcrumb = "territories",
bare_category_parent = "abbreviations of political divisions",
},
["ABBREVIATION_OF country"] = {
link = false,
default = {"Abbreviations of countries"},
},
["ABBREVIATION_OF county"] = {
link = false,
fallback = "ABBREVIATION_OF subpolity",
},
["ABBREVIATION_OF department"] = {
link = false,
fallback = "ABBREVIATION_OF subpolity",
},
["ABBREVIATION_OF district"] = {
link = false,
fallback = "ABBREVIATION_OF subpolity",
},
["ABBREVIATION_OF division"] = {
link = false,
fallback = "ABBREVIATION_OF subpolity",
},
["ABBREVIATION_OF FORMER country"] = {
link = false,
default = {"Abbreviations of former countries"},
},
["ABBREVIATION_OF FORMER place"] = {
link = false,
default = {"Abbreviations of former places"},
},
["ABBREVIATION_OF place"] = {
link = false,
default = {"Abbreviations of places"},
},
["ABBREVIATION_OF prefecture"] = {
link = false,
fallback = "ABBREVIATION_OF subpolity",
},
["ABBREVIATION_OF province"] = {
link = false,
fallback = "ABBREVIATION_OF subpolity",
},
["ABBREVIATION_OF region"] = {
link = false,
fallback = "ABBREVIATION_OF subpolity",
},
["ABBREVIATION_OF state"] = {
link = false,
fallback = "ABBREVIATION_OF subpolity",
},
["ABBREVIATION_OF subpolity"] = {
link = false,
default = {"Abbreviations of political divisions"},
},
["ABBREVIATION_OF territory"] = {
link = false,
fallback = "ABBREVIATION_OF subpolity",
},
["ABBREVIATION_OF union territory"] = {
link = false,
fallback = "ABBREVIATION_OF subpolity",
},
---------- Archaic forms ----------
["archaic forms of places!"] = {
full_category_link = "{{glossary|archaic}} [[form]]s of [[name]]s of [[place]]s",
bare_category_breadcrumb = "archaic forms",
bare_category_parent = "places",
},
["ARCHAIC_FORM_OF place"] = {
link = false,
default = {"Archaic forms of places"},
},
---------- Clippings ----------
["clippings of places!"] = {
full_category_link = "{{glossary|clipping}}s of [[name]]s of [[place]]s",
bare_category_breadcrumb = "clippings",
bare_category_parent = "places",
},
["CLIPPING_OF place"] = {
link = false,
default = {"Clippings of places"},
},
---------- Dated forms ----------
["dated forms of places!"] = {
full_category_link = "{{glossary|dated}} [[form]]s of [[name]]s of [[place]]s",
bare_category_breadcrumb = "dated forms",
bare_category_parent = "places",
},
["DATED_FORM_OF place"] = {
link = false,
default = {"Dated forms of places"},
},
---------- Derogatory names ----------
["derogatory names for cities!"] = {
full_category_link = "{{glossary|derogatory}} [[name]]s for [[city|cities]]",
bare_category_breadcrumb = "cities",
bare_category_parent = "derogatory names for places",
addl_bare_category_parents = {"nicknames for cities"},
},
["derogatory names for continents!"] = {
full_category_link = "{{glossary|derogatory}} [[name]]s for [[continent]]s",
bare_category_breadcrumb = "continents",
bare_category_parent = "derogatory names for places",
addl_bare_category_parents = {"nicknames for continents"},
},
["derogatory names for countries!"] = {
full_category_link = "{{glossary|derogatory}} [[name]]s for [[country|countries]]",
bare_category_breadcrumb = "countries",
bare_category_parent = "derogatory names for places",
addl_bare_category_parents = {"nicknames for countries"},
},
["derogatory names for places!"] = {
full_category_link = "{{glossary|derogatory}} [[name]]s for [[place]]s",
bare_category_breadcrumb = "derogatory names",
bare_category_parent = "nicknames for places",
},
["derogatory names for states!"] = {
full_category_link = "{{glossary|derogatory}} [[name]]s for [[state]]s",
bare_category_breadcrumb = "states",
bare_category_parent = "derogatory names for places",
addl_bare_category_parents = {"nicknames for states"},
},
["DEROGATORY_NAME_FOR capital"] = {
link = false,
default = {"Derogatory names for cities"},
},
["DEROGATORY_NAME_FOR city"] = {
link = false,
default = {"Derogatory names for cities"},
},
["DEROGATORY_NAME_FOR continent"] = {
link = false,
default = {"Derogatory names for continents"},
},
["DEROGATORY_NAME_FOR country"] = {
link = false,
default = {"Derogatory names for countries"},
},
["DEROGATORY_NAME_FOR metropolitan city"] = {
-- "metropolitan city" doesn't fall back to "city"
link = false,
default = {"Derogatory names for cities"},
},
["DEROGATORY_NAME_FOR place"] = {
link = false,
default = {"Derogatory names for places"},
},
["DEROGATORY_NAME_FOR prefecture-level city"] = {
-- "prefecture-level city" doesn't fall back to "city" but things like "county-level city" and
-- "subprovincial city" fall back to "prefecture-level city"
link = false,
default = {"Derogatory names for cities"},
},
["DEROGATORY_NAME_FOR state"] = {
link = false,
default = {"Derogatory names for states"},
},
["DEROGATORY_NAME_FOR town"] = {
link = false,
default = {"Derogatory names for cities"},
},
---------- Ellipses ----------
["ellipses of places!"] = {
full_category_link = "{{glossary|ellipsis|ellipses}} of [[name]]s of [[place]]s",
bare_category_breadcrumb = "ellipses",
bare_category_parent = "places",
},
["ELLIPSIS_OF place"] = {
link = false,
default = {"Ellipses of places"},
},
---------- Former long-form names ----------
["former long-form names of countries!"] = {
full_category_link = "no-longer-[[use]]d [[long]]-[[form]] (but typically [[unofficial]]) [[name]]s of [[country|countries]]",
bare_category_breadcrumb = "countries",
bare_category_parent = "former long-form names of places",
addl_bare_category_parents = {{name = "former names of countries", sort = "long-form"}},
},
["former long-form names of places!"] = {
full_category_link = "no-longer-[[use]]d [[long]]-[[form]] (but typically [[unofficial]]) [[name]]s of [[place]]s",
bare_category_breadcrumb = "long-form",
bare_category_parent = "former names of places",
},
["FORMER_LONG_FORM_OF country"] = {
link = false,
default = {"Former long-form names of countries"},
},
["FORMER_LONG_FORM_OF place"] = {
link = false,
default = {"Former long-form names of places"},
},
---------- Former names ----------
["former names of capitals!"] = {
full_category_link = "[[former]] [[name]]s of [[capital city|capital cities]] that generally still exist but under a different name",
bare_category_breadcrumb = "capitals",
bare_category_parent = "former names of settlements",
},
["former names of countries!"] = {
full_category_link = "[[former]] [[name]]s of [[country|countries]] that generally still exist but under a different name",
bare_category_breadcrumb = "countries",
bare_category_parent = "former names of places",
},
["former names of places!"] = {
full_category_link = "[[former]] [[name]]s of [[place]]s that generally still exist but under a different name",
bare_category_breadcrumb = "former names",
bare_category_parent = "places",
},
["former names of political divisions!"] = {
full_category_link = "[[former]] [[name]]s of [[political]] [[division]]s (states, provinces, counties, etc.) that generally still exist but under a different name",
bare_category_breadcrumb = "political divisions",
bare_category_parent = "former names of places",
},
["former names of polities!"] = {
full_category_link = "[[former]] [[name]]s of [[polity|polities]] (e.g. [[country|countries]]) that generally still exist but under a different name",
bare_category_breadcrumb = "polities",
bare_category_parent = "former names of places",
},
["former names of settlements!"] = {
full_category_link = "[[former]] [[name]]s of [[city|cities]], [[town]]s, [[village]]s, etc. that generally still exist but under a different name",
bare_category_breadcrumb = "settlements",
bare_category_parent = "former names of political divisions",
},
["FORMER_NAME_OF capital"] = {
link = false,
default = {"Former names of capitals"},
},
["FORMER_NAME_OF country"] = {
link = false,
default = {"Former names of countries"},
},
["FORMER_NAME_OF place"] = {
link = false,
default = {"Former names of places"},
},
["FORMER_NAME_OF polity"] = {
link = false,
default = {"Former names of polities"},
},
["FORMER_NAME_OF region"] = {
link = false,
fallback = "FORMER_NAME_OF subpolity",
},
["FORMER_NAME_OF settlement"] = {
link = false,
default = {"Former names of settlements"},
},
["FORMER_NAME_OF subpolity"] = {
link = false,
default = {"Former names of political divisions"},
},
---------- Former nicknames ----------
["former nicknames for cities!"] = {
full_category_link = "no-longer-used [[nickname]]s for [[city|cities]], e.g. the [[Eternal City]] for [[Kyoto]] during the {{w|Heian period}} ({{circa2|800–1100|short=yes}} {{AD}})",
bare_category_breadcrumb = "cities",
bare_category_parent = "former nicknames for places",
addl_bare_category_parents = {"nicknames for cities"},
},
["former nicknames for places!"] = {
full_category_link = "no-longer-used [[nickname]]s for [[place]]s",
bare_category_breadcrumb = "former",
bare_category_parent = "nicknames for places",
addl_bare_category_parents = {{name = "former names of places", sort = "nicknames"}},
},
["FORMER_NICKNAME_FOR capital"] = {
link = false,
default = {"Former nicknames for cities"},
},
["FORMER_NICKNAME_FOR city"] = {
link = false,
default = {"Former nicknames for cities"},
},
["FORMER_NICKNAME_FOR metropolitan city"] = {
-- "metropolitan city" doesn't fall back to "city"
link = false,
default = {"Former nicknames for cities"},
},
["FORMER_NICKNAME_FOR place"] = {
link = false,
default = {"Former nicknames for places"},
},
["FORMER_NICKNAME_FOR prefecture-level city"] = {
-- "prefecture-level city" doesn't fall back to "city" but things like "county-level city" and
-- "subprovincial city" fall back to "prefecture-level city"
link = false,
default = {"Former nicknames for cities"},
},
["FORMER_NICKNAME_FOR town"] = {
link = false,
default = {"Former nicknames for cities"},
},
---------- Former official names ----------
["former official names of countries!"] = {
full_category_link = "no-longer-[[use]]d [[official]] [[name]]s of [[country|countries]]",
bare_category_breadcrumb = "countries",
bare_category_parent = "former official names of places",
addl_bare_category_parents = {{name = "former names of countries", sort = "official"}},
},
["former official names of places!"] = {
full_category_link = "no-longer-[[use]]d [[official]] [[name]]s of [[place]]s",
bare_category_breadcrumb = "official",
bare_category_parent = "former names of places",
},
["FORMER_OFFICIAL_NAME_OF country"] = {
link = false,
default = {"Former official names of countries"},
},
["FORMER_OFFICIAL_NAME_OF place"] = {
link = false,
default = {"Former official names of places"},
},
---------- Long-form names ----------
["long-form names of countries!"] = {
full_category_link = "[[long]]-[[form]] (but typically [[unofficial]]) [[name]]s of [[country|countries]]",
bare_category_breadcrumb = "countries",
bare_category_parent = "long-form names of places",
},
["long-form names of places!"] = {
full_category_link = "[[long]]-[[form]] (but typically [[unofficial]]) [[name]]s of [[place]]s",
bare_category_breadcrumb = "long-form names",
bare_category_parent = "places",
},
["LONG_FORM_OF country"] = {
link = false,
default = {"Long-form names of countries"},
},
["LONG_FORM_OF place"] = {
link = false,
default = {"Long-form names of places"},
},
---------- Nicknames ----------
["nicknames for cities!"] = {
full_category_link = "[[nickname]]s for [[city|cities]], e.g. the [[Big Apple]] for [[New York City]]",
bare_category_breadcrumb = "cities",
bare_category_parent = "nicknames for places",
addl_bare_category_parents = {"cities"},
},
["nicknames for continents!"] = {
full_category_link = "[[nickname]]s for [[continent]]s",
bare_category_breadcrumb = "continents",
bare_category_parent = "nicknames for places",
addl_bare_category_parents = {"continents"},
},
["nicknames for countries!"] = {
full_category_link = "[[nickname]]s for [[country|countries]]",
bare_category_breadcrumb = "countries",
bare_category_parent = "nicknames for places",
addl_bare_category_parents = {"countries"},
},
["nicknames for places!"] = {
full_category_link = "[[nickname]]s for [[place]]s",
bare_category_breadcrumb = "places",
bare_category_parent = "nicknames",
addl_bare_category_parents = {"places"},
},
["nicknames for states!"] = {
-- For categorizing nicknames for states of e.g. the United States
full_category_link = "[[nicknames]] for [[state]]s",
bare_category_breadcrumb = "states",
bare_category_parent = "nicknames for places",
addl_bare_category_parents = {"states"},
},
["NICKNAME_FOR capital"] = {
link = false,
default = {"Nicknames for cities"},
},
["NICKNAME_FOR city"] = {
link = false,
default = {"Nicknames for cities"},
},
["NICKNAME_FOR continent"] = {
link = false,
default = {"Nicknames for continents"},
},
["NICKNAME_FOR country"] = {
link = false,
default = {"Nicknames for countries"},
},
["NICKNAME_FOR metropolitan city"] = {
-- "metropolitan city" doesn't fall back to "city"
link = false,
default = {"Nicknames for cities"},
},
["NICKNAME_FOR place"] = {
link = false,
default = {"Nicknames for places"},
},
["NICKNAME_FOR prefecture-level city"] = {
-- "prefecture-level city" doesn't fall back to "city" but things like "county-level city" and
-- "subprovincial city" fall back to "prefecture-level city"
link = false,
default = {"Nicknames for cities"},
},
["NICKNAME_FOR state"] = {
link = false,
default = {"Nicknames for states"},
},
["NICKNAME_FOR town"] = {
link = false,
default = {"Nicknames for cities"},
},
---------- Obsolete forms ----------
["obsolete forms of places!"] = {
full_category_link = "{{glossary|obsolete}} [[form]]s of [[name]]s of [[place]]s",
bare_category_breadcrumb = "obsolete forms",
bare_category_parent = "places",
},
["OBSOLETE_FORM_OF place"] = {
link = false,
default = {"Obsolete forms of places"},
},
---------- Official names ----------
["official names of countries!"] = {
full_category_link = "[[official]] [[name]]s of [[country|countries]]",
bare_category_breadcrumb = "countries",
bare_category_parent = "official names of places",
},
["official names of former countries!"] = {
full_category_link = "[[official]] [[name]]s of [[country|countries]] that no longer [[exist]]",
bare_category_breadcrumb = "countries",
bare_category_parent = "official names of former places",
},
["official names of former places!"] = {
full_category_link = "[[official]] [[name]]s of [[place]]s that no longer [[exist]]",
bare_category_breadcrumb = "official names",
bare_category_parent = "former places",
addl_bare_category_parents = {{name = "official names of places", sort = "former"}},
},
["official names of places!"] = {
full_category_link = "[[official]] [[name]]s of [[place]]s",
bare_category_breadcrumb = "official names",
bare_category_parent = "places",
},
["OFFICIAL_NAME_OF country"] = {
link = false,
default = {"Official names of countries"},
},
["OFFICIAL_NAME_OF FORMER country"] = {
link = false,
default = {"Official names of former countries"},
},
["OFFICIAL_NAME_OF FORMER place"] = {
link = false,
default = {"Official names of former places"},
},
["OFFICIAL_NAME_OF place"] = {
link = false,
default = {"Official names of places"},
},
---------- Official nicknames ----------
["official nicknames for places!"] = {
full_category_link = "[[official]] [[nickname]]s for [[place]]s",
bare_category_breadcrumb = "official",
bare_category_parent = "nicknames for places",
},
["official nicknames for states!"] = {
-- For categorizing official nicknames for states of e.g. the United States
full_category_link = "[[official]] [[nicknames]] for [[state]]s",
bare_category_breadcrumb = "official",
bare_category_parent = "nicknames for states",
addl_bare_category_parents = {"states"},
},
["OFFICIAL_NICKNAME_FOR place"] = {
link = false,
default = {"Official nicknames for places"},
},
["OFFICIAL_NICKNAME_FOR state"] = {
link = false,
default = {"Official nicknames for states"},
},
}
export.plural_placetype_to_singular = {}
for sg_placetype, spec in pairs(export.placetype_data) do
if spec.plural then
export.plural_placetype_to_singular[spec.plural] = sg_placetype
end
end
return export
0i5icp7fj8km7659tytty7f31iboenv
88793
88792
2026-08-06T18:28:27Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:place/placetypes]]
88792
Scribunto
text/plain
local export = {}
export.force_cat = false -- set to true for testing
local m_locations = require("Module:place/locations")
local m_links = require("Module:links")
local m_table = require("Module:table")
local m_strutils = require("Module:string utilities")
local debug_track_module = "Module:debug/track"
local en_utilities_module = "Module:en-utilities"
local dump = mw.dumpObject
local insert = table.insert
local concat = table.concat
local internal_error = m_locations.internal_error
export.internal_error = internal_error
local process_error = m_locations.process_error
export.process_error = process_error
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
local ucfirst = m_strutils.ucfirst
local ulower = m_strutils.lower
local rmatch = m_strutils.match
local split = m_strutils.split
--[==[ intro:
This module contains placetype data used by [[Module:place]] and {{tl|place}}, along with a significant amount of code
to work with both placetypes and locations, as well as some placename-related info (FIXME: Consider moving it to
[[Module:place/locations]]). See also [[Module:place/locations]], which has definitions of all known locations. You must
currently load this module using {{cd|require()}}, not using {{cd|mw.loadData()}}.
In particular, it contains two fundamental and tricky functions:
# `get_placetype_equivs`, which finds the equivalent placetypes to look under in order to find a given property, and in
the process correctly handles placetypes with qualifiers (including qualifiers that act similar to "type-raising"
operators in that they do something non-trivial to the placetype to their right) as well as form-of directives and
fallbacks.
# `find_matching_holonym_location`, which looks up a holonym to find a matching known location, but in the process
checks holonyms to the right to make sure there isn't a clash between the user-specified containing holonyms and the
containers of the known location being considered. This is done to prevent overcategorizing when either there are two
known locations with the same name (e.g. Birmingham in England and Birmingham, Alabama in the US), or more generally
two locations with the same name, one of which is a known location but where the other is not (e.g. we're processing
non-known-location Mérida, Spain and don't want it categorized like known location Mérida, Yucatán, Mexico).
Both of these functions are invoked repeatedly, and probably are invoked several times on the same inputs and as a
result are candidates for memoization to speed up the operation of {{tl|place}}.
]==]
------------------------------------------------------------------------------------------
-- Basic utilities --
------------------------------------------------------------------------------------------
--[==[
Return true if `force_cat` is set either in this module or in [[Module:place/locations]].
]==]
function export.get_force_cat()
return export.force_cat or m_locations.force_cat
end
-- Add the page to a tracking "category". To see the pages in the "category",
-- go to [[Wiktionary:Tracking/place/PAGE]] and click on "What links here".
local function track(page)
require(debug_track_module)("place/" .. page)
return true
end
function export.remove_links_and_html(text)
text = m_links.remove_links(text)
return text:gsub("<.->", "")
end
--[==[
Return the singular version of a maybe-plural placetype, or nil if not plural. This correctly handles placetypes with
irregular plurals such as `kibbutzim` plural of `kibbutz` by looking up in a table constructed from the `plural` values
specified in `placetype_data`. If a special plural value is not found, the regular singularization algorithm in
[[Module:en-utilities]] is invoked, which reverses the y -> ies change after vowels and the 'es' addition after sh/ch/x,
and otherwise just subtracts a final 's' (which will incorrectly generate 'passe' for plural 'passes'; FIXME: consider
changing this for words ending in '-sses'). If the generated singular is the same as the passed-in value, nil is
returned.
]==]
function export.maybe_singularize_placetype(placetype)
if not placetype then
return nil
end
if export.plural_placetype_to_singular[placetype] then
return export.plural_placetype_to_singular[placetype]
end
local retval = require(en_utilities_module).singularize(placetype)
if retval == placetype then
return nil
end
return retval
end
-- Return the correct plural of a placetype, and (if `do_ucfirst` is given) make the first letter uppercase. We first
-- look up the plural in `placetype_data`, falling back to pluralize() in [[Module:en-utilities]], which is almost
-- always correct.
function export.pluralize_placetype(placetype, do_ucfirst)
local ptdata = export.placetype_data[placetype]
if ptdata and ptdata.plural then
placetype = ptdata.plural
else
placetype = require(en_utilities_module).pluralize(placetype)
end
if do_ucfirst then
return ucfirst(placetype)
else
return placetype
end
end
--[==[
Get the data associated with a placetype, which may be in its singular or plural form. If `from_category` is specified,
we also look for category-only placetypes (generally plural) followed by `!`. Return three values: (a) the placetype
under which the data can be looked up (i.e. in its singular form if the passed-in `placetype` is plural and did not
match a category-only placetype followed by `!`); (b) the placetype data structure; (c) the type of `placetype` match
that occurred, one of `"direct"` if the canonical placetype is the same as the passed-in `placetype` and also the same
as the key under which `ptdata` was looked up, or `"direct-category"` if the `ptdata` was looked up under a key formed
from the passed-in `placetype` by adding `!`, or `"plural"` if the `ptdata` was looked up under the singularized version
of the plural passed-in `placetype`.
]==]
function export.get_placetype_data(placetype, from_category)
local ptdata = export.placetype_data[placetype]
if ptdata then
return placetype, ptdata, "direct"
end
if from_category then
ptdata = export.placetype_data[placetype .. "!"]
if ptdata then
return placetype .. "!", ptdata, "direct-category"
end
end
local sg_placetype = export.maybe_singularize_placetype(placetype)
if sg_placetype then
ptdata = export.placetype_data[sg_placetype]
if ptdata then
return sg_placetype, ptdata, "plural"
end
end
return nil
end
--[==[
Check for special pseudo-placetypes that should be ignored for categorization purposes.
]==]
function export.placetype_is_ignorable(placetype)
return placetype == "and" or placetype == "or" or placetype:find("^%(")
end
function export.resolve_placetype_aliases(placetype)
return export.placetype_aliases[placetype] or placetype
end
--[==[
Return a property from `placetype_data` for a given placetype. If the placetype isn't found in `placetype_data`, or the
key isn't found in the placetype's entry in `placetype_data`, return nil.
]==]
function export.get_placetype_prop(placetype, key)
-- Usually we are called on equivalent placetypes returned from `get_placetype_equivs`, in which case placetype
-- aliases have been resolved, but sometimes not, e.g. when fetching the indefinite article in
-- get_placetype_article(). `resolve_placetype_aliases` is just a simple lookup and it doesn't hurt to do it twice.
placetype = export.resolve_placetype_aliases(placetype)
if export.placetype_data[placetype] then
return export.placetype_data[placetype][key]
else
return nil
end
end
--[==[
Given a placetype, split the placetype into one or more potential ''splits'', each consisting of a three-element list
{ {``prev_qualifiers``, ``this_qualifier``, ``reduced_placetype``}}, i.e.
# the concatenation of zero or more previously-recognized qualifiers on the left, normally canonicalized (if there are
zero such qualifiers, the value will be nil);
# a single recognized qualifier, normally canonicalized (if there is no qualifier, the value will be nil);
# the "reduced placetype" on the right.
Splitting between the qualifier in (2) and the reduced placetype in (3) happens at each space character, proceeding from
left to right, and stops if a qualifier isn't recognized. All placetypes are canonicalized by checking for aliases
in `placetype_aliases`, but no other checks are made as to whether the reduced placetype is recognized. Canonicalization
of qualifiers does not happen if `no_canon_qualifiers` is specified.
For example, given the placetype `"small beachside unincorporated community"`, the return value will be
{ {
{nil, nil, "small beachside unincorporated community"},
{nil, "small", "beachside unincorporated community"},
{"small", "[[beachfront]]", "unincorporated community"},
{"small [[beachfront]]", "[[unincorporated]]", "community"},
}}
Here, `"beachside"` is canonicalized to `"[[beachfront]]"` and `"unincorporated"` is canonicalized to
`"[[unincorporated]]"`, in both cases according to the entry in `placetype_qualifiers`.
On the other hand, if given `"small former haunted community"`, the return value will be
{ {
{nil, nil, "small former haunted community"},
{nil, "small", "former haunted community"},
{"small", "former", "haunted community"},
}}
because `"small"` and `"former"` but not `"haunted"` are recognized as qualifiers.
Finally, if given `"former adr"`, the return value will be
{ {
{nil, nil, "former adr"},
{nil, "former", "administrative region"},
}}
because `"adr"` is a recognized placetype alias for `"administrative region"`.
]==]
function export.split_qualifiers_from_placetype(placetype, no_canon_qualifiers)
local splits = {{nil, nil, export.resolve_placetype_aliases(placetype)}}
local prev_qualifier = nil
while true do
local qualifier, reduced_placetype = placetype:match("^(.-) (.*)$")
if qualifier then
local canon = export.placetype_qualifiers[qualifier]
if canon == nil then
break
end
local new_qualifier = qualifier
if type(canon) == "table" then
canon = canon.link
end
if not no_canon_qualifiers and canon ~= false then
if canon == true then
new_qualifier = "[[" .. qualifier .. "]]"
else
new_qualifier = canon
end
end
insert(splits, {prev_qualifier, new_qualifier, export.resolve_placetype_aliases(reduced_placetype)})
prev_qualifier = prev_qualifier and prev_qualifier .. " " .. new_qualifier or new_qualifier
placetype = reduced_placetype
else
break
end
end
return splits
end
--[==[
Given a `placetype` (which may be pluralized), return an ordered list of equivalent placetypes to look under to find the
placetype's properties (such as the category or categories to be inserted). The return value is actually an ordered list
of objects of the form `{qualifier=``qualifier``, placetype=``equiv_placetype``}` where ``equiv_placetype`` is a
placetype whose properties to look up, derived from the passed-in placetype or from a contiguous subsequence of the
words in the passed-in placetype (always including the rightmost word in the placetype, i.e. we successively chop off
qualifier words from the left and use the remainder to find equivalent placetypes). ``qualifier`` is the remaining words
not part of the subsequence used to find ``equiv_placetype``; or nil if all words in the passed-in placetype were used
to find ``equiv_placetype``. (FIXME: This qualifier is not currently used anywhere.) Only placetypes for which there is
an entry in `placetype_data` are included. The placetype passed in is always checked first, and will form the first
entry if it exists in `placetype_data`.
'''NOTE:''' This is a tricky function as it implements handling of (a) qualifiers, (b) fallback logic, (c)
"type-raising" qualifiers such as `former`/`ancient`/etc. as well as `fictional` and `mythological`, and (d) form-of
directives, which act somewhat similarly to `former`, and allows interaction between more than one of these
simultaneously (e.g. official names of former places, which have their own categorization).
If {{tl|place}} gets too slow, one potential speedup is to memoize the results of this function, as it appears to be
getting called more than once on the same inputs. Another similar potential speedup is to memoize the results of
`iterate_matching_holonym_location()`.
For example, given the placetype `left tributary`, the following placetype/qualifier combinations are checked in turn:
```
{qualifier = nil, placetype="left tributary"}
{qualifier = "left", placetype="tributary"}
{qualifier = "left", placetype="river"}
```
and the return value will be
{ {
{qualifier = "left", placetype="tributary"},
{qualifier = "left", placetype="river"},
}}
The algorithm first enters the placetype itself into the list, then checks for `left tributary` as a recognized
placetype in `placetype_data` and doesn't find it, so it doesn't enter it into the returned list (if it found it, it
would add it as well as any fallbacks directly after it). It then splits off the recognized qualifier `left` to form the
''reduced placetype'' `tributary`, which is entered into the list because it is found in `placetype_data`. Then, because
it has a fallback `river`, which exists in `placetype_data`, the fallback is entered next.
Another example is `small rural fraziones` (where a ''frazione'' is type of subdivision of a ''comune'' or municipality,
often specifically an outlying hamlet). the placetype/qualifier combinations checked are:
```
{qualifier = nil, placetype="small rural fraziones"}
{qualifier = nil, placetype="small rural frazione"}
{qualifier = "small", placetype="rural fraziones"}
{qualifier = "small", placetype="rural frazione"}
{qualifier = "small [[rural]]", placetype="fraziones"}
{qualifier = "small [[rural]]", placetype="frazione"}
{qualifier = "small [[rural]]", placetype="hamlet"}
{qualifier = "small [[rural]]", placetype="village"}
```
The return value ends up as
{qualifier = "small [[rural]]", placetype="frazione"},
{qualifier = "small [[rural]]", placetype="hamlet"},
{qualifier = "small [[rural]]", placetype="village"},
}}
Here, because the result of singularizing `fraziones` returns a different value from the placetype itself, that
singularized value is checked after the original plural value. Also, in the process of splitting off qualifiers,
they are canonicalized if the entry in `placetype_qualifiers` says to do so; in this case, links are placed around
`rural`. Finally, `frazione` has `hamlet` as its fallback, which in turn has `village` as its fallback, so both
fallbacks end up being returned.
`no_fallback`, if set, disables returning equivalent placetypes based on the `fallback` setting for a placetype. This is
used in the first of two loops in find_placetype_cat_specs() in [[Module:place]] to prefer exact matches for placetypes
such as barangays with later holonyms to matches based on a fallback such as `neighborhood` with an earlier holonym.
See the comment in that function in [[Module:place]] for a more detailed explanation of why this is needed. Only the
placetype itself, and any reduced placetypes created by chopping off recognized qualifiers at the beginning, are
returned; but we do not return reduced placetypes if a containing placetype exists in `placetype_data`. (For example,
`"overseas territory"` has a fallback `"dependent territory"`, and `"overseas"` is also a recognized qualifier. When
`no_fallback` is in place, without the above proviso, we would return `"overseas territory"` followed by `"territory"`
with the incorrect effect of classifying an `"overseas territory"` of the United Kingdom such as `"Gibraltar"` under
[[:Category:Territories of the United Kingdom]] instead of [[:Category:Dependent territories of the United Kingdom]].)
As an exception, if `historical`, `ancient`, `former` or the like are found, they proceed ignoring `no_fallback`,
because it seems tricky to handle them correctly in the presence of `no_fallback`, and historical/former placetypes
rarely occur with exact match category specs anyway.
`no_split_qualifiers` prevents splitting off recognized qualifiers and returning the remainder of the placetype as an
equivalent placetype. Only the passed-in placetype, and any fallbacks, will be returned. This is used in
[[Module:category tree/topic cat/data/Places]] when looking up placetypes found in categories. Such placetypes won't
have qualifiers and so it doesn't make sense to try and look for them.
`from_category`, if set, causes category-only placetypes (those ending in `!`) to also be checked.
`form_of_directive`, if set, causes the specified form-of directive (e.g. `FORMER_NAME_OF`) to be prepended to checked
placetypes, their directive-specific type (e.g. `FORMER_NAME_OF_type`), and their classes (`class`) to get the
appropriate placetypes to check for form-of-directive categories. It falls back to the prepended generic `place` as a
placetype, e.g. `FORMER_NAME_OF place`, if nothing else matches.
`no_check_for_inherently_former` is used internally to prevent an infinite loop when checking for `inherently_former`.
`register_former_as_non_former` is a major hack used in `get_bare_categories` to deal with the mismatch between e.g.
known location `Yugoslavia` declaring itself a `country` but definitions of it declaring it a `former country`. It
causes the non-former version of the specified placetype to be included in the returned equivalents along with the
former placetypes. [FIXME: This should apply only to the entries in `former_countries` but it's tricky to do that now;
fix this in the known-location refactor. -- The known-location refactor is already done but we haven't yet fixed this.]
]==]
function export.get_placetype_equivs(placetype, props)
local no_fallback, no_split_qualifiers, no_check_for_inherently_former, from_category, register_former_as_non_former
local form_of_directive
if props then
no_fallback, no_split_qualifiers, no_check_for_inherently_former, from_category, register_former_as_non_former =
props.no_fallback, props.no_split_qualifiers, props.no_check_for_inherently_former, props.from_category,
props.register_former_as_non_former
form_of_directive = props.form_of_directive
end
local equivs = {}
-- Insert `placetype` into `equivs`, along with any fallback placetypes listed in `placetype_data`. `qualifier` is
-- the preceding qualifier to insert into `equivs` along with the placetype (see comment at top of function). If
-- `from_category` is given, we also check for a category-specific entry consisting of the placetype followed by
-- `!`, and in all cases we also check to see if `placetype` is plural, and if so, insert the singularized version
-- along with its fallbacks (if any) in `placetype_data`. `form_of_prefix` is a form-of prefix such as
-- `OFFICIAL_NAME_OF`. If specified, we check the fallbacks of `placetype` without the prefix but then insert into
-- `equivs` the prefixed placetype. This way, if the user says e.g. {{tl|place|pt|@official name of:Cuba|island country|r/Caribbean}},
-- we will correctly categorize into [[:Category:Official names of countries]], rather than only trying to look up
-- `OFFICIAL_NAME_OF island country` and failing, falling back ultimately to [[:Category:Official names of places]].
local function insert_placetype_and_fallbacks(qualifier, placetype, form_of_prefix)
local function insert_equiv(pt)
if form_of_prefix then
-- Let's say the user says {{tl|place|pt|@official name of:Cuba|island country|r/Caribbean}} and we have
-- no entry for `OFFICIAL_NAME_OF island country` but we do for `OFFICIAL_NAME_OF country` (which we end
-- up processing because `island country` falls back to `country`), and that entry in turn is defined
-- using a fallback. We have to insert that fallback-of-fallback, and the easiest/cleanest way of
-- handling this is by calling ourselves recursively.
insert_placetype_and_fallbacks(qualifier, form_of_prefix .. " " .. pt)
else
insert(equivs, {qualifier=qualifier, placetype=pt})
end
end
-- Insert the placetype, along with any fallbacks.
local canon_placetype, ptdata, ptmatch = export.get_placetype_data(placetype, from_category)
if ptdata then
insert_equiv(canon_placetype)
if no_fallback then
return
end
local first_placetype = #equivs + 1
local prev_placetype = nil
while true do
local pt_value = export.placetype_data[canon_placetype]
if not pt_value then
internal_error("Fallback value %s specified for placetype %s but is not in `placetype_data`",
canon_placetype, prev_placetype)
end
if pt_value.fallback then
insert_equiv(pt_value.fallback)
local last_placetype = #equivs
if last_placetype - first_placetype >= 10 then
local fallback_loop = {}
for i = first_placetype, last_placetype do
insert(fallback_loop, equivs[i].placetype)
end
internal_error("Apparent loop in fallback chain: %s", table.concat(fallback_loop, " -> "))
end
prev_placetype = canon_placetype
canon_placetype = pt_value.fallback
else
break
end
end
end
end
-- Insert `placetype` into `equivs`, along with any fallback placetypes listed in `placetype_data`. This is a
-- wrapper around the more basic `insert_placetype_and_fallbacks()` which handles form-of directives. If there is no
-- form-of directive, this function directly calls `insert_placetype_and_fallbacks()`. We do things this way so that
-- form-of directives correctly combine with `former`-type qualifiers. Note that we also have special backups for
-- form-of directives that check `DIRECTIVE place` (and before that, `DIRECTIVE FORMER/ANCIENT place` is there's a
-- `former`-type directive); these backups live outside this function because we want them done once, late, rather
-- than in each invocation of `process_and_insert_placetype()`.
local function process_and_insert_placetype(qualifier, reduced_placetype)
if form_of_directive then
-- First check for e.g. `OFFICIAL_NAME_OF island country` and its fallbacks; then we look for fallbacks of
-- `island country` and check e.g. `OFFICIAL_NAME_OF country` and its fallbacks. All of this is handled by
-- `insert_placetype_and_fallbacks()` with appropriate parameters. After that, check the general class of
-- the directive, e.g. `subpolity` if something like `district` is given. (Eventually, we check for
-- `OFFICIAL_NAME_OF place` as a backup, but this happens at the end outside the loop over qualifiers.)
insert_placetype_and_fallbacks(qualifier, reduced_placetype, form_of_directive)
if not no_fallback then
local reduced_placetype_equivs = export.get_placetype_equivs(reduced_placetype)
local directive_type = export.get_equiv_placetype_prop_from_equivs(reduced_placetype_equivs,
function(pt) return export.get_placetype_prop(pt, form_of_directive .. "_type") or
export.get_placetype_prop(pt, "class") end
)
if not directive_type then
local pt_data = export.get_equiv_placetype_prop_from_equivs(reduced_placetype_equivs,
function(pt) return export.placetype_data[pt] end
)
if pt_data then
internal_error("For placetype %s in conjunction with form-of directive %s, placetype data " ..
'located but directive-specific type property %s missing, and so is "class"; ' ..
"placetypes searched are %s", reduced_placetype, form_of_directive,
form_of_directive .. "_type", reduced_placetype_equivs)
else
-- This should be allowed, as we allow unrecognized placetypes in general.
end
elseif directive_type ~= "!" then
insert_placetype_and_fallbacks(qualifier, directive_type, form_of_directive)
end
end
else
insert_placetype_and_fallbacks(qualifier, reduced_placetype)
end
end
-- Successively split off recognized qualifiers and loop over successively greater sets of qualifiers from the left
-- (unless `no_split_qualifiers` is specified, in which case we don't check for qualifiers).
local splits
if no_split_qualifiers then
splits = {{nil, nil, export.resolve_placetype_aliases(placetype)}}
else
splits = export.split_qualifiers_from_placetype(placetype)
end
for _, split in ipairs(splits) do
local prev_qualifier, this_qualifier, reduced_placetype = unpack(split, 1, 3)
-- If a special "former" qualifier like `former` or `historical` isn't present, and
-- `no_check_for_inherently_former` is not given (this flag is used to avoid infinite loops), check for
-- "inherently former" placetypes like `satrapy` and `treaty port` that always refer to no-longer-existing
-- placetypes, and handle accordingly.
local unlinked_this_qualifier
if this_qualifier and this_qualifier:find("%[") then
unlinked_this_qualifier = export.remove_links_and_html(this_qualifier)
else
unlinked_this_qualifier = this_qualifier
end
local former_qualifiers = this_qualifier and export.former_qualifiers[unlinked_this_qualifier] or nil
if not former_qualifiers and not no_check_for_inherently_former then
former_qualifiers = export.get_equiv_placetype_prop(reduced_placetype,
function(pt) return export.get_placetype_prop(pt, "inherently_former") end,
{no_check_for_inherently_former = true})
end
-- If a special "former" qualifier like `former` or `historical` is present, map it to the appropriate internal
-- qualifiers (`ANCIENT` and/or `FORMER`, which are written in all-caps to distinguish them from user-specified
-- qualifiers), fetch the `former_type` property, and treat the placetype as if a concatenation of the mapped
-- qualifier(s) and the value of `former_type`. For example, if `medieval village` is given, we map `medieval`
-- to `ANCIENT` and `FORMER`, and `village` to its `former_type` of `settlement`, and enter the placetypes
-- `ANCIENT settlement` and `FORMER settlement` (in that order) into `equivs`. If the placetype following the
-- "former" qualifier is recognized in `placetype_data` but has no `former_type` and no fallback with a
-- `former_type` specified, it is an internal error; but if the placetype isn't recognized (e.g. something like
-- `former greenhouse` is specified and we don't have an entry for `greenhouse`), just track the occurrence and
-- don't enter anything into `equivs`.
if former_qualifiers then
-- FIXME: Should we respect `no_fallback` here? My instinct says no.
local reduced_placetype_equivs = export.get_placetype_equivs(reduced_placetype, {
no_check_for_inherently_former = true
})
local former_type = export.get_equiv_placetype_prop_from_equivs(reduced_placetype_equivs,
function(pt) return export.get_placetype_prop(pt, "former_type") or
export.get_placetype_prop(pt, "class") end
)
if not former_type then
local pt_data = export.get_equiv_placetype_prop_from_equivs(reduced_placetype_equivs,
function(pt) return export.placetype_data[pt] end
)
if pt_data then
internal_error("For placetype %s, placetype data located but `former_type` missing; " ..
"placetypes searched are %s", reduced_placetype, reduced_placetype_equivs)
else
-- Enable error when we've verified there aren't any examples.
track("bad-former-placetype")
track("bad-former-placetype/" .. reduced_placetype)
--process_error("For placetype '%s', unrecognized placetype following 'former'-type " ..
-- "qualifier; searched placetype(s) %s", reduced_placetype, dump(reduced_placetype_equivs))
end
elseif former_type ~= "!" then
-- First check directly for `ANCIENT/FORMER` + the original following placetype. This makes it possible
-- for (e.g.) former provinces of the Roman empire to be categorized specially.
for _, former_qualifier in ipairs(former_qualifiers) do
process_and_insert_placetype(prev_qualifier, former_qualifier .. " " .. reduced_placetype)
end
for _, former_qualifier in ipairs(former_qualifiers) do
process_and_insert_placetype(prev_qualifier, former_qualifier .. " " .. former_type)
end
-- HACK! See explanation above for `register_former_as_non_former`.
if register_former_as_non_former then
process_and_insert_placetype(prev_qualifier, reduced_placetype)
end
-- If we're processing a form-of directive, after doing everything else we do
-- `DIRECTIVE ANCIENT/FORMER place` e.g. `OFFICIAL_NAME_OF FORMER place` as a backup.
if form_of_directive and not no_fallback then
for _, former_qualifier in ipairs(former_qualifiers) do
insert_placetype_and_fallbacks(prev_qualifier, form_of_directive .. " " .. former_qualifier ..
" place")
end
end
-- Don't continue processing equivs. The reason is probably the same as the `break` below for
-- qualifier_to_placetype_equivs[]; categories for `former BLAH` are set using `default`, and
-- non-former equivs will otherwise take precedence.
break
end
end
-- Then see if the rightmost split-off qualifier is in qualifier_to_placetype_equivs
-- (e.g. 'fictional *' -> 'fictional location'). If so, add the mapping.
if this_qualifier and export.qualifier_to_placetype_equivs[unlinked_this_qualifier] then
insert(equivs, {
qualifier=prev_qualifier,
placetype=export.qualifier_to_placetype_equivs[unlinked_this_qualifier]
})
-- Don't continue processing equivs; otherwise, if we specify 'mythological city', even though the
-- equivalent entry for 'mythological location' gets inserted ahead of the entry for 'city', the
-- latter ends up generating the category because the category for 'mythological location' is set as
-- the default value, which is used only when no non-default category can be found.
break
end
-- Finally, join the rightmost split-off qualifier to the previously split-off qualifiers to form a combined
-- qualifier, and add it along with reduced_placetype and any mapping in placetype_data for reduced_placetype.
-- NOTE: The first time through this loop, both `prev_qualifier` and `this_qualifier` are nil, and this inserts
-- the full placetype into `equivs`.
local qualifier = prev_qualifier and prev_qualifier .. " " .. this_qualifier or this_qualifier
process_and_insert_placetype(qualifier, reduced_placetype)
-- If `no_fallback` and there's an entry in `placetype_data` for this placetype, don't include any reduced
-- placetypes to avoid the "overseas territory treated as a territory" issue describe above.
if no_fallback then
local canon_placetype, ptdata, ptmatch = export.get_placetype_data(reduced_placetype, from_category)
if canon_placetype then
break
end
end
end
-- If we're processing a form-of directive, after doing everything else we do `DIRECTIVE place` e.g.
-- `OFFICIAL_NAME_OF place` as a backup; but only if either the placetype as a whole is recognized or the placetype
-- begins with a recognized qualifier. This latter check is to avoid categorizing into e.g.
-- [[Category:en:Former names of places]] in an invocation like
-- {{place|en|@former name of:Democratic Republic of the Congo|country|r/Central Africa|;|used from 1971–1997}};
-- the `used from 1971–1997` gets treated as a placetype and we're called on it.
if form_of_directive and not no_fallback and (splits[2] or export.get_placetype_data(placetype, from_category)) then
insert_placetype_and_fallbacks(nil, form_of_directive .. " place")
end
return equivs
end
function export.get_equiv_placetype_prop_from_equivs(equivs, fun, continue_on_nil_only)
for _, equiv in ipairs(equivs) do
local retval = fun(equiv.placetype)
if continue_on_nil_only and retval ~= nil or not continue_on_nil_only and retval then
return retval, equiv
end
end
return nil, nil
end
--[==[
Given a placetype `placetype` and a function `fun` of one argument, iteratively call the function on equivalent
placetypes fetched from `get_placetype_equivs` until the function returns a non-falsy value (i.e. not {nil} or {false});
but if `continue_on_nil_only` is specified, the iterations continue until the function returns non non-{nil} value.
FIXME: We should make `continue_on_nil_only` the default; but this requires changing some callers.) When `fun` returns a
non-falsy or non-{nil} value, `get_equiv_placetype_prop` returns two values: the value returned by `fun` and the
equivalent placetype that triggered the non-falsy (or non-{nil}) return value. If `fun` never returns a non-falsy (or
non-{nil}) value, `get_equiv_placetype_prop` returns {nil} for both return values. If `placetype` is passed in as {nil},
the return value is the result of calling `fun` on {nil} (whatever it is) with {nil} for the second return value.
]==]
function export.get_equiv_placetype_prop(placetype, fun, props)
if not placetype then
return fun(nil), nil
end
return export.get_equiv_placetype_prop_from_equivs(export.get_placetype_equivs(placetype, props), fun,
props and props.continue_on_nil_only)
end
--[==[
Return the article that is used with an entry placetype. We proceed as follows:
# See if there is a recognized qualifier at the beginning that specifies an article (including `false` for no article).
This takes precedence over anything else, so that e.g. `various capitals` gets no article rather than "`the"`.
# Then check the placetype or any equivalent placetype for the `entry_placetype_use_the` property, indicating that
`"the"` should be used.
# Otherwise we look to see if the placetype itself (not any equivalents, even those involving deleting a qualifier from
the beginning) has an entry in `placetype_data` that specifies the indefinite article using `entry_placetype_use_the`
(principally for use with placetypes like `union territory`).
# Otherwise, we use [[Module:en-utilities]] to apply the standard algorithm to generate `"an"` for words beginning with
a vowel and `"a"` otherwise.
If `ucfirst` is true, the first letter of the article is made upper-case.
]==]
function export.get_placetype_article(placetype, ucfirst)
local art
local qualifier, reduced_placetype = placetype:match("^(.-) (.*)$")
if qualifier then
local canon = export.placetype_qualifiers[qualifier]
if type(canon) == "table" then
art = canon.article
end
end
if art == false then
return art
end
if art == nil then
local placetype_use_the = export.get_equiv_placetype_prop(placetype,
function(pt) return export.get_placetype_prop(pt, "entry_placetype_use_the") end)
if placetype_use_the then
art = "the"
else
art = export.get_placetype_prop(placetype, "entry_placetype_indefinite_article")
if not art then
art = require(en_utilities_module).get_indefinite_article(placetype)
end
end
end
if ucfirst then
art = m_strutils.ucfirst(art)
end
return art
end
--[==[
Return the preposition that should be used after `placetype` when occurring as an entry placetype or in categories
(e.g. `city >in< France` but `country >of< South America`). The preposition defaults to `"in"` if not specified.
]==]
function export.get_placetype_entry_preposition(placetype)
local pt_prep = export.get_equiv_placetype_prop(placetype,
function(pt) return export.get_placetype_prop(pt, "preposition") end
)
return pt_prep or "in"
end
--[==[
Given a place desc (see top of file) and a holonym object (see top of file), add a key/value into the place desc's
`holonyms_by_placetype` field corresponding to the placetype and placename of the holonym. For example, corresponding
to the holonym "c/Italy", a key "country" with the list value {"Italy"} will be added to the place desc's
`holonyms_by_placetype` field. If there is already a key with that place type, the new placename will be added to the
end of the value's list.
]==]
function export.key_holonym_into_place_desc(place_desc, holonym)
if not holonym.placetype then
return
end
-- Key in equivalent placetypes, so that e.g. `cities/San Francisco` gets keyed under `city`; but don't do
-- fallbacks, as it doesn't seem correct for the "do other holonyms of the same placetype" algorithm to do holonyms
-- of different types just because they have the same fallback.
local equiv_placetypes = export.get_placetype_equivs(holonym.placetype, {no_fallback = true})
local unlinked_placename = holonym.unlinked_placename
for _, equiv in ipairs(equiv_placetypes) do
local placetype = equiv.placetype
if not place_desc.holonyms_by_placetype then
place_desc.holonyms_by_placetype = {}
end
if not place_desc.holonyms_by_placetype[placetype] then
place_desc.holonyms_by_placetype[placetype] = {unlinked_placename}
else
insert(place_desc.holonyms_by_placetype[placetype], unlinked_placename)
end
end
end
--[=[
Construct a formatted link from the raw link spec `link` given the canonical singular placetype `sg_placetype`. If the
placetype was originally plural, `orig_placetype` should contain this plural value; otherwise it should be nil. This
will construct the appropriate type of link that displays as `orig_placetype` (or otherwise `sg_placetype`) but links to
whatever the `link` spec specifies (which may be `sg_placetype`, a Wikipedia article, etc.). `ptdata` is the placetype
data structure for the placetype, and `from_category` indicates that we are generating the description of a category
(otherwise we are generating the display form of an entry placetype).
]=]
local function make_placetype_link(link, sg_placetype, orig_placetype, ptdata, from_category, noerror)
if not from_category and ptdata.disallow_in_entries then
if noerror then
return "[not meant to be specified directly, with warning: " .. ptdata.disallow_in_entries .. "]"
else
process_error("Placetype %s is not meant to be specified directly: " .. ptdata.disallow_in_entries, sg_placetype)
end
end
if link == nil then
internal_error("Placetype data present for placetype %s but no link= setting given", sg_placetype)
elseif link == true then
if orig_placetype then
return ("[[%s|%s]]"):format(sg_placetype, orig_placetype)
else
return ("[[%s]]"):format(sg_placetype)
end
elseif link == false then
process_error("Placetype %s is not meant to be specified directly, but is only for internal use", sg_placetype)
elseif link == "w" then
return ("[[w:%s|%s]]"):format(sg_placetype, orig_placetype or sg_placetype)
elseif link == "separately" then
if orig_placetype then
local sg_words = split(sg_placetype, " ")
local orig_words = split(orig_placetype, " ")
if #sg_words ~= #orig_words then
internal_error("Can't construct 'separately' link for plural placetype %s as original placetype %s " ..
"has different number of words", orig_placetype, sg_placetype)
else
for i = 1, #sg_words do
if sg_words[i] == orig_words[i] then
sg_words[i] = ("[[%s]]"):format(sg_words[i])
else
sg_words[i] = ("[[%s|%s]]"):format(sg_words[i], orig_words[i])
end
end
return concat(sg_words, " ")
end
else
return (sg_placetype:gsub("([^ ]+)", "[[%1]]"))
end
elseif link:find("^%+") then
link = link:sub(2) -- discard initial +
return ("[[%s|%s]]"):format(link, orig_placetype or sg_placetype)
elseif not orig_placetype then
return link
else
return require(en_utilities_module).pluralize(link)
end
end
--[==[
Get the display form of a placetype by looking it up in `placetype_data`. If the placetype is recognized, or is the
plural of a recognized placetype, the corresponding linked display form is returned (with plural placetypes displaying
as plural but linked to the singular form of the placetype). Otherwise, return nil. If we're generating the description
of a category, `category_type` should be set to one of `"top-level"` (for top-level categories like
[[:Category:Neighborhoods]]), `"noncity"` (for non-city categories like [[:Category:Neighborhoods in Illinois, USA]]) or
`"city"` (for city categories like [[:Category:Neighborhoods of Chicago]]). Otherwise, we're generating the description
for use in formatting a {{tl|place}} call, and category-only placetypes ending in `!` will be ignored, along with
special `category_link*` settings. `return_full` is used along with `category_type` and will preferably return the
"full" variant of category link settings, i.e. `full_category_link*`; if they don't exist, the `category_link*` value is
prepended with `"names of"`. `noerror` says to not throw an error when encountering entry placetypes that would be
disallowed.
]==]
function export.get_placetype_display_form(placetype, category_type, return_full, noerror)
local from_category = not not category_type
local canon_placetype, ptdata, ptmatch = export.get_placetype_data(placetype, from_category)
if canon_placetype then
local raw_link
local function is_linked_string(str)
return type(str) == "string" and str:find("%[%[")
end
if category_type then
local fetched_full
local function fetch_maybe_full(prop)
local retval = ptdata["full_" .. prop]
if retval ~= nil then
if return_full then
return retval, true
else
internal_error("Saw full_" .. prop .. "=%s but `return_full` not set, can't handle", retval)
end
end
return ptdata[prop], false
end
local function maybe_prefix(str)
if return_full and not fetched_full then
return "names of " .. str
else
return str
end
end
-- Careful with `false` as possible value.
if category_type == "top-level" then
raw_link, fetched_full = fetch_maybe_full("category_link_top_level")
elseif category_type == "noncity" then
raw_link, fetched_full = fetch_maybe_full("category_link_before_noncity")
elseif category_type == "city" then
raw_link, fetched_full = fetch_maybe_full("category_link_before_city")
else
internal_error('Unrecognized value for `category_type` %s, should be "top-level", "noncity" or "city"',
category_type)
end
if type(raw_link) == "string" then
return maybe_prefix(raw_link), ptdata
elseif raw_link ~= nil then
return raw_link, ptdata
end
raw_link, fetched_full = fetch_maybe_full("category_link")
if raw_link == false then
return raw_link, ptdata
end
if is_linked_string(raw_link) then
return maybe_prefix(raw_link), ptdata
end
if ptmatch == "plural" then
raw_link, fetched_full = fetch_maybe_full("plural_link")
if raw_link == false then
return raw_link, ptdata
end
if is_linked_string(raw_link) then
return maybe_prefix(raw_link), ptdata
end
end
if raw_link == nil then
raw_link, fetched_full = fetch_maybe_full("link")
end
if raw_link == false then
return raw_link, ptdata
end
return maybe_prefix(make_placetype_link(raw_link, canon_placetype,
placetype ~= canon_placetype and placetype or nil, ptdata, from_category, noerror)), ptdata
else
if ptmatch == "plural" then
raw_link = ptdata.plural_link
if raw_link == false then
process_error("Placetype %s cannot appear plural", placetype)
end
if is_linked_string(raw_link) then
return raw_link, ptdata
end
end
if raw_link == nil then
raw_link = ptdata.link
end
return make_placetype_link(raw_link, canon_placetype,
placetype ~= canon_placetype and placetype or nil, ptdata, from_category, noerror), ptdata
end
end
return nil
end
local function resolve_unlinked_placename_display_aliases(placetype, placename)
local equiv_placetypes = export.get_placetype_equivs(placetype)
for i, equiv in ipairs(equiv_placetypes) do
equiv_placetypes[i] = equiv.placetype
end
local all_display_aliases_found = {}
local all_others_found = {}
for group, key, spec in m_locations.iterate_matching_location {
placetypes = equiv_placetypes,
placename = placename,
alias_resolution = "display",
} do
if spec.alias_of and spec.display then
insert(all_display_aliases_found, {group, key, spec, spec.display_as_full})
else
insert(all_others_found, {group, key, spec})
end
end
if not all_display_aliases_found[1] then
return placename
elseif all_display_aliases_found[2] then
internal_error("Found multiple matching display aliases for placename %s, placetype %s: " ..
"all_display_aliases_found=%s, all_others_found=%s", placename, placetype, all_display_aliases_found,
all_others_found)
elseif all_others_found[1] then
internal_error("Found a display alias along with other possible meanings for placename %s, placetype %s: " ..
"all_display_aliases_found=%s, all_others_found=%s", placename, placetype, all_display_aliases_found,
all_others_found)
else
local group, key, spec, as_full = unpack(all_display_aliases_found[1])
local full, elliptical = m_locations.key_to_placename(group, key)
return as_full and full or elliptical
end
end
--[==[
If `placename` of type `placetype` is a display alias, convert it to its canonical form; otherwise, return unchanged.
Display aliases transform certain placenames into canonical displayed forms. For example, if any of `country/US`,
`country/USA` or `country/United States of America` (or `c/US`, etc.) are given, the result will be displayed as
`United States`.
'''NOTE''': Display aliases change what is displayed from what the editor wrote in the Wikitext. As a result, they
should (a) be non-political in nature, and (b) not involve a change where the word `the` needs to be added or removed.
For example, normalizing `US` and `USA` to `United States` for display purposes is OK but normalizing `Burma` to
`Myanmar` is not (instead a cat alias should be used) because the terms `Burma` and `Myanmar` have clear political
connotations. Similarly, we have a display alias that maps the old name of `Macedonia` as a country (but not a region!)
to `North Macedonia`, but `Republic of Macedonia` is mapped to `North Macedonia` only as a cat alias because the two
terms differ in their use of `the`. (For example, if we had a display alias mapping `Republic of Macedonia` to
`North Macedonia`, the call {{tl|place|en|the <<capital city>> of the <<c/Republic of Macedonia>>}} would wrongly
display as `the [[capital city]] of the [[North Macedonia]]`.) Generally, display normalizations tend to involve
alternative forms (e.g. abbreviations, ellipses, foreign spellings) where the normalization improves clarity and
consistency.
]==]
function export.resolve_placename_display_aliases(placetype, placename)
-- If the placename is a link, apply the alias inside the link.
-- This pattern matches both piped and unpiped links. If the link is not piped, the second capture (linktext) will
-- be empty.
local link, linktext = rmatch(placename, "^%[%[([^|%[%]]+)|?([^|%[%]]-)%]%]$")
if link then
if linktext ~= "" then
local alias = resolve_unlinked_placename_display_aliases(placetype, linktext)
return "[[" .. link .. "|" .. alias .. "]]"
else
local alias = resolve_unlinked_placename_display_aliases(placetype, link)
return "[[" .. alias .. "]]"
end
else
return resolve_unlinked_placename_display_aliases(placetype, placename)
end
end
--[==[
Generate the "prefixed" version of a bare key, i.e. prefix it with `the` if correct for this key.
]==]
function export.get_prefixed_key(key, spec)
if spec.the then
return "the " .. key
else
return key
end
end
-- Necessary for use by [[Module:place]]. FIXME: Reorganize the modules so this isn't necessary.
export.iterate_matching_location = m_locations.iterate_matching_location
--[=[
Iterator that iterates over holonyms in `place_desc`. If `first_holonym_index` is given, start iterating at the
specified holonym and stop either when there are no more holonyms or a holonym with modifier `:also` is found. If
`first_holonym_index` is nil or omitted, iterate over all holonyms regardless. If `include_raw_text_holonyms` is
specified, raw text holonyms (those not of the form `placetype/placename`) are returned as well; they can be identified
by the fact that the `placetype` field in the holonym structure is nil. Two values are returned at each iteration, the
holonym index and holonym structure, similar to `ipairs()`.
]=]
function export.get_holonyms_to_check(place_desc, first_holonym_index, include_raw_text_holonyms)
local stop_at_also = not not first_holonym_index
return function(place_desc, index)
while true do
index = index + 1
local this_holonym = place_desc.holonyms[index]
-- If we were passed in a starting holonym index, go up to but not including a holonym marked with `:also`
-- (continue_cat_loop); the categorization code will then restart the loop at that holonym. That holonym
-- will have `:also` marked on it, so make sure not to stop immediately if the first holonym is marked with
-- `:also`.
if not this_holonym or stop_at_also and index > first_holonym_index and this_holonym.continue_cat_loop then
return nil
end
-- If not placetype, we're processing raw text, which we normally want to skip.
if include_raw_text_holonyms or this_holonym.placetype then
return index, this_holonym
end
end
end, place_desc, first_holonym_index and first_holonym_index - 1 or 0
end
--[==[
If the holonym in `data` (in the format as passed to a category handler) refers to a known location, iterate over all
such known locations, returning for each location the corresponding key, spec and group as well as the trail of
ancestral containers. Unlike `iterate_matching_location()`, this specifically checks that there is no mismatch between
the location's containers at any level and any of the following holonyms in the {{tl|place}} spec. The fields in `data`
are:
* `holonym_placetype`: The placetype of the holonym. It can actually be a list of possible placetypes, as with
`iterate_matching_location()`.
* `holonym_placename`: The placename of the holonym.
* `holonym_index`: The index of the holonym among the holonyms in `place_desc`, or nil if the holonym is not among the
holonyms in `place_desc`. (If a holonym index is given, we check for container mismatches among the holonyms
following the specified index, stopping either when encountering a holonym marked with modifier `:also` or, if none
exist, when we run out of holonyms. If no holonym index is given, we check all holonyms for container mismatches.)
* `place_desc`: Description of the place; used for the holonyms, to check for container mismatches.
Returns four values: the location group, the canonical key by which the location is known, the spec object describing
the location and the trail of ancestral containers for the location. The first three values are the same as for
`iterate_matching_location`.
]==]
function export.iterate_matching_holonym_location(data)
local holonym_placetype, holonym_placename, holonym_index, place_desc =
data.holonym_placetype, data.holonym_placename, data.holonym_index, data.place_desc
local matching_location_iterator = m_locations.iterate_matching_location {
placetypes = holonym_placetype,
placename = holonym_placename,
}
return function()
while true do
local group, key, spec = matching_location_iterator()
if not group then
return nil
end
local container_trail = {}
-- For each level of container, check that there are no mismatches (i.e. other location of the same
-- placetype) mentioned. We allow a mismatch at a given level if there's also a match with the container
-- at that level. For example, in the case of Kansas City, defined in [[Module:place/locations]] as a city
-- in Missouri, if we define it as {{tl|place|city|s/Missouri,Kansas}}, we ignore the mismatching state of
-- Kansas because the correct state of Missouri was also mentioned. But imagine we are defining Newark,
-- Delaware as {{tl|place|city|s/Delaware|c/US}} and (as is the case) we have an entry for Newark, New
-- Jersey in [[Module:place/locations]]. Just because the containing location `US` matches isn't enough,
-- because Newark, NJ also has New Jersey as a containing location and there's a mismatch at that level. If
-- there are no mismatches at any level we assume we're dealing with the right known location.
--
-- If at a given level there are multiple containing locations, we count a match if any holonym matches any
-- containing location, and a mismatch only if a holonym exists of the same placetype that doesn't match any
-- containing location.
local containers_mismatch = false
for containers in m_locations.iterate_containers(group, key, spec) do
insert(container_trail, containers)
local match_at_level = false
local mismatch_at_level = false
for other_holonym_index, other_holonym in export.get_holonyms_to_check(place_desc,
holonym_index and holonym_index + 1 or nil) do
local other_source_holonym = other_holonym.augmented_from_holonym
if other_source_holonym and other_source_holonym.placetype == holonym_placetype and
other_source_holonym.unlinked_placename ~= holonym_placename then
-- Ignore holonyms added during the augmentation process for other holonyms of the same
-- placetype as the placetype of the holonym we're considering. See comment in
-- augment_holonyms_with_container() for why we do this.
-- continue; grrr, no 'continue' in Lua
else
local holonym_matches_at_level = false
local holonym_exists_with_same_placetype = false
for _, container in ipairs(containers) do
if not container.spec.no_check_holonym_mismatch then
local full_container_placename, elliptical_container_placename =
m_locations.key_to_placename(container.group, container.key)
local placetypes = container.spec.placetype
if type(placetypes) ~= "table" then
placetypes = {placetypes}
end
local placetype_equivs = {}
for _, pt in ipairs(placetypes) do
m_table.extend(placetype_equivs, export.get_placetype_equivs(pt))
end
local this_holonym_matches = export.get_equiv_placetype_prop_from_equivs(
placetype_equivs, function(placetype)
return other_holonym.placetype == placetype and
(other_holonym.unlinked_placename == full_container_placename or
other_holonym.unlinked_placename == elliptical_container_placename)
end
)
if this_holonym_matches then
holonym_matches_at_level = true
break
end
local this_holonym_exists_with_same_placetype = export.get_equiv_placetype_prop_from_equivs(
placetype_equivs, function(placetype)
return other_holonym.placetype == placetype
end
)
if this_holonym_exists_with_same_placetype then
-- We seem to have a mismatch at this level. But before we decide conclusively that this
-- is the case, check to see whether the putative mismatch is an alias and matches when
-- we resolve the alias.
for oh_group, oh_key, oh_spec, oh_container_trail in
export.iterate_matching_holonym_location {
holonym_placetype = other_holonym.placetype,
holonym_placename = other_holonym.unlinked_placename,
holonym_index = other_holonym_index,
place_desc = place_desc,
} do
local oh_full_placename, oh_elliptical_placename =
m_locations.key_to_placename(oh_group, oh_key)
if oh_full_placename == full_container_placename or
oh_elliptical_placename == elliptical_container_placename then
-- Alias matched when resolved.
this_holonym_matches = true
break
end
end
if this_holonym_matches then
-- Alias matched above when resolved.
holonym_matches_at_level = true
break
else
-- Not an alias, or doesn't match when resolved. We have a true mismatch.
holonym_exists_with_same_placetype = true
end
end
end
end
if holonym_matches_at_level then
match_at_level = true
break
end
if holonym_exists_with_same_placetype then
mismatch_at_level = true
end
end
end
if not match_at_level and mismatch_at_level then
containers_mismatch = true
break
end
end
if not containers_mismatch then
return group, key, spec, container_trail
end
end
end
end
--[==[
If the holonym in `data` (in the format as passed to a category handler) refers to a known location, find and return the
corresponding key, spec and group as well as the trail of ancestral containers. This is like
`iterate_matching_holonym_location()` but throws an error if more than one location matches. (An example where this
would happen is {{tl|place|en|neighborhood|city/Newcastle}}, because there are two known locations named Newcastle. To
fix this, specify additional following disambiguating holonyms, e.g.
{{tl|place|en|neighborhood|city/Newcastle|s/New South Wales}}.
]==]
function export.find_matching_holonym_location(data)
local all_found = {}
for group, key, spec, container_trail in export.iterate_matching_holonym_location(data) do
insert(all_found, {group, key, spec, container_trail})
end
if not all_found[1] then
return nil
elseif all_found[2] then
local holonym_placetype = data.holonym_placetype
if type(holonym_placetype) == "table" then
holonym_placetype = concat(holonym_placetype, ",")
end
local found_keys = {}
for _, found in ipairs(all_found) do
local _, key, _, _ = unpack(found)
insert(found_keys, key)
end
error(("Found multiple matching locations for holonym '%s/%s'; specify disambiguating context in the " ..
"containing holonyms: %s"):format(holonym_placetype, data.holonym_placename, dump(found_keys)))
else
return unpack(all_found[1])
end
end
------------------------------------------------------------------------------------------
-- Placename and placetype data --
------------------------------------------------------------------------------------------
--[==[ var:
This is a map from aliases to their canonical forms. Any placetypes appearing as keys here will be mapped to their
canonical forms in all respects, including the display form. Contrast entries in 'placetype_data' with a fallback, which
applies to categorization and other processes but not to display.
The most important aliases are for holonym placetypes, particularly those that occur often such as "country", "state",
"province" and the like. Particularly long placetypes that mostly occur as entry placetypes (e.g.
"census-designated place") can be given abbreviations, but it is generally preferred to spell out the entry placetype.
Note also that we purposely avoid certain abbreviations that would be ambiguous (e.g. "d", which could variously be
interpreted as "department", "district" or "division").
]==]
export.placetype_aliases = {
["acomm"] = "autonomous community",
["adr"] = "administrative region",
["adterr"] = "administrative territory", -- Pakistan
["aobl"] = "autonomous oblast",
["aokr"] = "autonomous okrug",
["ap"] = "autonomous province",
["apref"] = "autonomous prefecture",
["aprov"] = "autonomous province",
["ar"] = "autonomous region",
["arch"] = "archipelago",
["arep"] = "autonomous republic",
["aterr"] = "autonomous territory",
["atu"] = "autonomous territorial unit",
["bor"] = "borough",
["c"] = "country",
["can"] = "canton",
["carea"] = "council area",
["cc"] = "constituent country",
["cdblock"] = "community development block",
["cdep"] = "Crown dependency",
["CDP"] = "census-designated place",
["cdp"] = "census-designated place",
["clcity"] = "county-level city",
["co"] = "county",
["cobor"] = "county borough",
["colcity"] = "county-level city",
["coll"] = "collectivity",
["comm"] = "community",
["cont"] = "continent",
["contr"] = "continental region",
["contregion"] = "continental region",
["cpar"] = "civil parish",
["damun"] = "direct-administered municipality",
["dep"] = "dependency",
["department capital"] = "departmental capital",
["dept"] = "department",
["depterr"] = "dependent territory",
["dist"] = "district",
["distmun"] = "district municipality",
["div"] = "division",
["emp"] = "empire",
["fpref"] = "French prefecture",
["gov"] = "governorate",
["govnat"] = "governorate",
["home-rule city"] = "home rule city",
["home-rule municipality"] = "home rule municipality",
["inner-city area"] = "inner city area",
["ires"] = "Indian reservation",
["isl"] = "island",
["lbor"] = "London borough",
["lga"] = "local government area",
["lgarea"] = "local government area",
["lgd"] = "local government district",
["lgdist"] = "local government district",
["metbor"] = "metropolitan borough",
["metcity"] = "metropolitan city",
["metmun"] = "metropolitan municipality",
["mtn"] = "mountain",
["mun"] = "municipality",
["mundist"] = "municipal district",
["nonmetropolitan county"] = "non-metropolitan county",
["obl"] = "oblast",
["okr"] = "okrug",
["p"] = "province",
["par"] = "parish",
["parmun"] = "parish municipality",
["pen"] = "peninsula",
["plcity"] = "prefecture-level city",
["plcolony"] = "Polish colony",
["pref"] = "prefecture",
["prefcity"] = "prefecture-level city",
["preflcity"] = "prefecture-level city",
["prov"] = "province",
["r"] = "region",
["range"] = "mountain range",
["rcm"] = "regional county municipality",
["rcomun"] = "regional county municipality",
["rdist"] = "regional district",
["rep"] = "republic",
["rhrom"] = "rural hromada",
["riv"] = "river",
["rmun"] = "regional municipality",
["robor"] = "royal borough",
["romp"] = "Roman province",
["runit"] = "regional unit",
["rurmun"] = "rural municipality",
["s"] = "state",
["sar"] = "special administrative region",
["shrom"] = "settlement hromada",
["spref"] = "subprefecture",
["sprefcity"] = "sub-prefectural city",
["sprovcity"] = "subprovincial city",
["submet city"] = "sub-metropolitan city",
["submetropolitan city"] = "sub-metropolitan city",
["sub-prefecture-level city"] = "sub-prefectural city",
["sub-provincial city"] = "subprovincial city",
["sub-provincial district"] = "subprovincial district",
["terr"] = "territory",
["terrauth"] = "territorial authority",
["twp"] = "township",
["twpmun"] = "township municipality",
["uauth"] = "unitary authority",
["ucomm"] = "unincorporated community",
["udist"] = "unitary district",
["uhrom"] = "urban hromada",
["uterr"] = "union territory",
["utwpmun"] = "united township municipality",
["val"] = "valley",
["vdc"] = "village development committee",
["vil"] = "village",
["voi"] = "voivodeship",
["wcomm"] = "Welsh community",
}
local no_link_def_article = {link = false, article = "the"}
local no_link_no_article = {link = false, article = false}
--[==[ var:
These qualifiers can be prepended onto any placetype and will be handled correctly. For example, the placetype
`large city` will be displayed as `large <nowiki>[[city]]</nowiki>` and categorized as if `city` were specified. If the
value in the following table is a string, the qualifier will display according to the string. If the value is `true`,
the qualifier will be linked to its corresponding Wiktionary entry. If the value is `false`, the qualifier will not be
linked but will appear as-is. Note that these qualifiers do not override placetypes with entries elsewhere that contain
those same qualifiers. For example, the entry for `inland sea` in `placetype_data` will apply in preference to treating
`inland sea` as equivalent to `sea`.
]==]
export.placetype_qualifiers = {
-- generic qualifiers
["huge"] = false,
["tiny"] = false,
["large"] = false,
["big"] = false,
["mid-size"] = false,
["mid-sized"] = false,
["small"] = false,
["sizable"] = false,
["important"] = false,
["long"] = false,
["short"] = false,
["major"] = false,
["minor"] = false,
["high"] = false,
["tall"] = false,
["low"] = false,
["left"] = false, -- left tributary
["right"] = false, -- right tributary
["modern"] = false, -- for use in opposition to "ancient" in another definition
-- "former" qualifiers
["abandoned"] = true,
["ancient"] = true,
["deserted"] = true,
["extinct"] = true,
["former"] = false,
["historic"] = "historical",
["historical"] = true,
["medieval"] = true,
["mediaeval"] = true,
["ruined"] = true,
["traditional"] = true,
-- sea qualifiers
["coastal"] = true,
["inland"] = true, -- note, we also have an entry in placetype_data for 'inland sea' to get a link to [[inland sea]]
["maritime"] = true,
["overseas"] = true,
["seaside"] = true,
["beachfront"] = true,
["beachside"] = true,
["riverside"] = true,
-- lake qualifiers
["freshwater"] = true,
["saltwater"] = true,
["endorheic"] = true,
["oxbow"] = true,
["ox-bow"] = "[[oxbow]]", -- [[ox-bow]] is a red link
["tidal"] = true,
-- land qualifiers
["hilltop"] = true,
["hilly"] = true,
["insular"] = true,
["peninsular"] = true,
["chalk"] = true,
["karst"] = true,
["limestone"] = true,
["mountainous"] = true,
["mountaintop"] = true,
["alpine"] = true,
["volcanic"] = true, -- for an island
-- political status qualifiers
["autonomous"] = true,
["incorporated"] = true,
["special"] = true,
["unincorporated"] = true,
["coterminous"] = true,
-- monetary status/etc. qualifiers
["fashionable"] = true,
["wealthy"] = true,
["affluent"] = true,
["declining"] = true,
-- city vs. rural qualifiers
["urban"] = true,
["suburban"] = true,
["exurban"] = true,
["outlying"] = true,
["remote"] = true,
["rural"] = true,
["outback"] = true,
["inner"] = false,
["inner-city"] = true,
["central"] = false,
["outer"] = false,
-- land use qualifiers
["residential"] = true,
["agricultural"] = true,
["business"] = true,
["commercial"] = true,
["industrial"] = true,
-- business use qualifiers
["railroad"] = true,
["railway"] = true,
["farming"] = true,
["fishing"] = true,
["mining"] = true,
["logging"] = true,
["cattle"] = true,
-- tourism use qualifiers
["resort"] = true, -- note, we also have 'resort city' and 'resort town', that take precedecne
["spa"] = true, -- note, we also have 'spa city' and 'spa town', that take precedecne
["ski"] = true, -- note, we also have 'ski resort city' and 'ski resort town', that take precedecne
-- religious qualifiers
["holy"] = true,
["sacred"] = true,
["religious"] = true,
["secular"] = true,
-- qualifiers for nonexistent places
["claimed"] = false,
["fictional"] = true,
["legendary"] = true,
["mythical"] = true,
["mythological"] = true,
-- directional qualifiers
["northern"] = false,
["southern"] = false,
["eastern"] = false,
["western"] = false,
["north"] = false,
["south"] = false,
["east"] = false,
["west"] = false,
["northeastern"] = false,
["southeastern"] = false,
["northwestern"] = false,
["southwestern"] = false,
["northeast"] = false,
["southeast"] = false,
["northwest"] = false,
["southwest"] = false,
-- seasonal qualifiers
["summer"] = true, -- e.g. for 'summer capital'
["winter"] = true,
-- legal status qualifiers
-- FIXME: Two-word qualifiers don't work yet. But you can enter "de-facto" and it's canonicalized to [[de facto]].
["official"] = true,
["unofficial"] = true,
["de facto"] = true, -- 'de facto capital'
["de-facto"] = "[[de facto]]", -- [[de-facto]] is a red link
["de jure"] = true, -- 'de jure capital'
["de-jure"] = "[[de jure]]", -- [[de-jure]] is a red link
-- NOTE: 'unrecognized/unrecognised' are handled as placetypes 'unrecognized country', 'unrecognized state'
-- misc. qualifiers
["planned"] = true,
["chartered"] = true,
["landlocked"] = true,
["uninhabited"] = true,
-- superlative qualifiers
["first"] = no_link_def_article,
["second"] = no_link_def_article, -- for "second largest" etc.
["third"] = no_link_def_article,
["fourth"] = no_link_def_article,
["last"] = no_link_def_article,
["only"] = no_link_def_article,
["sole"] = no_link_def_article,
["main"] = no_link_def_article,
["largest"] = no_link_def_article,
["biggest"] = no_link_def_article,
["smallest"] = no_link_def_article,
["shortest"] = no_link_def_article,
["longest"] = no_link_def_article,
["tallest"] = no_link_def_article,
["highest"] = no_link_def_article,
["lowest"] = no_link_def_article,
["leftmost"] = no_link_def_article,
["rightmost"] = no_link_def_article,
["innermost"] = no_link_def_article,
["outermost"] = no_link_def_article,
["northernmost"] = no_link_def_article,
["southernmost"] = no_link_def_article,
["westernmost"] = no_link_def_article,
["easternmost"] = no_link_def_article,
["northwesternmost"] = no_link_def_article,
["southwesternmost"] = no_link_def_article,
["northeasternmost"] = no_link_def_article,
["southeasternmost"] = no_link_def_article,
-- several/various
["several"] = no_link_no_article,
["various"] = no_link_no_article,
["numerous"] = no_link_no_article,
["multiple"] = no_link_no_article,
["many"] = no_link_no_article,
["other"] = no_link_no_article,
}
--[==[ var:
In this table, the key qualifiers should be treated the same as the value qualifiers for categorization purposes. This
is overridden by `placetype_data` and `qualifier_to_placetype_equivs`.
]==]
export.former_qualifiers = {
["abandoned"] = {"FORMER"},
["ancient"] = {"ANCIENT", "FORMER"},
["former"] = {"FORMER"},
["extinct"] = {"FORMER"},
["historic"] = {"FORMER"},
["historical"] = {"FORMER"},
["medieval"] = {"ANCIENT", "FORMER"},
["mediaeval"] = {"ANCIENT", "FORMER"},
["ruined"] = {"ANCIENT", "FORMER"},
["traditional"] = {"FORMER"},
}
--[==[ var:
In this table, any placetypes containing these qualifiers that do not occur in `placetype_data` should be mapped to the
specified placetypes for categorization purposes. Entries here are overridden by `placetype_data`.
]==]
export.qualifier_to_placetype_equivs = {
["fictional"] = "fictional location",
["legendary"] = "mythological location",
["mythical"] = "mythological location",
["mythological"] = "mythological location",
-- For e.g. Taiwan as a "claimed province" of China; parts of Belize as claimed by Guatemala; various islands
-- claimed by various parties in East Asia. FIXME: We should conditionalize on what is being claimed since there are
-- also claimed capitals, e.g. Israel and Palestine claim Jerusalem as their capital.
["claimed"] = "claimed political division",
}
--[==[ var:
Mapping from placetypes to the corresponding plural category-only placetype for a capital of that placetype. The reverse
mapping also exists.
]==]
export.placetype_to_capital_cat = {
["autonomous community"] = "autonomous community capitals",
["canton"] = "cantonal capitals",
["comarca"] = "comarca capitals",
["country"] = "national capitals",
-- The following are not obviously different from 'county seats' but the latte terminology is used in the US.
["county"] = "county capitals",
["department"] = "departmental capitals",
["district"] = "district capitals",
["division"] = "division capitals",
["emirate"] = "emirate capitals",
["governorate"] = "governorate capitals",
["hromada"] = "hromada capitals",
["krai"] = "krai capitals",
["metropolitan city"] = "metropolitan city capitals",
["municipality"] = "municipal capitals",
["oblast"] = "oblast capitals",
["okrug"] = "okrug capitals",
["prefecture"] = "prefectural capitals",
["province"] = "provincial capitals",
["raion"] = "raion capitals",
["regency"] = "regency capitals",
["region"] = "regional capitals",
["regional unit"] = "regional unit capitals",
["republic"] = "republic capitals",
["state"] = "state capitals",
["territory"] = "territorial capitals",
["voivodeship"] = "voivodeship capitals",
}
--[==[ var:
This contains placenames that should be preceded by an article (almost always "the"). '''NOTE''': There are multiple
ways that placenames can come to be preceded by "the":
# Listed here.
# Given in [[Module:place/locations]] with an initial "the". All such placenames are added to this map by the code
just below the map.
# The placetype of the placename has `holonym_use_the = true` in its placetype_data.
# A regex in placename_the_re matches the placename.
Note that "the" is added only before the first holonym in a place description.
]==]
export.placename_article = {
-- This should only contain info that can't be inferred from [[Module:place/locations]].
["archipelago"] = {
["Cyclades"] = "the",
["Dodecanese"] = "the",
},
["country"] = {
["Holy Roman Empire"] = "the",
},
["empire"] = {
["Holy Roman Empire"] = "the",
},
["island"] = {
["North Island"] = "the",
["South Island"] = "the",
},
["region"] = {
["Balkans"] = "the",
["Russian Far East"] = "the",
["Caribbean"] = "the",
["Caucasus"] = "the",
["Middle East"] = "the",
["New Territories"] = "the",
["North Caucasus"] = "the",
["South Caucasus"] = "the",
["West Bank"] = "the",
["Gaza Strip"] = "the",
},
["valley"] = {
["San Fernando Valley"] = "the",
},
}
--[==[ var:
Regular expressions to apply to determine whether we need to put 'the' before a holonym. The key "*" applies to all
holonyms, otherwise only the regexes for the holonym's placetype apply.
]==]
export.placename_the_re = {
-- We don't need entries for peninsulas, seas, oceans, gulfs or rivers
-- because they have holonym_use_the = true.
["*"] = {"^Isle of ", " Islands$", " Mountains$", " Empire$", " Country$", " Region$", " District$", "^City of "},
["bay"] = {"^Bay of "},
["lake"] = {"^Lake of "},
["country"] = {"^Republic of ", " Republic$"},
["republic"] = {"^Republic of ", " Republic$"},
["region"] = {" [Rr]egion$"},
["river"] = {" River$"},
["local government area"] = {"^Shire of "},
["county"] = {"^Shire of "},
["Indian reservation"] = {" Reservation", " Nation"},
["tribal jurisdictional area"] = {" Reservation", " Nation"},
}
--[==[ var:
If any of the following holonyms are present, the associated holonyms are automatically added to the end of the list of
holonyms for categorization (but not display) purposes.
]==]
export.cat_implications = {
["region"] = {
["Eastern Europe"] = {"continent/Europe"},
["Central Europe"] = {"continent/Europe"},
["Western Europe"] = {"continent/Europe"},
["South Europe"] = {"continent/Europe"},
["Southern Europe"] = {"continent/Europe"},
["Northern Europe"] = {"continent/Europe"},
["Northeast Europe"] = {"continent/Europe"},
["Northeastern Europe"] = {"continent/Europe"},
["Southeast Europe"] = {"continent/Europe"},
["Southeastern Europe"] = {"continent/Europe"},
["North Caucasus"] = {"continent/Europe"},
["South Caucasus"] = {"continent/Asia"},
["South Asia"] = {"continent/Asia"},
["Southern Asia"] = {"continent/Asia"},
["East Asia"] = {"continent/Asia"},
["Eastern Asia"] = {"continent/Asia"},
["Central Asia"] = {"continent/Asia"},
["West Asia"] = {"continent/Asia"},
["Western Asia"] = {"continent/Asia"},
["Southeast Asia"] = {"continent/Asia"},
["North Asia"] = {"continent/Asia"},
["Northern Asia"] = {"continent/Asia"},
["Anatolia"] = {"continent/Asia"},
["Asia Minor"] = {"continent/Asia"},
["Mesopotamia"] = {"continent/Asia"},
["North Africa"] = {"continent/Africa"},
["Central Africa"] = {"continent/Africa"},
["West Africa"] = {"continent/Africa"},
["East Africa"] = {"continent/Africa"},
["Southern Africa"] = {"continent/Africa"},
["Central America"] = {"continent/Central America"},
["Caribbean"] = {"continent/North America"},
["Polynesia"] = {"continent/Oceania"},
["Micronesia"] = {"continent/Oceania"},
["Melanesia"] = {"continent/Oceania"},
["Siberia"] = {"country/Russia", "continent/Asia"},
["Russian Far East"] = {"country/Russia", "continent/Asia"},
["South Wales"] = {"constituent country/Wales", "continent/Europe"},
["Balkans"] = {"continent/Europe"},
["West Bank"] = {"country/Palestine", "continent/Asia"},
["Gaza"] = {"country/Palestine", "continent/Asia"},
["Gaza Strip"] = {"country/Palestine", "continent/Asia"},
}
}
------------------------------------------------------------------------------------------
-- Category and display handlers --
------------------------------------------------------------------------------------------
local function city_type_cat_handler(data)
local entry_placetype = data.entry_placetype
local generic_before_non_cities = export.get_placetype_prop(entry_placetype, "generic_before_non_cities")
if not generic_before_non_cities then
internal_error("city_type_cat_handler called on placetype %s that doesn't have a `generic_before_non_cities`" ..
" setting", entry_placetype)
end
local plural_entry_placetype = export.pluralize_placetype(entry_placetype)
local group, key, spec, container_trail = export.find_matching_holonym_location(data)
if group and not spec.is_former_place and not spec.is_city then
-- Categorize both in key, and in the larger polity that the key is part of, e.g. [[Hirakata]] goes in both
-- "Cities in Osaka Prefecture" and "Cities in Japan". (But don't do the latter if no_container_cat is set.)
local cap_plural_entry_placetype = ucfirst(plural_entry_placetype)
local retcats = {("%s %s %s"):format(cap_plural_entry_placetype, generic_before_non_cities,
export.get_prefixed_key(key, spec))}
if container_trail[1] and not spec.no_container_cat then
for _, container in ipairs(container_trail[1]) do
insert(retcats, ("%s %s %s"):format(cap_plural_entry_placetype, generic_before_non_cities,
export.get_prefixed_key(container.key, container.spec)))
end
end
return retcats
end
end
local function capital_city_cat_handler(data, non_city)
local holonym_placetype, holonym_placename, holonym_index, place_desc =
data.holonym_placetype, data.holonym_placename, data.holonym_index, data.place_desc
-- The first time we're called we want to return something; otherwise we will be called for later-mentioned
-- holonyms, which can result in wrongly classifying into e.g. `National capitals`. Simulate the loop in
-- find_placetype_cat_specs() over holonyms so we get the proper `Cities in ...` categories as well as the capital
-- category/categories we add below.
local retcats
if not non_city and place_desc.holonyms then
for h_index, holonym in export.get_holonyms_to_check(place_desc, holonym_index) do
local h_placetype, h_placename = holonym.placetype, holonym.unlinked_placename
retcats = city_type_cat_handler {
entry_placetype = "city",
holonym_placetype = h_placetype,
holonym_placename = h_placename,
holonym_index = h_index,
place_desc = place_desc,
}
if retcats then
break
end
end
end
if not retcats then
retcats = {}
end
-- Now find the appropriate capital-type category for the placetype of the holonym, e.g. 'State capitals'. If we
-- recognize the holonym among the known holonyms in [[Module:place/locations]], also add a category like 'State
-- capitals of the United States'. Truncate e.g. 'autonomous region' to 'region', 'union territory' to 'territory'
-- when looking up the type of capital category, if we can't find an entry for the holonym placetype itself (there's
-- an entry for 'autonomous community').
local capital_cat = export.placetype_to_capital_cat[holonym_placetype]
if not capital_cat then
capital_cat = export.placetype_to_capital_cat[holonym_placetype:gsub("^.* ", "")]
end
if capital_cat then
capital_cat = ucfirst(capital_cat)
local inserted_specific_variant_cat = false
if holonym_index then
-- Now find the first recognized holonym location. We don't stop when :also is seen because of the common pattern
-- where we use :also to specify that a given city is the capital at multiple surrounding levels.
local matching_group, matching_key, matching_spec, matching_container_trail, matching_holonym_index
for h_index = holonym_index, #place_desc.holonyms do
if place_desc.holonyms[h_index].placetype then
matching_group, matching_key, matching_spec, matching_container_trail = export.find_matching_holonym_location {
holonym_placetype = place_desc.holonyms[h_index].placetype,
holonym_placename = place_desc.holonyms[h_index].unlinked_placename,
holonym_index = h_index,
place_desc = place_desc,
}
if matching_group then
matching_holonym_index = h_index
break
end
end
end
if matching_holonym_index == holonym_index then
if matching_container_trail[1] and not matching_spec.no_container_cat then
for _, container in ipairs(matching_container_trail[1]) do
insert(retcats, ("%s of %s"):format(capital_cat, export.get_prefixed_key(container.key,
container.spec)))
inserted_specific_variant_cat = true
end
end
elseif matching_holonym_index then
-- Check to make sure that the holonym placetype we were called on is listed among the
-- divtypes of the location we found.
local function insert_specific_variant_if_possible(key, spec)
return export.get_equiv_placetype_prop(holonym_placetype, function(pt)
local plural_holonym_placetype = export.pluralize_placetype(pt)
local saw_matching_div
if spec.divs then
local divs = spec.divs
if type(divs) ~= "table" then
divs = {divs}
end
for _, div in ipairs(divs) do
if type(div) ~= "table" then
div = {type = div}
end
if plural_holonym_placetype == div.type then
saw_matching_div = true
break
end
end
end
if saw_matching_div then
insert(retcats, ("%s of %s"):format(capital_cat, export.get_prefixed_key(key, spec)))
return true
end
return false
end)
end
if insert_specific_variant_if_possible(matching_key, matching_spec) then
inserted_specific_variant_cat = true
elseif not matching_spec.no_container_cat then
for _, containers in ipairs(matching_container_trail) do
local saw_no_container_cat = false
for _, container in ipairs(containers) do
if insert_specific_variant_if_possible(container.key, container.spec) then
inserted_specific_variant_cat = true
break
end
saw_no_container_cat = saw_no_container_cat or container.spec.no_container_cat
end
if inserted_specific_variant_cat or saw_no_container_cat then
break
end
end
end
end
else
-- This happens when in an invocation like {{place|en|capital city|s/Haryana,Punjab}} for
-- [[Chandigarh]]. We fall back to older code that doesn't depend on the holonym index existing.
-- FIXME: This may not be necessary. In the example just given, when processing Haryana we add to
-- [[:Category:en:State capitals of India]], and nothing extra gets added when processing Punjab.
-- Possibly we can just skip this case entirely.
local group, key, spec, container_trail = export.find_matching_holonym_location(data)
if group and container_trail[1] and not spec.no_container_cat then
for _, container in ipairs(container_trail[1]) do
insert(retcats, ("%s of %s"):format(capital_cat, export.get_prefixed_key(container.key,
container.spec)))
inserted_specific_variant_cat = true
end
end
end
if not inserted_specific_variant_cat then
insert(retcats, capital_cat)
end
else
-- We didn't recognize the holonym placetype; just put in 'Capital cities'.
insert(retcats, "Capital cities")
end
return retcats
end
--[=[
This is invoked specially for all placetypes (see the `*` placetype key at the bottom of `placetype_data`). This is used
in two ways:
# To add pages to generic holonym categories like [[:Category:en:Places in Merseyside, England]] (and
[[:Category:en:Places in England]]) for any pages that have `co/Merseyside` as their holonym.
# To categorize demonyms in bare placename categories like [[:Category:en:Merseyside, England]] if the demonym
description mentions `co/Merseyside` and doesn't mention a more specific placename that also has a category. (In this
case there are none, but we can have demonyms at multiple levels, e.g. in France for individual villages, departments,
administrative regions, and for the entire country, and for example we only want to categorize a demonym into
[[:Category:France]] if no more specific category applies.) Unlike when invoked from {{tl|place}}, a demonym
invocation only adds the most specific holonym category and not the category of any containing polity (hence if we
add [[:Category:en:Merseyside, England]] we won't also add [[:Category:England]]).
This code also handles cities; e.g. for the first use case above, it would be used to add a page that has `city/Boston`
as a holonym to [[:Category:en:Places in Boston]], along with [[:Category:en:Places in Massachusetts, USA]] and
[[:Category:en:Places in the United States]]. The city handler tries to deal with the possibility of multiple cities
having the same name. For example, the code in [[Module:place/locations]] knows about the city of [[Columbus]],
[[Ohio]], which has containing polities `Ohio` (a state) and `the United States` (a country). If either containing
polity is mentioned, the handler proceeds to return the key `Columbus` (along with `Ohio, USA` and `the United States`).
Otherwise, if any other state or country is mentioned, the handler returns nothing, and otherwise it assumes the
mentioned city is the one we're considering and returns `Columbus` etc. This works correctly if the place only mentions
Ohio and a holonym for a Columbus in a different country is encountered, because of the function
`augment_holonyms_with_container`, which adds the US as a holonym when Ohio is encountered.
The single parameter `data` is as in category handlers. The return value is a list of categories (without the preceding
language code).
]=]
local function generic_place_cat_handler(data)
local from_demonym = data.from_demonym
local retcats = {}
local function insert_retkey(key, spec)
if from_demonym then
insert(retcats, key)
else
insert(retcats, ("Places in %s"):format(export.get_prefixed_key(key, spec)))
end
end
local group, key, spec, container_trail = export.find_matching_holonym_location(data)
if group then
if not spec.no_generic_place_cat then
-- This applies to continents and continental regions.
insert_retkey(key, spec)
end
-- Categorize both in key, and in the larger location(s) that the key is part of, e.g. [[Hirakata]] goes in
-- both [[Category:Places in Osaka Prefecture, Japan]] and [[Category:Places in Japan]]. But not when
-- no_container_cat is set (e.g. for 'United Kingdom').
if not spec.no_container_cat then
for _, container_set in ipairs(container_trail) do
local stop_adding_containers = false
for _, container in ipairs(container_set) do
if not container.spec.no_generic_place_cat then
insert_retkey(container.key, container.spec)
end
if container.spec.no_container_cat then
stop_adding_containers = true
end
end
if stop_adding_containers then
break
end
end
end
return retcats
end
end
--[==[
Special category handler run for all placetypes that checks for specified division placetypes of known locations and
categorizes appropriately.
]==]
function export.political_division_cat_handler(data)
if data.from_demonym then
return
end
local group, key, spec, container_trail = export.find_matching_holonym_location(data)
if group then
local divlists = {}
if spec.divs then
insert(divlists, spec.divs)
end
if spec.addl_divs then
insert(divlists, spec.addl_divs)
end
for _, divlist in ipairs(divlists) do
if type(divlist) ~= "table" then
divlist = {divlist}
end
for _, div in ipairs(divlist) do
if type(div) == "string" then
div = {type = div}
end
local sgdiv = export.maybe_singularize_placetype(div.type) or div.type
local prep = div.prep or "of"
local cat_as = div.cat_as or div.type
if type(cat_as) ~= "table" then
cat_as = {cat_as}
end
if not export.placetype_data[sgdiv] then
internal_error("Placetype %s associated with known location key %s and data %s not found in " ..
"`placetype_data`", sgdiv, key, spec)
end
if sgdiv == data.entry_placetype then
local retcats = {}
for _, pt_cat in ipairs(cat_as) do
if type(pt_cat) == "string" then
pt_cat = {type = pt_cat}
end
local pt_prep = pt_cat.prep or prep
insert(retcats, ucfirst(pt_cat.type) .. " " .. pt_prep .. " " ..
export.get_prefixed_key(key, spec))
end
return retcats
end
end
end
end
end
--[==[
This is used to add pages to "bare" categories like [[:Category:en:Georgia, USA]] for `[[Georgia]]` and any
foreign-language terms that are translations of the state of Georgia. We look at the page title (or its overridden value
in {{para|pagename}}) as well as the glosses in {{para|t}}/{{para|t2}} etc., various extra-info values such as the
modern names in {{para|modern}}, and any values specified using a form-of directive. We need to pay attention to the
entry placetypes specified so we don't overcategorize; e.g. the US state of Georgia is `[[Джорджия]]` in Russian but the
country of Georgia is `[[Грузия]]`, and if we just looked for matching names, we'd get both Russian terms categorized
into both [[:Category:ru:Georgia, USA]] and [[:Category:ru:Georgia]]. We also need to check the containing holonyms to
make sure there isn't a mismatch (so we don't e.g. categorize Newark, Delaware in [[:Category:en:Newark]], which is
intended for Newark, New Jersey).
]==]
function export.get_bare_categories(args, overall_place_spec)
local bare_cats = {}
local place_descs = overall_place_spec.descs
local possible_placetypes_by_place_desc = {}
for i, place_desc in ipairs(place_descs) do
possible_placetypes_by_place_desc[i] = {}
for _, placetype in ipairs(place_desc.placetypes) do
if not export.placetype_is_ignorable(placetype) then
local equivs = export.get_placetype_equivs(placetype, {register_former_as_non_former = true})
for _, equiv in ipairs(equivs) do
insert(possible_placetypes_by_place_desc[i], equiv.placetype)
end
end
end
end
local function check_term(term)
-- Treat Wikipedia links like local ones.
term = term:gsub("%[%[w:", "[["):gsub("%[%[wikipedia:", "[[")
term = export.remove_links_and_html(term)
term = term:gsub("^the ", "")
for i, place_desc in ipairs(place_descs) do
-- Iterate over all matching locations in case there are multiple, as with Delhi defined as
-- {{place|en|megacity/and/union territory|c/India|containing the national capital [[New Delhi]]}}.
for group, key, spec, container_trail in export.iterate_matching_holonym_location {
holonym_placetype = possible_placetypes_by_place_desc[i],
holonym_placename = term,
place_desc = place_desc,
} do
insert(bare_cats, key)
end
end
end
-- FIXME: Should we only do the following if the language is English (requires that the lang is passed in)?
-- We should always do it if `pagename` is given (as it is with {{tcl}}) but maybe not otherwise unless 1=en. There
-- are cases like [[Ankara]] = English name for capital of Turkey, but also the name in various languages for the
-- capital of Ghana (= English [[Accra]]). But this should get caught by mismatching the containing country. The
-- advantage of checking when the language isn't English is we catch those places that fail to give an English
-- translation but where the translation happens to be the same as the other-language spelling. However, I don't
-- know how often this situation occurs.
check_term(args.pagename or mw.loadData("Module:headword/data").pagename)
for _, t in ipairs(args.t) do
check_term(t)
end
local function check_termobj_list(terms)
for _, term in ipairs(terms) do
if term.eq then
check_term(term.eq)
end
if term.alt or term.term then
check_term(term.alt or term.term)
end
end
end
for _, extra_info_terms in ipairs(overall_place_spec.extra_info) do
local arg = extra_info_terms.arg
if arg == "modern" or arg == "now" or arg == "full" or arg == "short" then
check_termobj_list(extra_info_terms.terms)
end
end
for _, directive in ipairs(overall_place_spec.directives) do
check_termobj_list(directive.terms)
end
return bare_cats
end
--[==[
This is used to augment the holonyms associated with a place description with the containing polities. For example,
given the following:
`# {{tl|place|en|subprefecture|pref/Hokkaido}}.`
We auto-add Japan as another holonym so that the term gets categorized into [[:Category:Subprefectures of Japan]].
To avoid over-categorizing we need to check to make sure no other countries are specified as holonyms.
]==]
function export.augment_holonyms_with_container(place_descs)
for _, place_desc in ipairs(place_descs) do
if place_desc.holonyms then
-- This ends up containing a copy of the original holonyms, with the augmented holonyms inserted in their
-- appropriate position. We don't just put them at the end because some holonyms have use the `:also`
-- modifier, which causes category processing to restart at that point after generating categories for a
-- preceding holonym, and we don't want the preceding holonym's augmented holonyms interfering with
-- categorization of a later holonym. We proceed from right to left, and each time we augment, we copy
-- the holonyms with the augmented holonym(s) inserted appropriately and replace the place description's
-- holonyms with the augmented ones before the next iteration. The reason for this is so that e.g.
-- {{place|neighborhood|city/Birmingham|co/West Midlands|cc/England}} doesn't throw an error during the
-- augmentation process due to 'Birmingham' referring to two known locations (in England and Alabama). If
-- we go left to right, we will throw an ambiguity error on `city/Birmingham` because code to exclude
-- Birmingham, Alabama needs `c/United Kingdom` present (to cause a mismatch with `c/United States`),
-- which isn't yet present as the augmentation code hasn't gotten to `cc/England` yet. For similar
-- reasons, we need to include the augmented holonyms in the holonyms considered in the next iteration
-- rather than modifying the place description once at athe end.
for i = #place_desc.holonyms, 1, -1 do
local holonym = place_desc.holonyms[i]
if holonym.placetype and not export.placetype_is_ignorable(holonym.placetype) then
local group, key, spec, container_trail = export.find_matching_holonym_location {
holonym_placetype = holonym.placetype,
holonym_placename = holonym.unlinked_placename,
holonym_index = i,
place_desc = place_desc,
}
if group and container_trail[1] and not spec.no_auto_augment_container then
local augmented_holonyms = {}
for j = 1, i do
insert(augmented_holonyms, place_desc.holonyms[j])
end
for _, containers in ipairs(container_trail) do
local any_no_auto_augment_container = false
for _, container in ipairs(containers) do
any_no_auto_augment_container = any_no_auto_augment_container or
container.spec.no_auto_augment_container
local containing_type = container.spec.placetype
if type(containing_type) == "table" then
-- If the containing type is a list, use the first element as the canonical variant.
containing_type = containing_type[1]
end
local full_container_placename, elliptical_container_placename =
m_locations.key_to_placename(container.group, container.key)
-- Don't side-effect holonyms while processing them.
local new_holonym = {
-- By the time we run, the display has already been generated so we don't need to
-- set display_placename.
placetype = containing_type,
-- placename_to_key() for the group should correctly handle both full and elliptical
-- placenames, but the full placename seems less likely to be ambiguous. FIXME: We
-- should just store the key directly and use it when available to avoid having to
-- convert key to placename and back to key.
unlinked_placename = full_container_placename,
-- Indicate that this is an augmented holonym, and was derived from the specified
-- holonym. In iterate_matching_holonym_location(), we ignore augmented holonyms
-- derived from holonyms that are different from the holonym we're searching for but
-- of the same placetype. This is to correctly handle a situation like
-- {{place|river|dept/Ardèche,Gard,Vaucluse,Bouches-du-Rhône|c/France}}. Here,
-- `Ardèche` is in `r/Auvergne-Rhône-Alpes`, while `Gard` is in `r/Occitania` and
-- the other two are in `r/Provence-Alpes-Côte d'Azur`. Augmenting proceeds from
-- right to left, so after it adds `r/Provence-Alpes-Côte d'Azur` to
-- `Bouches-du-Rhône`, Vaucluse gets augmented correctly but `Gard` fails to match
-- in find_matching_holonym_location() because of the mismatch between augmented
-- `r/Provence-Alpes-Côte d'Azur` and actual `r/Occitania`. Similarly, all later
-- calls to find_matching_holonym_location() fail to match `Gard` (and likewise
-- `Ardèche`) against any known location. To deal with this, we mark augmented
-- holoynms as being augmented due to a source holonym, and when processing a given
-- holonym, ignore augmented holonyms from other holonyms of the same placetype.
-- The restriction to the same placetype is so that `Birmingham` still gets
-- correctly disambiguated to Birmingham, England in the example given above near
-- the top of this function, using the augmented holonym `c/United Kingdom` added by
-- the specified `cc/England` (whose placetype `constituent country` differs from
-- the placetype `city` of Birmingham).
augmented_from_holonym = holonym,
}
insert(augmented_holonyms, new_holonym)
-- But it is safe to modify other parts of the place_desc.
export.key_holonym_into_place_desc(place_desc, new_holonym)
end
if any_no_auto_augment_container then
break
end
end
for j = i + 1, #place_desc.holonyms do
insert(augmented_holonyms, place_desc.holonyms[j])
end
place_desc.holonyms = augmented_holonyms
end
end
end
end
end
end
-- Cat handler for district, areas, neighborhoods and suburbs. Districts are tricky because they can either be political
-- divisions or city neighborhoods. Areas similarly can be political divisions (rarely; specifically, in Kuwait), city
-- neighborhoods or larger geographical areas/regions. We handle this as follows:
-- (1) `placetype_data` cat entries for specific countries or country divisions take precedence over cat_handlers, so if
-- the user says {{tl|place|district|s/Maharashtra|c/India}}, we won't even be called because there is an entry that
-- categorizes into [[:Category|Districts of Maharashtra, India]].
-- (2) If we're called, we check the holonym we're called on to see if it is a recognized city, e.g. if we're called
-- using {{tl|place|district|city/Mumbai|s/Maharashtra|c/India}}. If so, we categorize under e.g.
-- [[:Category:Neighbourhoods of Mumbai]]. (Choosing the spelling "neighbourhoods" because we're in India.)
-- (3) If we're called and the holonym is not a recognized city, we check if the placetype has has_neighborhoods set.
-- If so, it's "city-like" and we categorize under the first containing polity that we recognize. For example, if
-- we're called using {{tl|place|district|town/Northampton|co/Hampshire|s/Massachusetts|c/US}}, we should recognize
-- town as "city-like" and categorize under [[:Category:Neighborhoods in Massachusetts]]. (Note "in" not "of", and
-- note the spelling "neighborhoods" because we're in the US.)
-- (4) If the holonym is not city-like, we do nothing. If there's a city or city-like placetype farther up (e.g. we're
-- called as {{tl|place|district|ward/Foo|mun/Bar|...}}), we will handle the city-like entity according to (2) or
-- (3) when called on that holonym. Otherwise either the categorization in (1) takes place or there's no
-- categorization.
local function district_neighborhood_cat_handler(data)
local function get_plural_entry_placetype(location_spec, container_trail)
if data.entry_placetype == "suburb" then
return "Suburbs"
else
-- Check for `british_spelling` setting on the spec itself or any container.
local uses_british_spelling = location_spec.british_spelling
if uses_british_spelling == nil and container_trail then
for _, container_set in ipairs(container_trail) do
local must_outer_break = false
for _, container in ipairs(container_set) do
if container.spec.british_spelling ~= nil then
uses_british_spelling = container.spec.british_spelling
must_outer_break = true
break
end
end
if must_outer_break then
break
end
end
end
return uses_british_spelling and "Neighbourhoods" or "Neighborhoods"
end
end
-- First check the immediate holonym to see if it's a city or a city-like top-level entity (Hong Kong, Bonaire,
-- etc.)
local group, key, spec, container_trail = export.find_matching_holonym_location(data)
if group and not spec.is_former_place and spec.is_city then
return {get_plural_entry_placetype(spec, container_trail) .. " of " .. export.get_prefixed_key(key, spec)}
end
-- If the entry placetype is neighbo(u)rhood, assume it is a neighborhood even if there isn't a city-like
-- entity father up the chain. (E.g. due to a mistaken use of m/ instead of mun/ for municipality.)
local has_neighborhoods
local entry_placetype = data.entry_placetype
if entry_placetype == "neighborhood" or entry_placetype == "neighbourhood" or entry_placetype == "suburb" then
has_neighborhoods = true
else
-- Otherwise, make sure the current holonym is city-like.
has_neighborhoods = export.get_equiv_placetype_prop(data.holonym_placetype, function(pt)
return export.get_placetype_prop(pt, "has_neighborhoods")
end, {continue_on_nil_only = true})
end
if has_neighborhoods then
-- Loop up the holonyms, looking for city and city-like entities in case of e.g. [[Sepulveda]] written
-- {{place|en|neighborhood|valley/San Fernando Valley|city/Los Angeles|s/California|c/USA}}
-- but also look for a recognizable poldiv, and if so categorize as "Neighborhoods in POLDIV". We need
-- to start with the current holonym, which is especially important for neighborhoods and suburbs that
-- may have the first holonym be a recognizable province, etc. but can't hurt otherwise. (Previously
-- we skipped the first/current holonym.)
for other_holonym_index, other_holonym in export.get_holonyms_to_check(data.place_desc,
data.holonym_index) do
local other_holonym_data = {
holonym_placetype = other_holonym.placetype,
holonym_placename = other_holonym.unlinked_placename,
holonym_index = other_holonym_index,
place_desc = data.place_desc,
}
local group, key, spec, container_trail = export.find_matching_holonym_location(other_holonym_data)
if group and not spec.is_former_place then
return {get_plural_entry_placetype(spec, container_trail) .. (spec.is_city and " of " or " in ") ..
export.get_prefixed_key(key, spec)}
end
end
end
end
function export.check_already_seen_string(holonym_placename, already_seen_strings)
local canon_placename = ulower(m_links.remove_links(holonym_placename))
if type(already_seen_strings) ~= "table" then
already_seen_strings = {already_seen_strings}
end
for _, already_seen_string in ipairs(already_seen_strings) do
if canon_placename:find(already_seen_string) then
return true
end
end
return false
end
-- Prefix display handler that adds a prefix such as "Metropolitan Borough of " to the display
-- form of holonyms. We make sure the holonym doesn't contain the prefix or some variant already.
-- We do this by checking if any of the strings in ALREADY_SEEN_STRINGS, either a single string or
-- a list of strings, or the prefix if ALREADY_SEEN_STRINGS is omitted, are found in the holonym
-- placename, ignoring case and links. If the prefix isn't already present, we create a link that
-- uses the raw form as the link destination but the prefixed form as the display form, unless the
-- holonym already has a link in it, in which case we just add the prefix.
local function prefix_display_handler(prefix, holonym_placename, already_seen_strings)
if export.check_already_seen_string(holonym_placename, already_seen_strings or ulower(prefix)) then
return holonym_placename
end
if holonym_placename:find("%[%[") then
return prefix .. " " .. holonym_placename
end
return prefix .. " [[" .. holonym_placename .. "]]"
end
-- Suffix display handler that adds a suffix such as " parish" to the display form of holonyms.
-- Works identically to prefix_display_handler but for suffixes instead of prefixes.
local function suffix_display_handler(suffix, holonym_placename, already_seen_strings, include_suffix_in_link)
if export.check_already_seen_string(holonym_placename, already_seen_strings or ulower(suffix)) then
return holonym_placename
end
if holonym_placename:find("%[%[") then
return holonym_placename .. " " .. suffix
end
if include_suffix_in_link then
return "[[" .. holonym_placename .. " " .. suffix .. "]]"
else
return "[[" .. holonym_placename .. "]] " .. suffix
end
end
-- Display handler for boroughs. New York City boroughs are display as-is. Others are suffixed
-- with "borough".
local function borough_display_handler(holonym_placetype, holonym_placename)
local unlinked_placename = m_links.remove_links(holonym_placename)
if m_locations.new_york_boroughs[unlinked_placename] then
-- Hack: don't display "borough" after the names of NYC boroughs
return holonym_placename
end
return suffix_display_handler("borough", holonym_placename)
end
local function county_display_handler(holonym_placetype, holonym_placename)
local unlinked_placename = m_links.remove_links(holonym_placename)
-- Display handler for Irish counties. Irish counties are displayed as e.g. "County [[Cork]]".
if m_locations.ireland_counties["County " .. unlinked_placename .. ", Ireland"] or
m_locations.northern_ireland_counties["County " .. unlinked_placename .. ", Northern Ireland"] then
return prefix_display_handler("County", holonym_placename)
end
-- Display handler for Taiwanese counties. Taiwanese counties are displayed as e.g. "[[Chiayi]] County".
if m_locations.taiwan_counties[unlinked_placename .. " County, Taiwan"] then
return suffix_display_handler("County", holonym_placename)
end
-- Display handler for Romanian counties. Romanian counties are displayed as e.g. "[[Cluj]] County".
if m_locations.romania_counties[unlinked_placename .. " County, Romania"] then
return suffix_display_handler("County", holonym_placename)
end
-- FIXME, we need the same for US counties but need to key off the country, not the specific county.
-- Others are displayed as-is.
return holonym_placename
end
-- Display handler for prefectures. Japanese prefectures are displayed as e.g. "[[Fukushima]] Prefecture".
-- Others are displayed as e.g. "[[Fthiotida]] prefecture".
local function prefecture_display_handler(holonym_placetype, holonym_placename)
local unlinked_placename = m_links.remove_links(holonym_placename)
local suffix = m_locations.japan_prefectures[unlinked_placename .. " Prefecture, Japan"] and "Prefecture" or "prefecture"
return suffix_display_handler(suffix, holonym_placename)
end
-- Display handler for provinces of Bulgaria, Iran, Laos, North and South Korea, Thailand, Turkey and Vietnam. Recognized
-- provinces are displayed as e.g. "[[Gyeonggi]] Province" or "[[Antalya]] Province". Others are displayed as-is.
local function province_display_handler(holonym_placetype, holonym_placename)
local unlinked_placename = m_links.remove_links(holonym_placename)
if
m_locations.bulgaria_provinces[unlinked_placename .. "Province, Bulgaria"] or
m_locations.iran_provinces[unlinked_placename .. " Province, Iran"] or
m_locations.laos_provinces[unlinked_placename .. " Province, Laos"] or
m_locations.north_korea_provinces[unlinked_placename .. " Province, North Korea"] or
m_locations.south_korea_provinces[unlinked_placename .. " Province, South Korea"] or
m_locations.thailand_provinces[unlinked_placename .. " Province, Thailand"] or
m_locations.turkey_provinces[unlinked_placename .. " Province, Turkey"] or
m_locations.vietnam_provinces[unlinked_placename .. " Province, Vietnam"] then
return suffix_display_handler("Province", holonym_placename)
end
return holonym_placename
end
-- Display handler for Nigerian states. Nigerian states are display as "[[Kano]] State". Others are displayed as-is.
local function state_display_handler(holonym_placetype, holonym_placename)
local unlinked_placename = m_links.remove_links(holonym_placename)
if m_locations.nigeria_states[unlinked_placename .. " State, Nigeria"] then
return suffix_display_handler("State", holonym_placename)
end
return holonym_placename
end
-- Display handler for voivodeships. Display as e.g. [[Subcarpathian Voivodeship]].
local function voivodesip_display_handler(holonym_placetype, holonym_placename)
return suffix_display_handler("Voivodeship", holonym_placename, nil, "include_suffix_in_link")
end
------------------------------------------------------------------------------------------
-- Placetype data --
------------------------------------------------------------------------------------------
--[==[ var:
Main placetype data structure. This specifies, for each canonicalized placetype, various properties. The keys are
placetypes (in the singular, except for category-only placetypes, which are plural and followed by `!`), and the value
is a table of properties. The `"*"` key is special and is used for adding "generic" categories of the form
`Places in ``location`` `; it runs for all entry placetypes. Keys in the form of plural placetypes followed by `!` are
used only in [[Module:category tree/topic cat/data/Places]] for specifying the properties of categories containing the
specified placetype, esp. bare categories like [[:Category:States and territories]] (rather than qualified categories
like [[:Category:States and territories of Australia]]).
Keys under the value table for a given placetype of are two types: ''property keys'' (which specify the value of
specific properties) and ''categorization keys'' (which tell how to categorize certain sorts of holonyms if the
placetype in question occurs as an entry placetype). Categorization keys are either the special value `default` or are
wildcard strings with a slash in them, such as `"country/*"`. Note that only wildcard strings are currently allowed
directly in the placetype data; everything else is handled through category handlers, either per-placetype or special
(such as `political_division_cat_handler`). The algorithm for how category keys and handlers are used to generate
categories is described at the top of [[Module:place]].
There are several recognized property keys, of various types:
1. The following link-related property keys are recognized:
* `link`: '''Required''' except in category-only placetypes ending in `!`. Describes how to link and display the
placetype in the formatted description when occurring as an entry placetype. Also used for formatting pluralized
placetypes (which may occur in entry placetypes, esp. new-format ones, such as `two <<islands>>`) and may occur in
categories). The possible values are:
*# `true`: Link to the same-named Wiktionary entry. This creates a raw link, e.g. `<nowiki>[[city]]</nowiki>`, which is
converted to an English-specific link by JavaScript postprocessing. If the placetype is plural, this creates a
two-part raw link e.g. `<nowiki>[[city|cities]]</nowiki>`.
*# `"w"`: Link to the same-named Wikipedia entry. This creates a two-part link, e.g.
`<nowiki>[[w:census town|census town]]</nowiki>`, or `<nowiki>[[w:census town|census towns]]</nowiki>` if the
placetype is given plural.
*# `"+..."`: Create a two-part link to the entry following the `+` sign. For example, if `cercle` specifies
`"+w:cercles of Mali"`, a two-part link `<nowiki>[[w:cercles of Mali|cercle]]</nowiki>` will be generated, or
`<nowiki>[[w:cercles of Mali|cercles]]</nowiki>` if plural `cercles` is specified.
*# `"separately"`: Link each word separately. For example, if `administrative territory` specifies `"separately"`, it
will be linked as `<nowiki>[[administrative]] [[territory]]</nowiki>`, or as
`<nowiki>[[administrative]] [[territory|territories]]</nowiki>` if plural `administrative territories` is given.
*# another string: Use that string directly. If the placetype is plural, `pluralize()` in [[Module:en-utilities]] is
called on the string, which will correctly pluralize most strings, including those with links in them. (If there
are multiple links, the display form of the last link is pluralized.)
*# `false`: This placetype is not allowed as an entry placetype. An error will be thrown if this placetype is given as
an entry placetype. This is specified for internal-use placetypes, especially placetypes used in conjunction with
the qualifiers `former`, `ancient`, `historical` and such.
* `plural_link`: If specified and the placetype is plural, use the value in place of generating a pluralized version of
the link spec in `link`. Most commonly, this is either a string with links in it (which is used directly) or the
value `false`, indicating that the placetype cannot occur plural. (This is used for example by `caplc`, which displays
as `<nowiki>[[capital]] and [[large]]st [[city]]</nowiki>`, where a plural version doesn't make sense.) Generally if
this is specified, `plural` also needs to be specified to give a special placetype plural; this situation occurs
especially with multiword placetypes where something other than the last word is pluralized. An example is
`town with bystatus`, whose plural is `towns with bystatus`, which needs to be explicitly given. This example uses
`link = <nowiki>"[[town]] with [[bystatus#Norwegian Bokmål|bystatus]]"</nowiki>` ({{m|nb|bystatus}}) is a Norwegian
Bokmål word, and template calls aren't currently permitted in link strings), along with
`plural_link = <nowiki>"[[town]]s with [[bystatus#Norwegian Bokmål|bystatus]]"</nowiki>`.
* `category_link`: Spec indicating how to display the placetype when occurring in category descriptions. Defaults to
the value of `link`, and in turn is overridden by more specific `category_link_*` keys; see below. Category-only
placetypes (which are plural and end in `!`) usually use `category_link` in preference to `link`. The value of
`category_link` can be any of the types of specs given above, but most commonly is a plural string with links in it,
spelling out the description; in this case it is used directly. When both `category_link` and `link` are given, the
value in `category_link` is typically longer and more descriptive. For example, `polity` uses `link = true`, which
just generates a link `<nowiki>[[polity]]</nowiki>` or plural `<nowiki>[[polity|polities]]</nowiki>`, but specifies a
separate `category_link = <nowiki>"[[independent]] or [[semi-]][[independent]] [[polity|polities]]"</nowiki>`, which
clarifies in the category description what a polity is.
* `category_link_top_level`: Spec indicating how to display top-level (bare/unqualified) categories, i.e. categories
where the placetype is not followed by `in ``location`` ` or `of ``location`` `. If given, this overrides
`category_link` for this type of category.
* `category_link_before_noncity`: Spec indicating how to display qualified categories of the form
` ``placetypes`` in/of ``location`` ` where ``location`` does not refer to a city. If given, this overrides
`category_link` for this type of category.
* `category_link_before_city`: Spec indicating how to display qualified categories of the form
` ``placetypes`` in/of ``location`` ` where ``location`` refer to a city. If given, this overrides `category_link` for
this type of category. An example where this is given is `neighborhood`, which uses the following specs:<ol>
<li>`link = true`</li>
<li>`category_link = <nowiki>"[[neighborhood]]s, [[district]]s and other subportions of [[city|cities]]"</nowiki>`</li>
<li>`category_link_before_city = <nowiki>"[[neighborhood]]s, [[district]]s and other subportions"</nowiki>`</li>
</ol> This has the effect of making the entry placetype `neighborhood` display as just
`<nowiki>[[neighborhood]]</nowiki>`, while e.g. a category like `Neighborhoods of Chicago` displays as
`<nowiki>[[neighborhood]]s, [[district]]s and other subportions of [[Chicago]], ...</nowiki>` and a category like
`Neighborhoods in Illinois, USA` displays as
`<nowiki>[[neighborhood]]s, [[district]]s and other subportions of [[city|cities]] in [[Illinois]], ...</nowiki>`.
* `disallow_in_entries`: If specified, this placetype cannot occur as an entry placetype, and the specified value
(a message indicating what to use instead) is displayed in the error message.
* `disallow_in_holonyms`: If specified, this placetype cannot occur as a holonym placetype, and the specified value
(a message indicating what to use instead) is displayed in the error message.
2. There is currently one fallback-related property key recognized:
* `fallback`: If specified, its value is a placetype which will be used for categorization purposes if no categories
get added using the placetype itself. As an example, `branch` sets a fallback of `river` but also sets
`preposition = "of"`, meaning that {{tl|place|en|branch|riv/Mississippi}} displays as `a branch of the Mississippi`
(whereas `river` itself uses the preposition `in`), but otherwise categorizes the same as `river`. A more complex
example is `area`, which sets a fallback of `geographic and cultural area` and also sets a category handler that
checks for cities or city-like entities (e.g. boroughs) occurring as holonyms and categorizes the toponym under
[[:Category:Neighborhoods of CITY]] (for recognized cities) or otherwise [[:Category:Neighborhoods of POLDIV]] (for
the nearest containing recognized location). In addition, `area` is set as a political division of Kuwait, meaning if
`c/Kuwait` occurs as holonym, the toponym is categorized under [[:Category:Areas of Kuwait]]. If none of these
categories trigger, the fallback of `geographic and cultural area` will take effect, and the toponym will be
categorized as e.g. [[:Category:Geographic and cultural areas of England]].
3. There is currently one property to control irregular plurals of placetypes:
* `plural`: If specified, its value is the plural of the placetype. Otherwise, the default pluralization algorithm in
[[Module:en-utilities]] applies (which correctly pluralizes most words, including those ending in `-y`, `-ch`, `-sh`,
`-x`, etc.). The value of `plural` is also used when converting a pluralized placetype into its singular equivalent;
for example, since the placetype `kibbutz` has `plural = "kibbutzim"`, the placetype `kibbutzim` will be recognized
as a plural and singularized to `kibbutz`. For this reason, it's occasionally necessary to specify a `plural` value
even when the default pluralization algorithm works correctly, if the default singularization algorithm won't
correctly reverse the pluralization (as with `pass` and other terms ending in `-ss`).
4. The following property keys relate to generating categories for entry placetypes and specifying the parents of those
categories:
* `class`: The general class of placetype. This is used for various purposes: (a) to categorize placetypes preceded by
a qualifier such as `former`, `ancient`, `medieval` or `historical` (note that these placetypes are not all treated
alike); (b) to determine the parent category of bare placetype categories (e.g. [[:Category:Villages]] for placetype
`village`); (c) to determine whether to add a parent category `political divisions of specific countries` to
qualified placetype categories (e.g. [[:Category:Villages in Mali]]). The possible values are:
*# `polity`: a more-or-less sovereign/independent polity, such as a country, kingdom or empire.
*# `subpolity`: a non-sovereign division of a polity, above the level of an individual settlement.
*# `settlement`: a city or smaller equivalent, such as a village. This also includes administrative divisions of a
settlement, such as wards and barangays.
*# `non-admin settlement`: similar to a settlement but without administrative or political significance, such as an
unincorporated community, farm or neighborhood.
*# `capital`: a settlement that is a capital. A former capital is generally still in existence, just not the capital
any more.
*# `natural feature`: any non-man-made feature, such as a lake, mountain, island, ocean, etc.
*# `man-made structure`: a man-made feature below the level of a neighborhood, such as a house, airport, university,
metro station, park or the like.
*# `geographic region`: a geographic or cultural region or area that has no administrative significance. These may vary
greatly in size but typically have some sort of cultural significance (possibly historical). The `former`, `ancient`,
etc. qualifier has no effect on the category of these placetypes.
*# `generic place`: a place that isn't further qualified into any specific subtype.
* `former_type`: The class of placetype used for categorizing placetypes preceded by a qualifier such as `former`,
`ancient`, `medieval` or `historical`. The possible values are the same as for `class` but with the addition of
`dependent territory` (for colonies, protectorates and the like) and `!` (ignore the historical/former/ancient/etc.
qualifier; used e.g. with `fictional location` and `mythological location`). If not specified, the value of `class`
is used. When a qualifier such as `former`, `ancient`, `medieval` or `historical` is encountered (specifically, those
in `former_qualifiers`), it is mapped using `former_qualifiers` to the appropriate internal qualifier or qualifiers
(one or both of `ANCIENT` and/or `FORMER`, which are written in all-caps to distinguish them from user-specified
qualifiers), which is prepended to the value of `former_type` or `class` to form a placetype whose properties are
looked up to determine how to categorize the toponym in question. For example, if `medieval village` is given, we map
`medieval` to `ANCIENT` and `FORMER`, and `village` to its `class` of `settlement`, and enter the placetypes
`ANCIENT settlement` and `FORMER settlement` (in that order) into the list of equivalent placetypes returned by
`get_placetype_equivs`. In this case, there is an entry in `placetype_data` for `ANCIENT settlement`, so its default
category spec `Ancient settlements` is used as the category. If on the other hand `medieval kingdom` is given, where
`kingdom` has a `class` value `polity`, we first look up `ANCIENT polity`, see there is no entry in `placetype_data`
for it, and then look up `FORMER polity`, which exists and has a default category spec `Former polities`, which is
used as the category. Note that if the placetype following the "former" qualifier is recognized in `placetype_data`
but has no `former_type` or `class` and no fallback with a `former_type` or `class` specified, it is an internal
error; but if the placetype isn't recognized (e.g. something like `former greenhouse` is specified and we don't have
an entry for `greenhouse`), we just track the occurrence and end up not categorizing.
* `bare_category_parent`: This specifies the first parent category of a bare placetype category named according to the
placetype in question (e.g. [[:Category:Atolls]] for placetype `atoll`, or [[:Category:Named buildings]] for
placetype `named buildings!`). If not specified, the first parent category is determined by the value of `class`,
using the mapping `class_to_bare_category_parent` in [[Module:category tree/topic cat/data/Places]].
* `addl_bare_category_parents`: Extra parent categories to add a bare placetype category to (see `bare_category_parent`
just above).
* `bare_category_breadcrumb`: Breadcrumb for bare placetype categories. Also used as the sort key of
`bare_category_parent` if it is a string.
* `inherently_former`: If specified and the given placetype is used as an entry placetype, act as if `former` or
`ancient` (depending on the value of `inherently_former`) were prefixed to the placetype. This is for placetypes that
always refer to no-longer-existing entities, such as `satrapy` and `treaty port`. The value of `inherently_former` is
a list of internal qualifiers (one or more of `ANCIENT` and/or `FORMER`), just as for `former_qualifiers`, and the
implementation is the same.
* `cat_handler`: Handler used to generate the categories to add a given toponym to, if its entry placetype is the
placetype in question. Generally the `cat_handler` function checks the holonyms specified in order to determine which
category or categories to generate. For example, `district_neighborhood_cat_handler` handles placetypes `district`,
`neighborhood`, `subdivision`, `suburb` and the like, and either adds the toponym to a category like
`Neighborhoods of ``city`` ` (if a recognized city is given as a holonym), or otherwise a category like
`Neighborhoods in ``location`` ` (for the first recognized non-city location given as a holonym, if an unrecognized
city or city-like entity is given before the recognized non-city). The algorithm that runs the category handlers
iterates over holonyms from left to right, running the `cat_handler` function on each holonym in turn until one or
more categories are returned; see below for more specifics. (Note that countries for which e.g. a `district` is a
political division do not get the corresponding category added by the `district_neighborhood_cat_handler` function but
by `political_division_cat_handler`.) `cat_handler` functions are called with one argument, `data`, describing the
resolved entry placetype (i.e. after resolving placetype aliases and fallbacks) and the holonym being processed. The
return value should be a list of category specs (categories minus the langcode prefix, with `+++` standing for the
holonym key, or the value `true`, which stands for ` ``Placetypes`` in/of ``Holonym`` `, i.e. the pluralized placetype
with the appropriate preposition as specified in `placetype_data`). `data` contains the following fields:
** `entry_placetype`: the resolved entry placetype for the entry placetype being processed (i.e. it will always have an
entry in `placetype_data` but may not be the original placetype given by the user);
** `holonym_placetype` and `holonym_placename`: the holonym placetype and placename being processed;
** `holonym_index`: the index of the holonym being processed, or {nil} if we're handling an overriding holonym (FIXME:
we will change the overriding holonym algorithm so there will be an index even when processing overriding holonyms);
** `place_desc`: a full description of the {{tl|place}} call, as specified at the top of [[Module:place]];
** `from_demonym`: If set, we are called from [[Module:demonym]], triggered by {{tl|demonym-adj}} or
{{tl|demonym-noun}}, instead of being triggered by {{tl|place}}.
* `has_neighborhoods`: If `true`, the specified placetype is city-like. This is used in the
`district_neighborhood_cat_handler` to determine whether to add a category such as `Neighborhoods in ``location`` `;
see the section just above on `cat_handler`.
5. The following preposition-related property keys are recognized:
* `preposition`: The preposition used after this placetype when it occurs as an entry placetype. Defaults to `"in"`.
* `generic_before_non_cities`: If specified, the appropriate category description handler in
[[Module:category tree/topic cat/data/Places]] will recognize categories of the form
` ``Placetype`` in/of ``location`` ` for the specified placetype and preposition, if ``location`` is a non-city. This
is used to generate descriptions for categories added by category handlers and by explicit category specs in the
placetype data. All placetypes that specify `generic_before_non_cities` or `generic_before_cities` *MUST* also specify
a value for `class` so that the category tree code can determine whether it's a political or non-political division.
* `generic_before_cities`: Like `generic_before_non_cities` but for locations referring to cities.
6. The following property keys control the auto-addition of affixes when formatting holonyms of a particular placetype:
* `affix_type`: If specified, add the placetype as an affix before or after holonyms of this placetype. Possible values
are:
*# `"pref"` (the holonym will display as `(the) placetype of Holonym`, where `the` appears when the holonym directly
follows an entry placetype);
*# `"Pref"` (same as `"pref"` but the placetype is capitalized; each word is capitalized if there are multiple);
*# `"suf"` (the holonym will display as `Holonym placetype`);
*# `"Suf"` (the holonym will display as `Holonym Placetype`, i.e. same as `"suf"` but the placetype is capitalized).
* `suffix`: String to use in place of the placetype itself when the placetype is displayed as a suffix after a holonym.
Note that `suffix` can be used independently of `affix_type` because the user can also request a suffix explicitly
using a syntax like `adr:suf/Occitania`, which will display as `Occitania region` because the placetype
`administrative region` specifies `suffix = "region"`.
* `prefix`: Like `suffix` but for use when the placetype is displayed as a prefix before the holonym.
* `affix`: Like `suffix` and `prefix` but for use when the placetype is displayed as an affix either before or after the
holonym. If both `suffix` or `prefix` and `affix` are given for a single placetype, `suffix` or `prefix` take
precedence.
* `no_affix_strings`: String or list of strings that, if they occur in the holonym, suppress the addition of any affix
requested using `affix_type`. Defaults to the placetype itself. For example, `autonomous okrug` specifies
`affix_type = "Suf"` so that `aokr/Nenets` displays as `Nenets Autonomous Okrug`, but also specifies
`no_affix_strings = "okrug"` so that `aokr/Nenets Okrug` or `aokr/Nenets Autonomous Okrug` displays as specified,
without a redundant `Autonomous Okrug` added. Matching is case-insensitive but whole-word.
* `display_handler`: A function of two arguments, `holonym_placetype` and `holonym_placename` (specifying a holonym).
Its return value is a string specifying the display form of the holonym.
7. The following property keys control the indefinite and definite articles used before entry placetypes and/or holonyms
of the specified placetype.
* `entry_placetype_use_the`: Use `"the"` before this placetype when it occurs as an entry placetype.
* `entry_placetype_indefinite_article`: Indefinite article used before this placetype when it occurs as an entry
placetype (usually `"a"`, specifically for placetypes beginning with u- that don't take the indefinite article
`"an"`). Defaults to the appropriate indefinite article (`"a"` or `"an"` depending on whether the placetype begins
with a vowel). Overridden by `entry_placetype_use_the`, and unlike for most properties, does not apply to equivalent
placetypes (i.e. fallbacks or those formed by removing a qualifier from the beginning); only to the exact placetype
specified.
* `holonym_use_the`: Use `"the"` before holonyms of this placetype.
'''NOTE:'''
# The `link` property must be specified on all placetypes, except those ending in `!` (category-only placetypes), which
must have either `link` or `category_link` specified.
# Either the `class` or `former_type` property must be specified on all placetypes not ending in `!` that do not have a
fallback (if a placetype has a fallback and omits the `class` and `former_type` properties, they are taken from the
fallback). An internal error will result if a placetype has no `class` or `former_type` property derivable either
directly or through a fallback, if an attempt is made to categorize a former/ancient/historical/etc. entity of this
placetype.
# It is possible to have multiple levels of fallback (e.g. `frazione` falls back to `hamlet`, which falls back
to `village`). Fallback loops will cause an internal error. All placetypes specified as fallbacks must exist in
`placetype_data` or an internal error occurs.
]==]
export.placetype_data = {
--[=[
If you need to sort the following, do this (using Vim):
1. Make sure all full-line comments are within the { ... } table, or are moved after and on the same line as single-line
entries.
2. Make sure the table uses tabs everywhere for indent, and not spaces.
3. Mark the top of the table with `ma`, go to the bottom and execute the following two lines in sequence:
:'a,.s/\n/\\n/g
:s/\\n\(\t\[\)/\r\1/g
The first command converts every newline to a literal `\n` sequence, so the whole thing becomes a single line, while
the second command restores the newlines before the beginning of each entry. The effect is to convert all entries to
a single line while not losing any information. (Potentially a negative lookahead could be used to do it all in one
command.)
4. Execute the following to sort:
:'a,.!perl -pe 's/^(\t\[")(.*?)(".*)$/$2 @@@ $1$2$3/' | sort -f | perl -pe 's/.*? @@@ //'
Note that a simple `sort -f` (where `-f` means case-insensitive) would almost work, but it would sort "hill station"
before "hill" and "county borough" before "county" because the space after e.g. "hill station" sorts before the
quotation mark after e.g. "hill". The above command deals with this by extracting the key, prepending it followed by
` @@@ `, sorting, and then removing key (the classic decorate-sort-undecorate pattern).
5. Put the table back to multi-line format by marking the top of the table with `ma`, going to the bottom and executing
:'a,.s/\\n/\r/g
Note that for some reason, in order to get a match a newline in the left side of a replacement, you must use \n, but
to insert a newline in the right sode of a replacement you must use \r.
]=]
["*"] = {
link = false,
cat_handler = generic_place_cat_handler,
},
["administrative atoll"] = {
-- Maldives
link = "+w:administrative divisions of the Maldives",
preposition = "of",
class = "subpolity",
},
["administrative capital"] = {
link = "w",
fallback = "capital city",
},
["administrative center"] = {
link = "w",
fallback = "non-city capital",
},
["administrative centre"] = {
link = "w",
fallback = "administrative center",
},
["administrative county"] = {
link = "w",
fallback = "county",
},
["administrative district"] = {
link = "w",
fallback = "district",
},
["administrative headquarters"] = {
link = "separately",
fallback = "administrative centre",
},
["administrative region"] = {
link = true,
preposition = "of",
suffix = "region", -- but prefix is still "administrative region (of)"
fallback = "region",
class = "subpolity",
},
["administrative seat"] = {
link = "w",
fallback = "administrative centre",
},
["administrative territory"] = {
link = "separately",
preposition = "of",
suffix = "territory", -- but prefix is still "administrative territory (of)"
fallback = "territory",
class = "subpolity",
},
["administrative unit"] = {
-- Grrr, it's difficult to generalize about "administrative units". In Albania, "administrative unit" is an
-- official term for a city-level division of municipalities; Wikipedia renders it using the more practical term
-- "commune". In Pakistan, "administrative unit" is a collective term used to refer to all the different types
-- of first-level divisions (four provinces, one federal territory, and two "disputed territories", i.e. Azad
-- Kashmir and Gilgit-Balistan, that are variously described). For this reason, we set no fallback, but we need
-- to include this so that it can be used as a placetype for Albania, categorizing as communes.
link = "w",
class = "subpolity",
},
["administrative village"] = {
link = "w",
preposition = "of",
has_neighborhoods = true,
class = "settlement",
},
["aimag"] = {
-- used in Mongolia, Russia and China (Inner Mongolia); in Mongolia, equivalent to a province;
-- in China, equivalent to a prefecture (below a province); in Russia, equivalent to a municipal district.
link = "w",
fallback = "prefecture",
},
["airport"] = {
link = true,
class = "man-made structure",
default = {true},
},
["alliance"] = {
link = true,
fallback = "confederation",
},
["archipelago"] = {
link = true,
fallback = "island",
},
["area"] = {
link = true,
preposition = "of",
fallback = "geographic and cultural area",
-- Areas can either be administrative divisions (specifically of Kuwait) or geographic areas. Assume the former
-- when categorizing 'Areas' but the latter when handling e.g. 'historical area'.
class = "subpolity",
former_type = "geographic region",
cat_handler = district_neighborhood_cat_handler,
},
["arm"] = {
link = true,
preposition = "of",
class = "natural feature",
default = {"Seas"},
},
["arrondissement"] = {
link = true,
preposition = "of",
-- FIXME!!! Grrrrr!!! In some countries, arrondissements are divisions of cities; in others, they are divisions
-- of departments or provinces. Need to conditionalize on the country for both of the following.
class = "subpolity",
has_neighborhoods = true,
},
["associated province"] = {
link = "separately",
fallback = "province",
},
["atoll"] = {
-- FIXME! Atolls are administrative divisions of the Maldives but natural features elsewhere. Need to
-- conditionalize `class` on the country. See also `administrative atoll`.
link = true,
class = "natural feature",
bare_category_parent = "islands",
default = {true},
},
["autonomous city"] = {
link = "w",
preposition = "of",
fallback = "city",
has_neighborhoods = true,
},
["autonomous community"] = {
-- Spain; refers to regional entities, not village-like entities, as might be expected from "community"
link = true,
preposition = "of",
class = "subpolity",
},
["autonomous island"] = {
-- Comoros; seems like an administrative atoll of the Maldives.
link = "+w:autonomous islands of Comoros",
preposition = "of",
class = "subpolity",
},
["autonomous oblast"] = {
link = true,
preposition = "of",
affix_type = "Suf",
no_affix_strings = "oblast",
class = "subpolity",
},
["autonomous okrug"] = {
link = true,
preposition = "of",
affix_type = "Suf",
no_affix_strings = "okrug",
class = "subpolity",
},
["autonomous prefecture"] = {
link = true,
fallback = "prefecture",
},
["autonomous province"] = {
link = "w",
fallback = "province",
},
["autonomous region"] = {
link = "w",
preposition = "of",
fallback = "administrative region",
-- "administrative region" sets an affix of "region" but we want to display as "Tibet Autonomous Region"
-- if the user writes 'ar:Suf/Tibet'.
affix = "autonomous region",
},
["autonomous republic"] = {
link = "w",
preposition = "of",
class = "subpolity",
},
["autonomous territorial unit"] = {
-- Moldova; only two of them, one for Gagauzia and one for Transnistria.
link = "w",
preposition = "of",
class = "subpolity",
},
["autonomous territory"] = {
link = "w",
fallback = "dependent territory",
},
["bailiwick"] = {
-- Jersey, etc.
link = true,
fallback = "polity",
},
["barangay"] = {
-- Philippines
link = true,
class = "settlement",
-- Barangays are formal administrative divisions of a city rather than informal neighborhoods, but can use
-- some of the properties of a neighborhood.
fallback = "neighborhood",
},
["barrio"] = {
-- Spanish-speaking countries; Philippines
link = true,
-- FIXME: Not completely correct, in some countries barrios are formal administrative divisions of a city.
-- `class` will need to conditionalize on the country to be completely correct.
fallback = "neighborhood",
},
["basin"] = {
link = true,
fallback = "lake",
},
["bay"] = {
link = true,
preposition = "of",
class = "natural feature",
addl_bare_category_parents = {"bodies of water"},
default = {true},
},
["beach"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"water"},
default = {true},
},
["beach resort"] = {
link = "w",
fallback = "resort town",
},
["bishopric"] = {
link = true,
fallback = "polity",
},
["bodies of water!"] = {
-- FIXME: This is (maybe?) a type category not a name category. There should be an option for this. We need to
-- straighten out the type vs. name vs. related-to issue.
category_link = "[[body of water|bodies of water]]",
class = "natural feature",
addl_bare_category_parents = {"landforms", "ecosystems", "water"},
},
["borough"] = {
link = true,
preposition = "of",
display_handler = borough_display_handler,
has_neighborhoods = true,
-- "former borough" could be a former settlement or a former part of a city but seems more likely to
-- be a former subpolity, particularly in England. FIXME, we really need a handler to take care of this
-- properly.
class = "subpolity",
-- Grr, some boroughs are city-like but some (e.g. in Britain) may be larger.
},
["borough seat"] = {
link = true,
entry_placetype_use_the = true,
preposition = "of",
has_neighborhoods = true,
class = "capital",
},
["branch"] = {
link = true,
preposition = "of",
fallback = "river",
},
["bridge"] = {
link = true,
class = "man-made structure",
default = {"Named bridges"},
},
["building"] = {
link = true,
class = "man-made structure",
default = {"Named buildings"},
},
["built-up area"] = {
link = "w",
fallback = "area",
},
["burgh"] = {
link = true,
fallback = "borough",
},
["business park"] = {
link = true,
fallback = "park",
},
["caliphate"] = {
link = true,
fallback = "polity",
},
["canton"] = {
link = true,
preposition = "of",
affix_type = "suf",
class = "subpolity",
},
["cape"] = {
link = true,
fallback = "headland",
},
["capital"] = {
link = true,
fallback = "capital city",
},
["capital city"] = {
link = true,
category_link = "[[capital city|capital cities]]: the [[seat of government|seats of government]] for a country or [[political]] [[division]] of a country",
entry_placetype_use_the = true,
preposition = "of",
has_neighborhoods = true,
class = "capital",
bare_category_parent = "cities",
cat_handler = capital_city_cat_handler,
default = {true},
-- The following is necessary so that e.g. [[Melbourne]] defined as {{place|en|capital city|s/Victoria|c/Australia}}
-- gets categorized in the bare category [[Category:en:Melbourne]]; otherwise placetype 'capital city' wouldn't
-- match against the placetype 'city' of Melbourne.
fallback = "city",
},
["caplc"] = {
link = "[[capital]] and [[large]]st [[city]]",
plural_link = false,
fallback = "capital city",
},
["captaincy"] = {
link = true,
preposition = "of",
class = "subpolity",
inherently_former = {"FORMER"},
},
["caravan city"] = {
link = "w",
fallback = "city",
class = "settlement",
inherently_former = {"ANCIENT", "FORMER"},
},
["castle"] = {
link = true,
fallback = "building",
},
["cathedral city"] = {
link = true,
fallback = "city",
},
["cattle station"] = {
-- Australia
link = true,
fallback = "farm",
},
["census area"] = {
link = true,
affix_type = "Suf",
has_neighborhoods = true,
class = "non-admin settlement",
},
["census-designated place"] = {
-- United States
link = true,
class = "non-admin settlement",
},
["census division"] = {
-- Canada
link = "w",
preposition = "of",
class = "subpolity",
},
["census town"] = {
link = "w",
fallback = "town",
},
["central business district"] = {
link = true,
fallback = "neighborhood",
},
["cercle"] = {
-- Mali
link = "+w:cercles of Mali",
preposition = "of",
class = "subpolity",
},
["ceremonial county"] = {
link = true,
fallback = "county",
},
["chain of islands"] = {
link = "[[chain]] of [[island]]s",
plural = "chains of islands",
plural_link = "[[chain]]s of [[island]]s",
fallback = "island",
},
["channel"] = {
link = true,
fallback = "strait",
},
["charter community"] = {
-- Northwest Territories, Canada
link = "w",
fallback = "village",
},
["chiwog"] = {
-- Bhutan
link = true,
preposition = "of",
class = "subpolity",
},
["city"] = {
link = true,
generic_before_non_cities = "in",
has_neighborhoods = true,
class = "settlement",
cat_handler = city_type_cat_handler,
default = {true},
},
["city-state"] = {
link = true,
category_link = "[[sovereign]] [[microstate]]s consisting of a single [[city]] and [[w:dependent territory|dependent territories]]",
has_neighborhoods = true,
class = "settlement",
["continent/*"] = {"City-states", "Cities in +++", "Countries in +++", "National capitals"},
default = {"City-states", "Cities", "Countries", "National capitals"},
},
["civil parish"] = {
-- Mostly England; similar to municipalities
link = true,
preposition = "of",
affix_type = "suf",
has_neighborhoods = true,
class = "subpolity",
},
["claimed political division"] = {
link = "[[claim]]ed [[political]] [[division]]",
class = "subpolity",
default = {true},
},
["co-capital"] = {
link = "[[co-]][[capital]]",
fallback = "capital city",
},
["coal city"] = {
link = "+w:coal town",
fallback = "city",
},
["coal town"] = {
link = "w",
fallback = "town",
},
["collectivity"] = {
link = "w",
preposition = "of",
-- No default; these are weird one-off governmental divisions in France (esp. for overseas collectivities)
class = "subpolity",
},
["colony"] = {
link = true,
fallback = "dependent territory",
},
["comarca"] = {
-- per Wikipedia: traditional region or local administrative division found in Portugal, Spain, and some of
-- their former colonies, like Brazil, Nicaragua, and Panama. In the Valencian Community, for example, it
-- sits between municipalities and provinces, something like a county or district.
link = true,
preposition = "of",
class = "subpolity",
},
["commandery"] = {
link = true,
preposition = "of",
class = "subpolity",
inherently_former = {"ANCIENT", "FORMER"},
},
["commonwealth"] = {
link = true,
preposition = "of",
-- No default; applies specifically to Puerto Rico
class = "subpolity",
},
["commune"] = {
link = true,
fallback = "municipality",
},
["community"] = {
link = true,
category_link = "[[community|communities]] of all sizes",
fallback = "village",
},
["community development block"] = {
-- in India; appears to be similar to a rural municipality; groups several villages, unclear if there will be
-- neighborhoods so I'm not setting `has_neighborhoods` for now
link = "w",
affix_type = "suf",
no_affix_strings = "block",
class = "subpolity",
},
["comune"] = {
-- Italy, Switzerland
link = true,
fallback = "municipality",
},
["condominium"] = {
link = true,
fallback = "polity",
},
["confederacy"] = {
link = true,
fallback = "confederation",
},
["confederation"] = {
link = true,
fallback = "polity",
},
["constituency"] = {
-- currently we have them as political divisions of Namibia but many countries have them
link = true,
preposition = "of",
class = "subpolity",
},
["constituent country"] = {
link = true,
preposition = "of",
class = "subpolity",
},
["constituent part"] = {
link = "separately",
preposition = "of",
class = "subpolity",
},
["constituent republic"] = {
-- Of Russia, Yugoslavia, etc.
link = "separately",
preposition = "of",
class = "subpolity",
},
["counties and county-level cities!"] = {
-- This is used when grouping counties and county-level cities under prefecture-level cities in China.
category_link = "[[county|counties]] and [[county-level city|county-level cities]]",
class = "subpolity",
},
["continent"] = {
link = true,
category_link = false, -- can't occur as a bare category
class = "natural feature",
default = {"Continents and continental regions"},
},
["continental region"] = {
link = "separately",
category_link = false, -- can't occur as a bare category
class = "geographic region",
fallback = "continent",
},
["continents and continental regions!"] = {
category_link = "[[continent]]s and [[continent]]-[[level]] [[region]]s (e.g. [[Polynesia]])",
class = "geographic region",
},
["council area"] = {
link = true,
-- in Scotland; similar to a county
preposition = "of",
affix_type = "suf",
class = "subpolity",
},
["country"] = {
link = true,
class = "polity",
["continent/*"] = {true, "Countries"},
default = {true},
},
["country-like entities!"] = {
category_link = "[[polity|polities]] not normally considered [[country|countries]] but treated similarly for categorization purposes; typically, [[unrecognized]] [[de-facto]] countries or [[w:dependent territory|dependent territories]]",
class = "polity",
},
["county"] = {
link = true,
preposition = "of",
display_handler = county_display_handler,
class = "subpolity",
},
["county borough"] = {
link = true,
-- in Wales; similar to a county
preposition = "of",
affix_type = "suf",
fallback = "borough",
class = "subpolity",
},
["county seat"] = {
link = true,
entry_placetype_use_the = true,
preposition = "of",
has_neighborhoods = true,
class = "capital",
},
["county town"] = {
link = true,
entry_placetype_use_the = true,
preposition = "of",
fallback = "town",
has_neighborhoods = true,
class = "capital",
},
["county-administered city"] = {
-- In Taiwan, per Wikipedia similar to a Taiwanese township or district, which is a small city.
-- NOT anything like a "county-level city" in PR China, which is a county masquerading as a city.
link = "w",
fallback = "city",
has_neighborhoods = true,
class = "settlement",
},
["county-controlled city"] = {
-- Taiwan
link = "w",
fallback = "county-administered city",
},
["county-level city"] = {
-- PR China
link = "w",
fallback = "prefecture-level city",
},
["crater lake"] = {
link = true,
fallback = "lake",
},
["creek"] = {
link = true,
fallback = "stream",
},
["Crown colony"] = {
link = "+crown colony",
fallback = "crown colony",
},
["crown colony"] = {
link = true,
fallback = "colony",
},
["Crown dependency"] = {
link = true,
fallback = "dependent territory",
},
["crown dependency"] = {
link = true,
fallback = "dependent territory",
},
["cultural area"] = {
link = "w",
fallback = "geographic and cultural area",
},
["cultural region"] = {
link = "w",
fallback = "geographic and cultural area",
},
["delegation"] = {
-- Tunisia
link = "+w:delegations of Tunisia",
preposition = "of",
class = "subpolity",
},
["department"] = {
link = true,
preposition = "of",
affix_type = "suf",
class = "subpolity",
},
["departmental capital"] = {
link = "separately",
fallback = "capital city",
},
["dependency"] = {
link = true,
fallback = "dependent territory",
},
["dependent territory"] = {
link = "w",
preposition = "of",
class = "subpolity",
former_type = "dependent territory",
bare_category_parent = "political divisions",
["country/*"] = {true},
default = {true},
},
["desert"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"ecosystems"},
default = {true},
},
["deserted mediaeval village"] = {
link = "w",
fallback = "deserted medieval village",
},
["deserted medieval village"] = {
link = "w",
fallback = "ANCIENT settlement",
},
["direct-administered municipality"] = {
-- China
link = "+w:direct-administered municipalities of China",
fallback = "municipality",
},
["direct-controlled municipality"] = {
-- several countries
link = "w",
fallback = "municipality",
},
["distributary"] = {
link = true,
preposition = "of",
fallback = "river",
},
["district"] = {
link = true,
preposition = "of",
affix_type = "suf",
-- Grrr! FIXME! Here is where we need handlers for `class`. Using similar logic to
-- district_neighborhood_cat_handler, we need to check if we're below or above a city to determine if the class
-- is "settlement" or "subpolity".
class = "subpolity",
cat_handler = district_neighborhood_cat_handler,
-- No default. Countries for which districts are political divisions will get entries.
},
["districts and autonomous regions!"] = {
-- This and other similar "combined placetypes" are for use in the plural when grouping first-level
-- administrative regions of certain countries, in this case Portugal.
category_link = "[[district]]s and [[autonomous region]]s",
class = "subpolity",
},
["districts and autonomous territorial units!"] = {
-- This and other similar "combined placetypes" are for use in the plural when grouping first-level
-- administrative regions of certain countries, in this case Moldova.
category_link = "[[district]]s and [[w:autonomous territorial unit|autonomous territorial unit]]s",
class = "subpolity",
},
["district capital"] = {
link = "separately",
fallback = "capital city",
},
["district headquarters"] = {
link = "separately",
fallback = "administrative centre",
},
["district municipality"] = {
-- In Canada, a district municipality is equivalent to a rural municipality and won't have neighborhoods; in
-- South Africa, district municipalities group local municipalities and hence won't have neighborhoods.
link = "w",
preposition = "of",
affix_type = "suf",
no_affix_strings = {"district", "municipality"},
fallback = "municipality",
class = "subpolity",
},
["division"] = {
link = true,
preposition = "of",
class = "subpolity",
},
["division capital"] = {
link = "separately",
fallback = "capital city",
},
["dome"] = {
link = true,
fallback = "mountain",
},
["dormant volcano"] = {
link = true,
fallback = "volcano",
},
["duchy"] = {
link = true,
fallback = "polity",
},
["emirate"] = {
link = true,
preposition = "of",
-- FIXME: Can be subpolities (of the United Arab Emirates).
fallback = "polity",
},
["empire"] = {
link = true,
fallback = "polity",
},
["enclave"] = {
link = true,
preposition = "of",
-- Enclaves can theoretically be any size but assume a subpolity.
class = "subpolity",
},
["entity"] = {
-- Bosnia and Herzegovina
link = "+w:entities of Bosnia and Herzegovina",
preposition = "of",
class = "subpolity",
},
["escarpment"] = {
link = true,
fallback = "mountain",
},
["ethnographic region"] = {
-- used in Lithuania
link = "+w:ethnographic regions of Lithuania",
fallback = "geographic and cultural area",
},
["exclave"] = {
link = true,
preposition = "of",
-- exclaves can theoretically be any size but assume a subpolity.
class = "subpolity",
},
["external territory"] = {
link = "separately",
fallback = "dependent territory",
},
["farm"] = {
link = true,
class = "non-admin settlement",
default = {"Farms and ranches"},
},
["farms and ranches!"] = {
category_link = "[[farm]]s and [[ranch]]es",
class = "non-admin settlement",
},
["federal city"] = {
link = "w",
preposition = "of",
fallback = "city",
},
["federal district"] = {
link = true,
preposition = "of",
-- Might have neighborhoods as federal districts are often cities (e.g. Mexico City)
has_neighborhoods = true,
class = "settlement",
},
["federal subject"] = {
-- In Russia; a generic term for first-level administrative divisions (republics, oblasts, okrugs, krais,
-- autonomous okrugs and autonomous oblasts).
link = "w",
preposition = "of",
class = "subpolity",
},
["federal territory"] = {
link = "w",
fallback = "territory",
},
["fictional location"] = {
link = "separately",
former_type = "!",
class = "hypothetical location",
bare_category_parent = "places",
default = {true},
},
["First Nations reserve"] = {
-- Canada
link = "[[First Nations]] [[w:Indian reserve|reserve]]",
-- Wikipedia uses "Indian reserve"; presumably that is the legal term
fallback = "Indian reserve",
class = "subpolity",
},
["fjord"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"bodies of water"},
default = {true},
},
["footpath"] = {
link = true,
fallback = "road",
},
["forest"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"ecosystems", "forestry"},
default = {true},
},
["fort"] = {
link = true,
fallback = "building",
},
["fortress"] = {
link = true,
-- The default plural algorithm gets this right but the singularization algorithm incorrectly converts
-- fortresses -> fortresse, so put an entry here to ensure we singularize correctly.
plural = "fortresses",
fallback = "building",
},
["frazione"] = {
link = "w",
fallback = "hamlet",
},
["freeway"] = {
link = true,
fallback = "road",
},
["French prefecture"] = {
link = "[[w:prefectures in France|prefecture]]",
entry_placetype_use_the = true,
preposition = "of",
has_neighborhoods = true,
class = "capital",
},
["geographic and cultural area"] = {
link = "+w:cultural area",
-- `generic_before_non_cities` is used when generating the category description of categories of the format
-- `Geographic and cultural areas of PLACE`. `preposition` is used when generating {{place}} description and
-- categories for any placetype that falls back to `geographic and cultural area`.
generic_before_non_cities = "of",
preposition = "of",
class = "geographic region",
bare_category_parent = "places",
["country/*"] = {true},
["constituent country/*"] = {true},
["continent/*"] = {true},
default = {true},
},
["geographic area"] = {
link = "+w:geographic region",
fallback = "geographic and cultural area",
},
["geographic region"] = {
link = "w",
fallback = "geographic and cultural area",
},
["geographical area"] = {
link = "w",
fallback = "geographic and cultural area",
},
["geographical region"] = {
link = "w",
fallback = "geographic and cultural area",
},
["geopolitical zone"] = {
-- Nigeria
link = true,
preposition = "of",
class = "subpolity",
},
["gewog"] = {
-- Bhutan
link = true,
preposition = "of",
class = "subpolity",
},
["ghost town"] = {
link = true,
generic_before_non_cities = "in",
class = "non-admin settlement",
bare_category_parent = "former settlements",
cat_handler = city_type_cat_handler,
default = {true},
},
["glen"] = {
link = true,
fallback = "valley",
},
["governorate"] = {
link = true,
preposition = "of",
affix_type = "suf",
class = "subpolity",
},
["greater administrative region"] = {
-- China (former division)
link = "w",
preposition = "of",
class = "subpolity",
inherently_former = {"FORMER"},
},
["gromada"] = {
-- Poland (former division)
link = "w",
preposition = "of",
affix_type = "Pref",
class = "subpolity",
inherently_former = {"FORMER"},
},
["group of islands"] = {
link = "[[group]] of [[island]]s",
plural = "groups of islands",
plural_link = "[[group]]s of [[island]]s",
fallback = "island group",
},
["gulf"] = {
link = true,
preposition = "of",
holonym_use_the = true,
class = "natural feature",
addl_bare_category_parents = {"bodies of water"},
default = {true},
},
["hamlet"] = {
link = true,
fallback = "village",
},
["harbor city"] = {
link = "separately",
fallback = "city",
},
["harbor town"] = {
link = "separately",
fallback = "town",
},
["harbour city"] = {
link = "separately",
fallback = "city",
},
["harbour town"] = {
link = "separately",
fallback = "town",
},
["headland"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"landforms"},
default = {true},
},
["headquarters"] = {
link = "w",
fallback = "administrative centre",
},
["heath"] = {
link = true,
fallback = "moor",
},
["hemisphere"] = {
link = true,
entry_placetype_use_the = true,
fallback = "continental region",
},
["highway"] = {
link = true,
fallback = "road",
},
["hill"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"landforms"},
default = {true},
},
["hill station"] = {
link = "w",
fallback = "town",
},
["hill town"] = {
link = "w",
fallback = "town",
},
["historic region"] = {
-- provided only for the link
link = "+w:historical region",
fallback = "FORMER geographic region",
},
["historical county"] = {
-- needed for historical counties of England/etc.
link = "+w:historic county",
fallback = "FORMER subpolity",
},
["historical region"] = {
-- provided only for the link
link = "w",
fallback = "FORMER geographic region",
},
["home rule city"] = {
link = "w",
fallback = "city",
},
["home rule municipality"] = {
link = "w",
fallback = "municipality",
},
["hot spring"] = {
link = true,
fallback = "spring",
},
["house"] = {
link = true,
fallback = "building",
},
["housing estate"] = {
-- not the same as a housing project (i.e. public housing)
link = true,
-- not exactly the case but approximately
fallback = "neighborhood",
},
["hromada"] = {
-- Ukraine
link = "w",
disallow_in_entries = "Use placetype 'urban hromada', 'rural hromada' or 'settlement hromada' in place of bare 'hromada'",
disallow_in_holonyms = "Use placetype 'urban hromada'/'uhrom', 'rural hromada'/'rhrom' or 'settlement hromada'/'shrom' in place of bare 'hromada'",
preposition = "of",
affix_type = "suf",
class = "subpolity",
},
["inactive volcano"] = {
link = "w",
fallback = "dormant volcano",
},
["independent city"] = {
link = true,
fallback = "city",
},
["independent town"] = {
link = "+independent city",
fallback = "town",
},
["Indian reservation"] = {
link = "w",
-- In the US. Also known as "Native American reservation" or "domestic dependent nation", and the reservations
-- themselves often use the term "nation" in their official name (e.g. the "Navajo Nation"). But Wikipedia puts
-- the article at [[w:Indian reservation]] and uses that term when describing e.g. what the Navajo Nation is,
-- so this must still be the legal term.
preposition = "of",
class = "subpolity",
default = {true},
},
["Indian reserve"] = {
link = "w",
-- In Canada. "First Nations reserve" sounds more modern/PC but Wikipedia uses "Indian reserve"; presumably that
-- is still the legal term.
preposition = "of",
class = "subpolity",
default = {true},
},
["inland sea"] = {
-- note, we also have 'inland' as a qualifier
link = true,
fallback = "sea",
},
["inner city area"] = {
link = "[[inner city]] [[area]]",
fallback = "neighborhood",
},
["island"] = {
link = true,
preposition = "of",
class = "natural feature",
addl_bare_category_parents = {"landforms"},
default = {true},
},
["island country"] = {
-- FIXME: The following should map to both 'island' and 'country'.
link = "w",
fallback = "country",
},
["island group"] = {
link = "separately",
fallback = "island",
},
["island municipality"] = {
link = "w",
fallback = "municipality",
},
["islet"] = {
link = "w",
fallback = "island",
},
["Israeli settlement"] = {
link = "w",
class = "settlement",
default = {true},
},
["judicial capital"] = {
link = "w",
fallback = "capital city",
},
["khanate"] = {
link = true,
fallback = "polity",
},
["kibbutz"] = {
link = true,
plural = "kibbutzim",
class = "non-admin settlement",
default = {true},
},
["kingdom"] = {
link = true,
fallback = "monarchy",
},
["krai"] = {
link = true,
preposition = "of",
affix_type = "Suf",
class = "subpolity",
},
["lake"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"bodies of water"},
default = {true},
},
["landforms!"] = {
category_link = "[[landform]]s",
bare_category_parent = "places",
addl_bare_category_parents = {"Earth"},
},
["largest city"] = {
link = "[[large]]st [[city]]",
entry_placetype_use_the = true,
fallback = "city",
has_neighborhoods = true,
},
["league"] = {
link = true,
fallback = "confederation",
},
["legislative capital"] = {
link = "separately",
fallback = "capital city",
},
["library"] = {
link = true,
fallback = "building",
},
["lieutenancy area"] = {
-- used in the United Kingdom; per Wikipedia:
-- In England, lieutenancy areas are colloquially known as the ceremonial counties, although this phrase does
-- not appear in any legislation referring to them. The lieutenancy areas of Scotland are subdivisions of
-- Scotland that are more or less based on the counties of Scotland, making use of the major cities as separate
-- entities.[2] In Wales, the lieutenancy areas are known as the preserved counties of Wales and are based on
-- those used for lieutenancy and local government between 1974 and 1996. The lieutenancy areas of Northern
-- Ireland correspond to the six counties and two former county boroughs.[3]
link = "w",
fallback = "ceremonial county",
},
["local authority district"] = {
link = "w",
fallback = "local government district",
},
["local government area"] = {
-- Australia
link = "w",
preposition = "of",
class = "subpolity",
},
["local council"] = {
-- Malta; similar to municipalities
link = "+w:local councils of Malta",
preposition = "of",
fallback = "municipality",
},
["local government district"] = {
link = "w",
preposition = "of",
affix_type = "suf",
affix = "district",
class = "subpolity",
},
["local government district with borough status"] = {
link = "[[w:local government district|local government district]] with [[w:borough status|borough status]]",
plural = "local government districts with borough status",
plural_link = "[[w:local government district|local government districts]] with [[w:borough status|borough status]]",
preposition = "of",
affix_type = "suf",
affix = "district",
class = "subpolity",
},
["local urban district"] = {
link = "w",
fallback = "unincorporated community",
},
["locality"] = {
link = "+w:locality (settlement)",
-- not necessarily true, but usually is the case
fallback = "village",
},
["London borough"] = {
link = "w",
preposition = "of",
affix_type = "pref",
affix = "borough",
fallback = "local government district with borough status",
has_neighborhoods = true,
},
["macroregion"] = {
link = true,
fallback = "region",
},
["man-made structures!"] = {
category_link = "[[w:geographical feature#Engineered constructs|man-made structures]] such as [[airport]]s, [[university|universities]] and [[metro station]]s",
bare_category_parent = "places",
},
["manor"] = {
-- FIXME: or is this more like a farm?
link = true,
fallback = "building",
},
["marginal sea"] = {
link = true,
preposition = "of",
fallback = "sea",
},
["market city"] = {
link = "+market town",
fallback = "city",
},
["market town"] = {
link = true,
fallback = "town",
},
["massif"] = {
link = true,
fallback = "mountain",
},
["megacity"] = {
link = true,
fallback = "city",
},
["metro station"] = {
link = true,
class = "man-made structure",
},
["metropolitan borough"] = {
link = true,
preposition = "of",
affix_type = "Pref",
no_affix_strings = {"borough", "city"},
fallback = "local government district",
has_neighborhoods = true,
},
["metropolitan city"] = {
-- These exist e.g. in Italy and are more like municipalities or even provinces than cities.
link = true,
preposition = "of",
affix_type = "Pref",
no_affix_strings = {"metropolitan", "city"},
class = "subpolity",
},
["metropolitan county"] = {
link = true,
fallback = "county",
},
["metropolitan municipality"] = {
-- In South Africa, metropolitan municipalities group local municipalities and are like districts, between
-- provinces and municipalities.
-- In Turkey, metropolitan municipalities are provinces-level.
link = "w",
preposition = "of",
affix_type = "Suf",
no_affix_strings = {"metropolitan", "municipality"},
fallback = "municipality",
class = "subpolity",
},
["microdistrict"] = {
-- residential complex in post-Soviet states
link = true,
fallback = "neighborhood",
},
["micronations!"] = {
-- FIXME, merge with microstate
category_link = "[[micronation]]s",
bare_category_parent = "countries",
},
["microstate"] = {
link = true,
fallback = "country",
},
["military base"] = {
link = "w",
class = "settlement", -- or "man-made structure"?
default = {true},
},
["minster town"] = {
-- England
link = "separately",
fallback = "town",
},
["monarchy"] = {
link = true,
fallback = "polity",
},
["moor"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"landforms", "ecosystems"},
default = {true},
},
["moorland"] = {
link = true,
fallback = "moor",
},
["motorway"] = {
link = true,
fallback = "road",
},
["mountain"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"landforms"},
default = {true},
},
["mountain indigenous district"] = {
-- Taiwan
link = "+w:district (Taiwan)",
fallback = "district",
},
["mountain indigenous township"] = {
-- Taiwan
link = "+w:township (Taiwan)",
fallback = "township",
},
["mountain pass"] = {
link = true,
-- The default plural algorithm gets this right but the singularization algorithm incorrectly converts
-- passes -> passe, so put an entry here to ensure we singularize correctly.
plural = "mountain passes",
class = "natural feature",
addl_bare_category_parents = {"mountains"},
default = {true},
},
["mountain range"] = {
link = true,
fallback = "mountain",
},
["mountainous region"] = {
link = "separately",
fallback = "region",
},
["mukim"] = {
-- Malaysia, Brunei, Indonesia, Singapore
link = true,
preposition = "of",
class = "subpolity",
},
["municipal district"] = {
link = "w",
-- meaning varies depending on the country; for now, assume no neighborhoods.
-- FIXME: has_neighborhoods might have to be a function that looks at the containing holonyms.
preposition = "of",
affix_type = "Pref",
no_affix_strings = "district",
fallback = "municipality",
},
["municipality"] = {
link = true,
preposition = "of",
has_neighborhoods = true,
class = "subpolity",
},
["municipality with city status"] = {
link = "[[municipality]] with [[w:city status|city status]]",
plural = "municipalities with city status",
plural_link = "[[municipality|municipalities]] with [[w:city status|city status]]",
fallback = "municipality",
},
["museum"] = {
link = true,
fallback = "building",
},
["mythological location"] = {
link = "separately",
former_type = "!",
class = "hypothetical location",
bare_category_parent = "places",
default = {true},
},
["named bridges!"] = {
category_link = "notable [[bridge]]s",
bare_category_parent = "man-made structures",
addl_bare_category_parents = {"bridges"},
},
["named buildings!"] = {
category_link = "notable [[house]]s, [[library|libraries]] and other [[building]]s",
bare_category_parent = "man-made structures",
addl_bare_category_parents = {"buildings"},
},
["named roads!"] = {
category_link = "notable [[road]]s, [[highway]]s, [[trail]]s and similar linear structures",
bare_category_parent = "man-made structures",
addl_bare_category_parents = {"roads"},
},
["national capital"] = {
link = "w",
fallback = "capital city",
},
["national park"] = {
link = true,
fallback = "park",
},
["natural features!"] = {
category_link = "[[w:geographical feature#Natural features|natural features]] such as [[lake]]s, [[mountain]]s, [[island]]s and [[ocean]]s",
bare_category_parent = "places",
},
["neighborhood"] = {
-- The majority of the properties here apply to both `neighborhoods` and `neighbourhoods`; the choice of which
-- one to use is made by district_neighborhood_cat_handler() based on the value of `british_spelling` for the
-- location (city, political division, etc.) of the holonym that follows the word "neighbo(u)hoods" in the
-- category name. It does *NOT* depend on whether the {{place}} call uses "neighborhoods" or "neighbourhoods".
-- (In general it can't, because other things like "urban areas", "districts", "subdivisions" and the like also
-- categorize as neighbo(u)rhoods.)
link = true,
-- See below. These are used by category handlers in [[Module:category tree/topic cat/data/Places]].
generic_before_non_cities = "in",
generic_before_cities = "of",
-- The following text is suitable for the top-level description of a neighborhood as well as categories of the
-- form `Neighborhoods in POLDIV` e.g. `Neighborhoods in Illinois, USA` but not for categories of the form
-- `Neighborhoods of Chicago`, where we'd get "... and other subportions of [[city|cities]] of [[Chicago]]".
category_link = "[[neighborhood]]s, [[district]]s and other subportions of [[city|cities]]",
category_link_before_city = "[[neighborhood]]s, [[district]]s and other subportions",
-- NOTE: This setting is needed for administrative divisions like barangays that fall back to `neighborhood`,
-- when set in [[Module:place/locations]] for a specific country (e.g. the Philippines). The above settings
-- for `generic_before_non_cities` and `generic_before_cities` are used by category handlers in
-- [[Module:category tree/topic cat/data/Places]] for `Neighborhoods in POLDIV` and `Neighborhoods of CITY`
-- categories. In fact, district_neighborhood_cat_handler() does not currently pay attention to them, but
-- generates "of" before cities and "in" before non-cities regardless. (FIXME: We should change that.)
preposition = "of",
class = "non-admin settlement",
cat_handler = district_neighborhood_cat_handler,
},
["neighbourhood"] = {
link = true,
category_link = "[[neighbourhood]]s, [[district]]s and other subportions of [[city|cities]]",
category_link_before_city = "[[neighbourhood]]s, [[district]]s and other subportions",
fallback = "neighborhood",
},
["new area"] = {
-- China (type of economic development zone, varying greatly in size)
link = "w",
preposition = "in",
class = "subpolity", --?
},
["new town"] = {
link = true,
fallback = "town",
},
["non-city capital"] = {
link = "[[capital]]",
entry_placetype_use_the = true,
preposition = "of",
has_neighborhoods = true,
class = "capital",
cat_handler = function(data)
return capital_city_cat_handler(data, "non-city")
end,
-- FIXME, do we need the following?
default = {true},
},
["non-metropolitan county"] = {
link = "w",
fallback = "county",
},
["non-metropolitan district"] = {
link = "w",
fallback = "local government district",
},
["non-sovereign kingdom"] = {
-- especially in Africa and Asia
link = "+w:non-sovereign monarchy",
generic_before_non_cities = "in",
class = "subpolity",
["country/*"] = {true},
["continent/*"] = {true},
default = {true},
},
["non-sovereign monarchy"] = {
link = "w",
fallback = "non-sovereign kingdom",
},
["oblast"] = {
link = true,
preposition = "of",
affix_type = "Suf",
class = "subpolity",
},
["oblasts and autonomous republics!"] = {
-- This and other similar "combined placetypes" are for use in the plural when grouping first-level
-- administrative regions of certain countries, in this case Ukraine.
category_link = "[[oblast]]s and [[w:autonomous republic|autonomous republic]]s",
class = "subpolity",
},
["ocean"] = {
link = true,
holonym_use_the = true,
class = "natural feature",
addl_bare_category_parents = {"seas", "bodies of water"},
default = {true},
},
["okrug"] = {
link = true,
preposition = "of",
affix_type = "Suf",
class = "subpolity",
},
["overseas collectivity"] = {
link = "w",
fallback = "collectivity",
},
["overseas department"] = {
link = "w",
fallback = "department",
},
["overseas territory"] = {
link = "w",
fallback = "dependent territory",
},
["parish"] = {
link = true,
preposition = "of",
affix_type = "suf",
class = "subpolity",
},
["parish municipality"] = {
-- in Quebec, often similar to a rural village; the famous [[Saint-Louis-du-Ha! Ha!]] is one of them.
link = "+w:parish municipality (Quebec)",
preposition = "of",
fallback = "municipality",
has_neighborhoods = true,
},
["parish seat"] = {
link = true,
entry_placetype_use_the = true,
preposition = "of",
class = "capital",
has_neighborhoods = true,
},
["park"] = {
link = true,
class = "man-made structure",
default = {true},
},
["pass"] = {
link = "+mountain pass",
-- The default plural algorithm gets this right but the singularization algorithm incorrectly converts
-- passes -> passe, so put an entry here to ensure we singularize correctly.
plural = "passes",
fallback = "mountain pass",
},
["path"] = {
link = true,
fallback = "road",
},
["peak"] = {
link = true,
fallback = "mountain",
},
["peninsula"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"landforms"},
default = {true},
},
["periphery"] = {
link = true,
preposition = "of",
class = "subpolity",
},
["places!"] = {
generic_before_non_cities = "in",
generic_before_cities = "in",
class = "generic place",
category_link = "[[place]]s of all sorts",
-- `category_link_top_level` control the description used in the top-level [[Category:Places]] and
-- language-specific variants such as [[Category:en:Places]]. The actual text for a language-spefic variant is
-- "{{{langname}}} names of [[geographical]] [[place]]s of all sorts; [[toponym]]s." where the "names of"
-- portion is automatically generated by the appropriate handler in
-- [[Module:category tree/topic cat/data/Places]].
category_link_top_level = "[[geographical]] [[place]]s of all sorts; [[toponym]]s",
bare_category_parent = "names",
},
["planned community"] = {
-- Include this so we don't categorize 'planned community' into villages, as 'community' does.
link = true,
class = "settlement",
has_neighborhoods = true,
},
["plateau"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"landforms"},
default = {true},
-- FIXME: Should generate both "Plateaus" and the appropriate 'geographic and cultural area' category
},
["Polish colony"] = {
link = "[[w:colony (Poland)|colony]]",
affix_type = "suf",
affix = "colony",
fallback = "village",
has_neighborhoods = true,
},
["political divisions!"] = {
category_link = "[[political]] [[division]]s and [[subdivision]]s, such as [[state]]s, [[province]]s, [[county|counties]] or [[district]]s",
bare_category_parent = "places",
},
["polity"] = {
link = true,
category_link = "[[independent]] or [[semi-]][[independent]] [[polity|polities]]",
class = "polity",
bare_category_parent = "places",
default = {true},
},
["populated place"] = {
link = "+w:populated place",
-- not necessarily true, but usually is the case
fallback = "village",
},
["port"] = {
link = true,
class = "man-made structure",
default = {true},
},
["port city"] = {
-- FIXME: should categorize into "Ports" as well as "Cities"
link = true,
fallback = "city",
},
["port town"] = {
-- FIXME: should categorize into "Ports" as well as "Towns"
link = "w",
fallback = "town",
},
["prefecture"] = {
-- FIXME! `prefecture` is like a county in Japan and elsewhere but a department capital city in France.
-- May need `has_neighborhoods` to be a function.
link = true,
preposition = "of",
display_handler = prefecture_display_handler,
class = "subpolity",
},
["prefecture-level city"] = {
-- China; they are huge entities with a central city; not cities themselves.
link = "w",
preposition = "of",
class = "subpolity",
},
["preserved county"] = {
-- In Wales; they are former counties enshrined in law; there are 8 of them and each consists of one or more
-- "principal areas" (styled as "counties" or "county boroughs"), of which there are 22.
link = "w",
preposition = "of",
class = "subpolity",
inherently_former = {"FORMER"},
},
["primary area"] = {
-- a grouping of "districts" (neighborhoods) in Gothenburg, Sweden
link = "+w:sv:primärområde",
fallback = "neighborhood",
},
["principality"] = {
link = true,
fallback = "monarchy",
},
["promontory"] = {
link = true,
fallback = "headland",
},
["protectorate"] = {
link = true,
fallback = "dependent territory",
},
["province"] = {
link = true,
preposition = "of",
display_handler = province_display_handler,
class = "subpolity",
},
["provinces and autonomous regions!"] = {
-- This and other similar "combined placetypes" are for use in the plural when grouping first-level
-- administrative regions of certain countries, in this case China.
category_link = "[[province]]s and [[autonomous region]]s",
class = "subpolity",
},
["provinces and territories!"] = {
-- This and other similar "combined placetypes" are for use in the plural when grouping first-level
-- administrative regions of certain countries, in this case Canada and Pakistan.
category_link = "[[province]]s and [[territory|territories]]",
class = "subpolity",
},
["provincial capital"] = {
link = "separately",
fallback = "capital city",
},
["raion"] = {
link = true,
preposition = "of",
affix_type = "Suf",
class = "subpolity",
},
["ranch"] = {
link = true,
fallback = "farm",
},
["range"] = {
-- FIXME: Where is this used? Is it a mountain range?
link = true,
holonym_use_the = true,
class = "natural feature",
},
["regency"] = {
link = true,
preposition = "of",
class = "subpolity",
},
["region"] = {
link = true,
preposition = "of",
-- If 'region' isn't a specific administrative division, fall back to 'geographic and cultural area'
fallback = "geographic and cultural area",
-- "former region" is a subpolity but traditional/historic(al)/ancient/medieval/etc. is a geographic region
class = "geographic region",
},
["regional capital"] = {
link = "separately",
fallback = "capital city",
},
["regional county municipality"] = {
-- Quebec
link = "w",
preposition = "of",
affix_type = "Suf",
no_affix_strings = {"municipality", "county"},
fallback = "municipality",
},
["regional district"] = {
link = "w",
preposition = "of",
affix_type = "Pref",
no_affix_strings = "district",
fallback = "district",
},
["regional municipality"] = {
link = "w",
preposition = "of",
affix_type = "Pref",
no_affix_strings = "municipality",
fallback = "municipality",
},
["regional unit"] = {
link = "w",
preposition = "of",
affix_type = "suf",
class = "subpolity",
},
["registration county"] = {
-- Used in Scotland for land registration purposes; formerly used in England, Wales and Ireland for statistical
-- purposes (registration of births, deaths and marriages, and for the output of census information).
link = "w",
fallback = "county",
},
["republic"] = {
-- Of Russia, Yugoslavia, etc. "Republics" in general are sovereign but we use "country" in that case.
link = true,
fallback = "constituent republic",
},
["research base"] = {
link = "+w:research station",
fallback = "research station",
},
["research station"] = {
link = "w",
class = "non-admin settlement", -- or "man-made structure"?
default = {true},
},
["reservoir"] = {
link = true,
fallback = "lake",
},
["residential area"] = {
link = "separately",
fallback = "neighborhood",
},
["resort city"] = {
link = "w",
fallback = "city",
},
["resort town"] = {
link = "w",
fallback = "town",
},
["river"] = {
link = true,
generic_before_non_cities = "in",
holonym_use_the = true,
class = "natural feature",
addl_bare_category_parents = {"bodies of water"},
cat_handler = city_type_cat_handler,
["continent/*"] = {true},
default = {true},
},
["river island"] = {
link = "w",
fallback = "island",
},
["road"] = {
link = true,
class = "man-made structure",
default = {"Named roads"},
},
["Roman province"] = {
-- FIXME! Eliminate this in favor of 'former province|emp/Roman Empire'
link = "w",
default = {"Provinces of the Roman Empire"},
class = "subpolity",
},
["royal borough"] = {
link = "w",
preposition = "of",
affix_type = "Pref",
no_affix_strings = {"royal", "borough"},
fallback = "local government district with borough status",
has_neighborhoods = true,
},
["royal burgh"] = {
link = true,
fallback = "borough",
},
["royal capital"] = {
link = "w",
fallback = "capital city",
},
["rural committee"] = {
-- Hong Kong; a group of villages
link = "w",
affix_type = "Suf",
has_neighborhoods = true,
class = "settlement",
},
["rural community"] = {
-- New Brunswick
link = "+w:list of municipalities in New_Brunswick#Rural communities",
fallback = "municipality",
},
["rural hromada"] = {
link = "[[rural]] [[w:hromada|hromada]]",
affix_type = "suf",
fallback = "hromada",
},
["rural municipality"] = {
link = "w",
preposition = "of",
affix_type = "Pref",
no_affix_strings = "municipality",
fallback = "municipality",
has_neighborhoods = true, --?
},
["rural township"] = {
-- Taiwan
link = "+w:rural township (Taiwan)",
fallback = "township",
},
["sanctuary"] = {
link = true,
fallback = "temple",
},
["satrapy"] = {
link = true,
preposition = "of",
class = "subpolity",
inherently_former = {"ANCIENT", "FORMER"},
},
["sea"] = {
link = true,
holonym_use_the = true,
class = "natural feature",
addl_bare_category_parents = {"bodies of water"},
default = {true},
},
["seaport"] = {
link = true,
fallback = "port",
},
["seat"] = {
link = true,
fallback = "administrative centre",
},
["self-administered area"] = {
-- Myanmar (groups self-administered divisions and zones)
link = "+w:self-administered zone",
preposition = "of",
class = "subpolity",
},
["self-administered division"] = {
-- Myanmar (only one of them: Wa Self-Administered Division)
link = "w",
fallback = "self-administered area",
},
["self-administered zone"] = {
-- Myanmar (five of them)
link = "w",
fallback = "self-administered area",
},
["separatist state"] = {
link = "separately",
fallback = "unrecognized country",
},
["settlement"] = {
link = true,
category_link = "[[settlement]]s such as [[city|cities]], [[village]]s and [[farm]]s",
bare_category_parent = "places",
-- not necessarily true, but usually is the case
fallback = "village",
},
["settlement hromada"] = {
link = "[[w:Populated places in Ukraine#Rural settlements|settlement]] [[w:hromada|hromada]]",
affix_type = "suf",
fallback = "hromada",
},
["sheading"] = {
-- Isle of Man
link = true,
fallback = "district",
},
["sheep station"] = {
-- Australia
link = true,
fallback = "farm",
},
["shire"] = {
link = true,
fallback = "county",
},
["shire county"] = {
link = "w",
fallback = "county",
},
["shire town"] = {
link = true,
fallback = "county seat",
},
["ski resort city"] = {
link = "[[ski resort]] [[city]]",
fallback = "city",
},
["ski resort town"] = {
link = "[[ski resort]] [[town]]",
fallback = "town",
},
["spa city"] = {
link = "+w:spa town",
fallback = "city",
},
["spa town"] = {
link = "w",
fallback = "town",
},
["space station"] = {
link = true,
fallback = "research station",
},
["special administrative region"] = {
-- in China; in practice they are city-like (Hong Kong, Macau); also [[Oecusse]] in East Timor is formally a
-- "special administrative region"; North Korea had one such region planned (Sinuiju) but abandoned; Indonesia
-- has similar "special regions" of Jakarta, Yogyakarta and Aceh; and South Sudan has three "special
-- administrative areas"
link = "+w:special administrative regions of China",
preposition = "of",
class = "subpolity",
has_neighborhoods = true, --?
-- no suffix since places in Hong Kong or Macau are listed without China, except Hong Kong and Macau themselves
-- they also contain regions (or areas), e.g. [[Kowloon]], so it would be confusing
suffix = "",
},
["special collectivity"] = {
link = "w",
fallback = "collectivity",
},
["special municipality"] = {
-- formerly linked to the Taiwan article but there are also special municipalities of the Netherlands
link = "w",
fallback = "municipality",
},
["special ward"] = {
-- Tokyo
link = true,
fallback = "municipality",
},
["spit"] = {
link = true,
fallback = "peninsula",
},
["spring"] = {
link = true,
class = "natural feature",
default = {true},
},
["star"] = {
link = true,
class = "natural feature",
default = {true},
},
["state"] = {
link = true,
preposition = "of",
class = "subpolity",
-- 'former/historical state' could refer either to a state of a country (a division) or a state = sovereign
-- entity. The latter appears more common (e.g. in various "ancient states" of East Asia).
former_type = "polity",
},
["states and territories!"] = {
-- This and other similar "combined placetypes" are for use in the plural when grouping first-level
-- administrative regions of certain countries, in this case Australia.
category_link = "[[state]]s and [[territory|territories]]",
class = "subpolity",
},
["states and union territories!"] = {
-- This and other similar "combined placetypes" are for use in the plural when grouping first-level
-- administrative regions of certain countries, in this case India.
category_link = "[[state]]s and [[union territory|union territories]]",
class = "subpolity",
},
["state capital"] = {
link = "separately",
fallback = "capital city",
},
["state park"] = {
link = true,
fallback = "park",
},
["state-level new area"] = {
-- China (type of economic development zone, varying greatly in size)
link = "w",
fallback = "new area",
},
["statistical region"] = {
-- Slovenia
link = true,
fallback = "administrative region",
},
["statutory city"] = {
link = "w",
fallback = "city",
},
["statutory town"] = {
link = "w",
fallback = "town",
},
["strait"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"bodies of water"},
default = {true},
},
["stream"] = {
link = true,
fallback = "river",
},
["street"] = {
link = true,
fallback = "road",
},
["strip"] = {
link = true,
fallback = "geographic region",
},
["strip of land"] = {
link = "[[strip]] of [[land]]",
plural = "strips of land",
plural_link = "[[strip]]s of [[land]]",
fallback = "geographic region",
},
["sub-metropolitan city"] = {
link = "+w:List of cities in Nepal#Sub-metropolitan cities",
fallback = "city",
},
["sub-prefectural city"] = {
link = "w",
fallback = "subprovincial city",
},
["subdistrict"] = {
link = true,
preposition = "of",
has_neighborhoods = true, --?
-- FIXME: subdistricts can be neighborhood-like (of Jakarta) or larger (in China); need a handler
class = "subpolity",
default = {true},
},
["subdivision"] = {
link = true,
preposition = "of",
affix_type = "suf",
-- FIXME: subdivisions can be neighborhood-like or larger; need a handler
class = "subpolity",
cat_handler = district_neighborhood_cat_handler,
},
["submerged ghost town"] = {
-- FIXME: Consider just having "submerged" as a qualifier.
link = "[[submerged]] [[ghost town]]",
fallback = "ghost town",
},
["subnational kingdom"] = {
link = "+w:subnational monarchy",
fallback = "non-sovereign kingdom",
},
["subnational monarchy"] = {
link = "w",
fallback = "non-sovereign kingdom",
},
["subprefecture"] = {
link = true,
affix_type = "suf",
preposition = "of",
class = "subpolity",
},
["subprovince"] = {
link = true,
preposition = "of",
class = "subpolity",
},
["subprovincial city"] = {
link = "w",
-- China; special status given to certain prefecture-level cities
fallback = "prefecture-level city",
},
["subprovincial district"] = {
link = "w",
-- China; special status given to Binhai New Area and Pudong New Area, which are county-level districts
preposition = "of",
class = "subpolity",
},
["subregion"] = {
link = true,
fallback = "geographic region",
},
["suburb"] = {
link = true,
-- The following text is suitable for the top-level description of a suburb as well as categories of the form
-- 'Suburbs in POLDIV' e.g. 'Suburbs in Illinois, USA' but not for categories of the form 'Suburbs of Chicago',
-- where we'd get "[[suburb]]s of [[city|cities]] of [[Chicago]]".
category_link = "[[suburb]]s of [[city|cities]]",
category_link_before_city = "[[suburb]]s",
-- See comments under "neighborhood" for the following three settings. They are used by
-- [[Module:category tree/topic cat/data/Places]] for generating the text of 'Suburbs in/of PLACE' categories
-- but currently ignored by district_neighborhood_cat_handler (which actually generates the categories for a
-- given page), which hardcodes "in" for non-cities and "of" for cities. (FIXME: Change this.)
generic_before_non_cities = "in",
generic_before_cities = "of",
preposition = "of",
has_neighborhoods = true, --?
class = "non-admin settlement", --?
cat_handler = district_neighborhood_cat_handler,
},
["suburban area"] = {
link = "w",
fallback = "suburb",
},
["subway station"] = {
link = "w",
fallback = "metro station",
},
["sum"] = {
-- In China, Mongolia, Russia; something like a county in Mongolia but a township in China (Inner Mongolia),
-- and equivalent to a [[selsoviet]] in the parts of Russia where it's in use (a rural council, below a raion).
link = "+w:sum (administrative division)",
-- This fallback is somewha arbitrary. We could use "county" but that has a display handler
-- which we don't want to be active (FIXME: If the display handler would be active, that's a bug).
fallback = "division",
},
["supercontinent"] = {
link = true,
fallback = "continent",
},
["tehsil"] = {
link = true,
affix_type = "suf",
no_affix_strings = {"tehsil", "tahsil"},
class = "subpolity",
},
["temple"] = {
link = true,
fallback = "building",
},
["territorial authority"] = {
link = "w",
fallback = "district",
},
["territory"] = {
link = true,
preposition = "of",
class = "subpolity",
},
["theme"] = {
link = "+w:theme (Byzantine district)",
preposition = "of",
class = "subpolity",
},
["town"] = {
link = true,
generic_before_non_cities = "in",
has_neighborhoods = true,
class = "settlement",
cat_handler = city_type_cat_handler,
default = {true},
},
["town with bystatus"] = {
-- can't use templates in links currently
link = "[[town]] with [[bystatus#Norwegian Bokmål|bystatus]]",
plural = "towns with bystatus",
plural_link = "[[town]]s with [[bystatus#Norwegian Bokmål|bystatus]]",
fallback = "town",
},
["township"] = {
link = true,
has_neighborhoods = true,
class = "settlement", --?
default = {true},
},
["township municipality"] = {
-- Quebec
link = "+w:township municipality (Quebec)",
preposition = "of",
fallback = "municipality",
has_neighborhoods = true, --?
},
["traditional county"] = {
link = true,
fallback = "county",
},
["traditional region"] = {
-- FIXME: Verify this works. Same for 'historic(al) region'.
-- provided only for the link
link = "w",
fallback = "FORMER geographic region",
},
["trail"] = {
link = true,
fallback = "road",
},
["treaty port"] = {
link = "w",
fallback = "city",
class = "settlement",
inherently_former = {"FORMER"},
},
["tributary"] = {
link = true,
preposition = "of",
fallback = "river",
},
["underground station"] = {
link = "w",
fallback = "metro station",
},
["unincorporated area"] = {
link = "w",
-- I don't know if this fallback makes sense everywhere.
fallback = "unincorporated community",
},
["unincorporated community"] = {
link = true,
generic_before_non_cities = "in",
class = "non-admin settlement",
},
["unincorporated territory"] = {
link = "w",
fallback = "territory",
},
["union territory"] = {
-- India
link = true,
preposition = "of",
entry_placetype_indefinite_article = "a",
class = "subpolity",
},
["unitary authority"] = {
-- UK, New Zealand
link = true,
entry_placetype_indefinite_article = "a",
fallback = "local government district",
},
["unitary district"] = {
link = "w",
entry_placetype_indefinite_article = "a",
fallback = "local government district",
},
["united township municipality"] = {
-- Quebec
link = "+w:united township municipality (Quebec)",
entry_placetype_indefinite_article = "a",
fallback = "township municipality",
has_neighborhoods = true, --?
},
["university"] = {
link = true,
entry_placetype_indefinite_article = "a",
class = "man-made structure",
default = {true},
},
["unrecognised country"] = {
link = "w",
fallback = "unrecognized country",
},
["unrecognized and nearly unrecognized countries!"] = {
category_link = "[[de facto]] [[independent]] [[state]]s with little or no {{w|international recognition}}",
bare_category_parent = "country-like entities",
},
["unrecognized country"] = {
link = "w",
class = "polity",
default = {"Unrecognized and nearly unrecognized countries"},
},
["unrecognised state"] = {
link = "w",
fallback = "unrecognized country",
},
["unrecognized state"] = {
link = "w",
fallback = "unrecognized country",
},
["urban area"] = {
link = "separately",
fallback = "neighborhood",
},
["urban hromada"] = {
link = "[[urban]] [[w:hromada|hromada]]",
affix_type = "suf",
fallback = "hromada",
},
["urban service area"] = {
-- A strange beast existing in Alberta; technically a type of hamlet but in practice used for much larger
-- cities and treated equivalent to a city. (There are only two of them, [[Fort McMurray]] and [[Sherwood Park]]).
link = "w",
fallback = "city",
},
["urban township"] = {
link = "w",
fallback = "township",
},
["urban-type settlement"] = {
-- appears to be a particular type of small urban settlement in post-Soviet states,
-- had an administrative function.
link = "w",
fallback = "town",
},
["valley"] = {
link = true,
class = "natural feature",
addl_bare_category_parents = {"landforms", "water"},
default = {true},
},
["viceroyalty"] = {
-- in essence, a type of colony
link = true,
fallback = "dependent territory",
},
["village"] = {
link = true,
generic_before_non_cities = "in",
category_link = "[[village]]s, [[hamlet]]s, and other small [[community|communities]] and [[settlement]]s",
class = "settlement",
cat_handler = city_type_cat_handler,
default = {true},
},
["village development committee"] = {
-- former administrative structure in Nepal; also exists in India but not as a formal unit
link = "+w:village development committee (Nepal)",
inherently_former = {"FORMER"},
fallback = "village",
},
["village municipality"] = {
-- Quebec
link = "+w:village municipality (Quebec)",
preposition = "of",
fallback = "municipality",
has_neighborhoods = true, --?
},
["voivodeship"] = {
-- Poland
link = true,
display_handler = voivodeship_display_handler,
preposition = "of",
class = "subpolity",
},
["volcano"] = {
link = true,
plural = "volcanoes",
class = "natural feature",
addl_bare_category_parents = {"landforms"},
default = {true, "Mountains"},
},
["ward"] = {
link = true,
class = "settlement",
-- Wards are formal administrative divisions of a city but have some properties of neighborhoods.
fallback = "neighborhood",
},
["watercourse"] = {
link = true,
fallback = "channel",
},
["Welsh community"] = {
-- Wales
link = "[[w:community (Wales)|community]]",
preposition = "of",
affix_type = "suf",
affix = "community",
has_neighborhoods = true,
class = "settlement",
},
["zone"] = {
-- administrative division of Ethiopia, Qatar, Nepal, India
link = "+w:zone#Place names",
preposition = "of",
class = "subpolity",
},
----------------------------------------------------------------------------------------------
-- Categories for former places --
----------------------------------------------------------------------------------------------
["ANCIENT capital"] = {
link = false,
entry_placetype_use_the = true,
preposition = "of",
has_neighborhoods = true,
class = "capital",
-- FIXME: Consider removing 'ancient settlements' here. Ancient capitals, like former capitals, often still
-- exist but just aren't the capital any more. Maybe we should have an 'Ancient capitals' category.
default = {"Ancient settlements", "Former capitals"},
},
["ANCIENT non-admin settlement"] = {
link = false,
class = "non-admin settlement",
fallback = "ANCIENT settlement",
},
["ANCIENT settlement"] = {
link = false,
has_neighborhoods = true,
class = "settlement",
default = {"Ancient settlements"},
},
["ancient settlements!"] = {
category_link = "former [[city|cities]], [[town]]s and [[village]]s that existed in [[antiquity]]",
bare_category_parent = "former settlements",
},
["FORMER capital"] = {
link = false,
entry_placetype_use_the = true,
preposition = "of",
has_neighborhoods = true,
class = "capital",
default = {"Former capitals"},
},
["former capitals!"] = {
category_link = "former [[capital]] [[city|cities]] and [[town]]s",
bare_category_parent = "settlements",
},
["former counties and county-level cities!"] = {
-- For categorizing former counties and county-level cities of China
category_link = "no-longer existing [[county|counties]] and [[county-level city|county-level cities]]",
bare_category_breadcrumb = "counties and county-level cities",
bare_category_parent = "former political divisions",
},
["FORMER county"] = {
-- For categorizing former counties and county-level cities of China
link = false,
fallback = "FORMER subpolity",
},
["FORMER county-level city"] = {
-- For categorizing former counties and county-level cities of China
link = false,
fallback = "FORMER subpolity",
},
["former countries and country-like entities!"] = {
category_link = "[[country|countries]] and similar [[polity|polities]] that no longer exist",
bare_category_breadcrumb = "countries and country-like entities",
bare_category_parent = "former polities",
},
["FORMER country"] = {
link = false,
class = "polity",
default = {"Former countries and country-like entities"},
},
["former dependent territories!"] = {
category_link = "[[w:dependent territory|dependent territories]] (colonies, dependencies, protectorates, etc.) that no longer exist",
bare_category_breadcrumb = "dependent territories",
bare_category_parent = "former political divisions",
},
["FORMER dependent territory"] = {
link = false,
preposition = "of",
class = "subpolity",
default = {"Former dependent territories"},
},
["former districts!"] = {
-- For categorizing former districts of China
category_link = "no-longer-existing [[district]]s",
bare_category_breadcrumb = "districts",
bare_category_parent = "former political divisions",
},
["FORMER district"] = {
-- For categorizing former districts of China
link = false,
fallback = "FORMER subpolity",
},
["FORMER geographic region"] = {
link = false,
fallback = "geographic and cultural area",
},
["FORMER man-made structure"] = {
link = false,
class = "man-made structure",
default = {"Former man-made structures"},
},
["former man-made structures!"] = {
category_link = "man-made structures such as [[airport]]s and [[park]]s that no longer exist",
bare_category_breadcrumb = "man-made structures",
bare_category_parent = "former places",
},
["former municipalities!"] = {
-- For categorizing former municipalities of the Netherlands
category_link = "no-longer-existing [[municipality|municipalities]]",
bare_category_breadcrumb = "municipalities",
bare_category_parent = "former political divisions",
},
["FORMER municipality"] = {
-- For categorizing former municipalities of the Netherlands
link = false,
fallback = "FORMER subpolity",
},
["FORMER natural feature"] = {
link = false,
class = "natural feature",
default = {"Former natural features"},
},
["former natural features!"] = {
category_link = "natural features such as [[lake]]s, [[river]]s and [[island]]s that no longer exist",
bare_category_breadcrumb = "natural features",
bare_category_parent = "former places",
},
["FORMER non-admin settlement"] = {
link = false,
class = "non-admin settlement",
fallback = "FORMER settlement",
},
["former places!"] = {
category_link = "[[place]]s of all sorts that no longer exist",
bare_category_breadcrumb = "former",
bare_category_parent = "places",
},
["former political divisions!"] = {
category_link = "[[political]] [[division]]s (states, provinces, counties, etc.) that no longer exist",
bare_category_breadcrumb = "political divisions",
bare_category_parent = "former places",
},
["former polities!"] = {
category_link = "[[polity|polities]] (countries, kingdoms, empires, etc.) that no longer exist",
bare_category_breadcrumb = "polities",
bare_category_parent = "former places",
},
["FORMER polity"] = {
link = false,
class = "polity",
default = {"Former polities"},
},
["former prefectures!"] = {
-- For categorizing former prefectures of China
category_link = "no-longer-existing [[prefecture]]s",
bare_category_breadcrumb = "prefectures",
bare_category_parent = "former political divisions",
},
["FORMER prefecture"] = {
-- For categorizing former prefectures of China
link = false,
fallback = "FORMER subpolity",
},
["former provinces!"] = {
-- For categorizing former provinces of China, etc.
category_link = "no-longer-existing [[province]]s",
bare_category_breadcrumb = "provinces",
bare_category_parent = "former political divisions",
},
["FORMER province"] = {
-- For categorizing ancient/historical/former provinces of the Roman Empire
link = false,
fallback = "FORMER subpolity",
},
["former region"] = {
-- A former region is considered a former political division, but not a 'historical/traditional/etc.' region.
link = "separately",
preposition = "of",
inherently_former = {"FORMER"},
class = "subpolity",
},
["FORMER settlement"] = {
link = false,
has_neighborhoods = true,
class = "settlement",
default = {"Former settlements"},
},
["former settlements!"] = {
category_link = "[[city|cities]], [[town]]s and [[village]]s that no longer exist or have been merged or reclassified",
bare_category_breadcrumb = "settlements",
bare_category_parent = "former political divisions",
},
["FORMER subpolity"] = {
link = false,
preposition = "of",
class = "subpolity",
default = {"Former political divisions"},
},
----------------------------------------------------------------------------------------------
-- form-of categories --
----------------------------------------------------------------------------------------------
---------- Abbreviations ----------
["abbreviations of counties!"] = {
-- For categorizing abbreviations of counties of e.g. England
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[county|counties]]",
bare_category_breadcrumb = "counties",
bare_category_parent = "abbreviations of political divisions",
},
["abbreviations of countries!"] = {
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[country|countries]]",
bare_category_breadcrumb = "countries",
bare_category_parent = "abbreviations of places",
},
["abbreviations of departments!"] = {
-- For categorizing abbreviations of departments of e.g. France
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[department]]s",
bare_category_breadcrumb = "departments",
bare_category_parent = "abbreviations of political divisions",
},
["abbreviations of districts!"] = {
-- For categorizing abbreviations of districts of e.g. ???
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[district]]s",
bare_category_breadcrumb = "districts",
bare_category_parent = "abbreviations of political divisions",
},
["abbreviations of divisions!"] = {
-- For categorizing abbreviations of divisions of e.g. Bangladesh
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[division]]s",
bare_category_breadcrumb = "divisions",
bare_category_parent = "abbreviations of political divisions",
},
["abbreviations of former countries!"] = {
full_category_link = "{{glossary|abbreviation}}s of [[country|countries]] that no longer [[exist]]",
bare_category_breadcrumb = "countries",
bare_category_parent = "abbreviations of former places",
},
["abbreviations of former places!"] = {
full_category_link = "{{glossary|abbreviation}}s of [[place]]s that no longer [[exist]]",
bare_category_breadcrumb = "abbreviations",
bare_category_parent = "former places",
addl_bare_category_parents = {{name = "abbreviations of places", sort = "former"}},
},
["abbreviations of places!"] = {
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[place]]s",
bare_category_breadcrumb = "abbreviations",
bare_category_parent = "places",
},
["abbreviations of political divisions!"] = {
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[political]] [[division]]s",
bare_category_breadcrumb = "political divisions",
bare_category_parent = "abbreviations of places",
},
["abbreviations of prefectures!"] = {
-- For categorizing abbreviations of prefectures of e.g. Japan
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[prefecture]]s",
bare_category_breadcrumb = "prefectures",
bare_category_parent = "abbreviations of political divisions",
},
["abbreviations of provinces!"] = {
-- For categorizing abbreviations of provinces of e.g. Canada
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[province]]s",
bare_category_breadcrumb = "provinces",
bare_category_parent = "abbreviations of political divisions",
},
["abbreviations of provinces and territories!"] = {
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[province]]s and [[territory|territories]]",
bare_category_breadcrumb = "provinces and territories",
bare_category_parent = "abbreviations of political divisions",
},
["abbreviations of regions!"] = {
-- For categorizing abbreviations of regions of e.g. Italy
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[administrative region]]s",
bare_category_breadcrumb = "regions",
bare_category_parent = "abbreviations of political divisions",
},
["abbreviations of states!"] = {
-- For categorizing abbreviations of states of e.g. the United States
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[state]]s",
bare_category_breadcrumb = "states",
bare_category_parent = "abbreviations of political divisions",
},
["abbreviations of states and territories!"] = {
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[state]]s and [[territory|territories]]",
bare_category_breadcrumb = "states and territories",
bare_category_parent = "abbreviations of political divisions",
},
["abbreviations of states and union territories!"] = {
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[state]]s and [[union territory|union territories]]",
bare_category_breadcrumb = "states and union territories",
bare_category_parent = "abbreviations of political divisions",
},
["abbreviations of territories!"] = {
full_category_link = "{{glossary|abbreviation}}s of [[name]]s of [[territory|territories]]",
bare_category_breadcrumb = "territories",
bare_category_parent = "abbreviations of political divisions",
},
["ABBREVIATION_OF country"] = {
link = false,
default = {"Abbreviations of countries"},
},
["ABBREVIATION_OF county"] = {
link = false,
fallback = "ABBREVIATION_OF subpolity",
},
["ABBREVIATION_OF department"] = {
link = false,
fallback = "ABBREVIATION_OF subpolity",
},
["ABBREVIATION_OF district"] = {
link = false,
fallback = "ABBREVIATION_OF subpolity",
},
["ABBREVIATION_OF division"] = {
link = false,
fallback = "ABBREVIATION_OF subpolity",
},
["ABBREVIATION_OF FORMER country"] = {
link = false,
default = {"Abbreviations of former countries"},
},
["ABBREVIATION_OF FORMER place"] = {
link = false,
default = {"Abbreviations of former places"},
},
["ABBREVIATION_OF place"] = {
link = false,
default = {"Abbreviations of places"},
},
["ABBREVIATION_OF prefecture"] = {
link = false,
fallback = "ABBREVIATION_OF subpolity",
},
["ABBREVIATION_OF province"] = {
link = false,
fallback = "ABBREVIATION_OF subpolity",
},
["ABBREVIATION_OF region"] = {
link = false,
fallback = "ABBREVIATION_OF subpolity",
},
["ABBREVIATION_OF state"] = {
link = false,
fallback = "ABBREVIATION_OF subpolity",
},
["ABBREVIATION_OF subpolity"] = {
link = false,
default = {"Abbreviations of political divisions"},
},
["ABBREVIATION_OF territory"] = {
link = false,
fallback = "ABBREVIATION_OF subpolity",
},
["ABBREVIATION_OF union territory"] = {
link = false,
fallback = "ABBREVIATION_OF subpolity",
},
---------- Archaic forms ----------
["archaic forms of places!"] = {
full_category_link = "{{glossary|archaic}} [[form]]s of [[name]]s of [[place]]s",
bare_category_breadcrumb = "archaic forms",
bare_category_parent = "places",
},
["ARCHAIC_FORM_OF place"] = {
link = false,
default = {"Archaic forms of places"},
},
---------- Clippings ----------
["clippings of places!"] = {
full_category_link = "{{glossary|clipping}}s of [[name]]s of [[place]]s",
bare_category_breadcrumb = "clippings",
bare_category_parent = "places",
},
["CLIPPING_OF place"] = {
link = false,
default = {"Clippings of places"},
},
---------- Dated forms ----------
["dated forms of places!"] = {
full_category_link = "{{glossary|dated}} [[form]]s of [[name]]s of [[place]]s",
bare_category_breadcrumb = "dated forms",
bare_category_parent = "places",
},
["DATED_FORM_OF place"] = {
link = false,
default = {"Dated forms of places"},
},
---------- Derogatory names ----------
["derogatory names for cities!"] = {
full_category_link = "{{glossary|derogatory}} [[name]]s for [[city|cities]]",
bare_category_breadcrumb = "cities",
bare_category_parent = "derogatory names for places",
addl_bare_category_parents = {"nicknames for cities"},
},
["derogatory names for continents!"] = {
full_category_link = "{{glossary|derogatory}} [[name]]s for [[continent]]s",
bare_category_breadcrumb = "continents",
bare_category_parent = "derogatory names for places",
addl_bare_category_parents = {"nicknames for continents"},
},
["derogatory names for countries!"] = {
full_category_link = "{{glossary|derogatory}} [[name]]s for [[country|countries]]",
bare_category_breadcrumb = "countries",
bare_category_parent = "derogatory names for places",
addl_bare_category_parents = {"nicknames for countries"},
},
["derogatory names for places!"] = {
full_category_link = "{{glossary|derogatory}} [[name]]s for [[place]]s",
bare_category_breadcrumb = "derogatory names",
bare_category_parent = "nicknames for places",
},
["derogatory names for states!"] = {
full_category_link = "{{glossary|derogatory}} [[name]]s for [[state]]s",
bare_category_breadcrumb = "states",
bare_category_parent = "derogatory names for places",
addl_bare_category_parents = {"nicknames for states"},
},
["DEROGATORY_NAME_FOR capital"] = {
link = false,
default = {"Derogatory names for cities"},
},
["DEROGATORY_NAME_FOR city"] = {
link = false,
default = {"Derogatory names for cities"},
},
["DEROGATORY_NAME_FOR continent"] = {
link = false,
default = {"Derogatory names for continents"},
},
["DEROGATORY_NAME_FOR country"] = {
link = false,
default = {"Derogatory names for countries"},
},
["DEROGATORY_NAME_FOR metropolitan city"] = {
-- "metropolitan city" doesn't fall back to "city"
link = false,
default = {"Derogatory names for cities"},
},
["DEROGATORY_NAME_FOR place"] = {
link = false,
default = {"Derogatory names for places"},
},
["DEROGATORY_NAME_FOR prefecture-level city"] = {
-- "prefecture-level city" doesn't fall back to "city" but things like "county-level city" and
-- "subprovincial city" fall back to "prefecture-level city"
link = false,
default = {"Derogatory names for cities"},
},
["DEROGATORY_NAME_FOR state"] = {
link = false,
default = {"Derogatory names for states"},
},
["DEROGATORY_NAME_FOR town"] = {
link = false,
default = {"Derogatory names for cities"},
},
---------- Ellipses ----------
["ellipses of places!"] = {
full_category_link = "{{glossary|ellipsis|ellipses}} of [[name]]s of [[place]]s",
bare_category_breadcrumb = "ellipses",
bare_category_parent = "places",
},
["ELLIPSIS_OF place"] = {
link = false,
default = {"Ellipses of places"},
},
---------- Former long-form names ----------
["former long-form names of countries!"] = {
full_category_link = "no-longer-[[use]]d [[long]]-[[form]] (but typically [[unofficial]]) [[name]]s of [[country|countries]]",
bare_category_breadcrumb = "countries",
bare_category_parent = "former long-form names of places",
addl_bare_category_parents = {{name = "former names of countries", sort = "long-form"}},
},
["former long-form names of places!"] = {
full_category_link = "no-longer-[[use]]d [[long]]-[[form]] (but typically [[unofficial]]) [[name]]s of [[place]]s",
bare_category_breadcrumb = "long-form",
bare_category_parent = "former names of places",
},
["FORMER_LONG_FORM_OF country"] = {
link = false,
default = {"Former long-form names of countries"},
},
["FORMER_LONG_FORM_OF place"] = {
link = false,
default = {"Former long-form names of places"},
},
---------- Former names ----------
["former names of capitals!"] = {
full_category_link = "[[former]] [[name]]s of [[capital city|capital cities]] that generally still exist but under a different name",
bare_category_breadcrumb = "capitals",
bare_category_parent = "former names of settlements",
},
["former names of countries!"] = {
full_category_link = "[[former]] [[name]]s of [[country|countries]] that generally still exist but under a different name",
bare_category_breadcrumb = "countries",
bare_category_parent = "former names of places",
},
["former names of places!"] = {
full_category_link = "[[former]] [[name]]s of [[place]]s that generally still exist but under a different name",
bare_category_breadcrumb = "former names",
bare_category_parent = "places",
},
["former names of political divisions!"] = {
full_category_link = "[[former]] [[name]]s of [[political]] [[division]]s (states, provinces, counties, etc.) that generally still exist but under a different name",
bare_category_breadcrumb = "political divisions",
bare_category_parent = "former names of places",
},
["former names of polities!"] = {
full_category_link = "[[former]] [[name]]s of [[polity|polities]] (e.g. [[country|countries]]) that generally still exist but under a different name",
bare_category_breadcrumb = "polities",
bare_category_parent = "former names of places",
},
["former names of settlements!"] = {
full_category_link = "[[former]] [[name]]s of [[city|cities]], [[town]]s, [[village]]s, etc. that generally still exist but under a different name",
bare_category_breadcrumb = "settlements",
bare_category_parent = "former names of political divisions",
},
["FORMER_NAME_OF capital"] = {
link = false,
default = {"Former names of capitals"},
},
["FORMER_NAME_OF country"] = {
link = false,
default = {"Former names of countries"},
},
["FORMER_NAME_OF place"] = {
link = false,
default = {"Former names of places"},
},
["FORMER_NAME_OF polity"] = {
link = false,
default = {"Former names of polities"},
},
["FORMER_NAME_OF region"] = {
link = false,
fallback = "FORMER_NAME_OF subpolity",
},
["FORMER_NAME_OF settlement"] = {
link = false,
default = {"Former names of settlements"},
},
["FORMER_NAME_OF subpolity"] = {
link = false,
default = {"Former names of political divisions"},
},
---------- Former nicknames ----------
["former nicknames for cities!"] = {
full_category_link = "no-longer-used [[nickname]]s for [[city|cities]], e.g. the [[Eternal City]] for [[Kyoto]] during the {{w|Heian period}} ({{circa2|800–1100|short=yes}} {{AD}})",
bare_category_breadcrumb = "cities",
bare_category_parent = "former nicknames for places",
addl_bare_category_parents = {"nicknames for cities"},
},
["former nicknames for places!"] = {
full_category_link = "no-longer-used [[nickname]]s for [[place]]s",
bare_category_breadcrumb = "former",
bare_category_parent = "nicknames for places",
addl_bare_category_parents = {{name = "former names of places", sort = "nicknames"}},
},
["FORMER_NICKNAME_FOR capital"] = {
link = false,
default = {"Former nicknames for cities"},
},
["FORMER_NICKNAME_FOR city"] = {
link = false,
default = {"Former nicknames for cities"},
},
["FORMER_NICKNAME_FOR metropolitan city"] = {
-- "metropolitan city" doesn't fall back to "city"
link = false,
default = {"Former nicknames for cities"},
},
["FORMER_NICKNAME_FOR place"] = {
link = false,
default = {"Former nicknames for places"},
},
["FORMER_NICKNAME_FOR prefecture-level city"] = {
-- "prefecture-level city" doesn't fall back to "city" but things like "county-level city" and
-- "subprovincial city" fall back to "prefecture-level city"
link = false,
default = {"Former nicknames for cities"},
},
["FORMER_NICKNAME_FOR town"] = {
link = false,
default = {"Former nicknames for cities"},
},
---------- Former official names ----------
["former official names of countries!"] = {
full_category_link = "no-longer-[[use]]d [[official]] [[name]]s of [[country|countries]]",
bare_category_breadcrumb = "countries",
bare_category_parent = "former official names of places",
addl_bare_category_parents = {{name = "former names of countries", sort = "official"}},
},
["former official names of places!"] = {
full_category_link = "no-longer-[[use]]d [[official]] [[name]]s of [[place]]s",
bare_category_breadcrumb = "official",
bare_category_parent = "former names of places",
},
["FORMER_OFFICIAL_NAME_OF country"] = {
link = false,
default = {"Former official names of countries"},
},
["FORMER_OFFICIAL_NAME_OF place"] = {
link = false,
default = {"Former official names of places"},
},
---------- Long-form names ----------
["long-form names of countries!"] = {
full_category_link = "[[long]]-[[form]] (but typically [[unofficial]]) [[name]]s of [[country|countries]]",
bare_category_breadcrumb = "countries",
bare_category_parent = "long-form names of places",
},
["long-form names of places!"] = {
full_category_link = "[[long]]-[[form]] (but typically [[unofficial]]) [[name]]s of [[place]]s",
bare_category_breadcrumb = "long-form names",
bare_category_parent = "places",
},
["LONG_FORM_OF country"] = {
link = false,
default = {"Long-form names of countries"},
},
["LONG_FORM_OF place"] = {
link = false,
default = {"Long-form names of places"},
},
---------- Nicknames ----------
["nicknames for cities!"] = {
full_category_link = "[[nickname]]s for [[city|cities]], e.g. the [[Big Apple]] for [[New York City]]",
bare_category_breadcrumb = "cities",
bare_category_parent = "nicknames for places",
addl_bare_category_parents = {"cities"},
},
["nicknames for continents!"] = {
full_category_link = "[[nickname]]s for [[continent]]s",
bare_category_breadcrumb = "continents",
bare_category_parent = "nicknames for places",
addl_bare_category_parents = {"continents"},
},
["nicknames for countries!"] = {
full_category_link = "[[nickname]]s for [[country|countries]]",
bare_category_breadcrumb = "countries",
bare_category_parent = "nicknames for places",
addl_bare_category_parents = {"countries"},
},
["nicknames for places!"] = {
full_category_link = "[[nickname]]s for [[place]]s",
bare_category_breadcrumb = "places",
bare_category_parent = "nicknames",
addl_bare_category_parents = {"places"},
},
["nicknames for states!"] = {
-- For categorizing nicknames for states of e.g. the United States
full_category_link = "[[nicknames]] for [[state]]s",
bare_category_breadcrumb = "states",
bare_category_parent = "nicknames for places",
addl_bare_category_parents = {"states"},
},
["NICKNAME_FOR capital"] = {
link = false,
default = {"Nicknames for cities"},
},
["NICKNAME_FOR city"] = {
link = false,
default = {"Nicknames for cities"},
},
["NICKNAME_FOR continent"] = {
link = false,
default = {"Nicknames for continents"},
},
["NICKNAME_FOR country"] = {
link = false,
default = {"Nicknames for countries"},
},
["NICKNAME_FOR metropolitan city"] = {
-- "metropolitan city" doesn't fall back to "city"
link = false,
default = {"Nicknames for cities"},
},
["NICKNAME_FOR place"] = {
link = false,
default = {"Nicknames for places"},
},
["NICKNAME_FOR prefecture-level city"] = {
-- "prefecture-level city" doesn't fall back to "city" but things like "county-level city" and
-- "subprovincial city" fall back to "prefecture-level city"
link = false,
default = {"Nicknames for cities"},
},
["NICKNAME_FOR state"] = {
link = false,
default = {"Nicknames for states"},
},
["NICKNAME_FOR town"] = {
link = false,
default = {"Nicknames for cities"},
},
---------- Obsolete forms ----------
["obsolete forms of places!"] = {
full_category_link = "{{glossary|obsolete}} [[form]]s of [[name]]s of [[place]]s",
bare_category_breadcrumb = "obsolete forms",
bare_category_parent = "places",
},
["OBSOLETE_FORM_OF place"] = {
link = false,
default = {"Obsolete forms of places"},
},
---------- Official names ----------
["official names of countries!"] = {
full_category_link = "[[official]] [[name]]s of [[country|countries]]",
bare_category_breadcrumb = "countries",
bare_category_parent = "official names of places",
},
["official names of former countries!"] = {
full_category_link = "[[official]] [[name]]s of [[country|countries]] that no longer [[exist]]",
bare_category_breadcrumb = "countries",
bare_category_parent = "official names of former places",
},
["official names of former places!"] = {
full_category_link = "[[official]] [[name]]s of [[place]]s that no longer [[exist]]",
bare_category_breadcrumb = "official names",
bare_category_parent = "former places",
addl_bare_category_parents = {{name = "official names of places", sort = "former"}},
},
["official names of places!"] = {
full_category_link = "[[official]] [[name]]s of [[place]]s",
bare_category_breadcrumb = "official names",
bare_category_parent = "places",
},
["OFFICIAL_NAME_OF country"] = {
link = false,
default = {"Official names of countries"},
},
["OFFICIAL_NAME_OF FORMER country"] = {
link = false,
default = {"Official names of former countries"},
},
["OFFICIAL_NAME_OF FORMER place"] = {
link = false,
default = {"Official names of former places"},
},
["OFFICIAL_NAME_OF place"] = {
link = false,
default = {"Official names of places"},
},
---------- Official nicknames ----------
["official nicknames for places!"] = {
full_category_link = "[[official]] [[nickname]]s for [[place]]s",
bare_category_breadcrumb = "official",
bare_category_parent = "nicknames for places",
},
["official nicknames for states!"] = {
-- For categorizing official nicknames for states of e.g. the United States
full_category_link = "[[official]] [[nicknames]] for [[state]]s",
bare_category_breadcrumb = "official",
bare_category_parent = "nicknames for states",
addl_bare_category_parents = {"states"},
},
["OFFICIAL_NICKNAME_FOR place"] = {
link = false,
default = {"Official nicknames for places"},
},
["OFFICIAL_NICKNAME_FOR state"] = {
link = false,
default = {"Official nicknames for states"},
},
}
export.plural_placetype_to_singular = {}
for sg_placetype, spec in pairs(export.placetype_data) do
if spec.plural then
export.plural_placetype_to_singular[spec.plural] = sg_placetype
end
end
return export
0i5icp7fj8km7659tytty7f31iboenv
Mòideal:category tree/topic/Nature
828
17898
88794
2026-08-02T00:35:54Z
en>Fenakhay
0
88794
Scribunto
text/plain
local labels = {}
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
labels["nature"] = {
type = "related-to",
parents = {"all topics"},
}
labels["acids"] = {
type = "set",
parents = {"matter"},
}
labels["actinide series chemical elements"] = {
breadcrumb = "actinide series",
type = "set",
description = "{{{langname}}} terms for those chemical elements in the {{w|f-block}} of the [[periodic table]] with [[atomic number]]s from 89 to 103.",
parents = {"chemical elements", "metals", "radioactivity"},
}
labels["air"] = {
type = "related-to",
parents = {"atmosphere"},
}
labels["alkali metals"] = {
type = "set",
description = "{{{langname}}} terms for [[alkali metal]]s, chemical elements in [[w:Group (periodic table)|group]] 1 of the [[periodic table]], which all have one [[valence electron]].",
parents = {"chemical elements", "metals"},
}
labels["alkaline earth metals"] = {
type = "set",
description = "{{{langname}}} terms for [[alkaline earth metal]]s, chemical elements in [[w:Group (periodic table)|group]] 2, which all have two [[valence electron]]s.",
parents = {"chemical elements", "metals"},
}
labels["alkaloids"] = {
type = "set",
parents = {"organic compounds"},
}
labels["alloys"] = {
type = "set",
parents = {"metals"},
}
labels["aluminium"] = {
type = "related-to",
parents = {"boron group elements"},
}
labels["amino acids"] = {
type = "set",
parents = {"carboxylic acids"},
}
labels["animal sounds"] = {
type = "set",
parents = {"sounds", "vocalizations"},
}
labels["animal welfare"] = {
type = "related-to",
description = "{{{langname}}} terms closely associated with [[animal welfare]].",
parents = {"ethics"},
}
labels["antimatter"] = {
type = "related-to",
parents = {"matter"},
}
labels["antimony"] = {
type = "related-to",
parents = {"pnictogens"},
}
labels["argon"] = {
type = "related-to",
parents = {"noble gases"},
}
labels["arsenic"] = {
type = "related-to",
parents = {"pnictogens"},
}
labels["astatine"] = {
type = "related-to",
parents = {"halogens"},
}
labels["asteroids"] = {
type = "set",
parents = {"celestial bodies"},
}
labels["atmosphere"] = {
type = "related-to",
parents = {"nature"},
}
labels["atmospheric phenomena"] = {
type = "set",
parents = {"atmosphere"},
}
labels["autumn"] = {
type = "related-to",
parents = {"seasons"},
}
labels["barium"] = {
type = "related-to",
parents = {"alkaline earth metals"},
}
labels["baryons"] = {
type = "set",
parents = {"hadrons"},
}
labels["beryllium"] = {
type = "related-to",
parents = {"alkaline earth metals"},
}
labels["birth"] = {
type = "related-to",
parents = {"reproduction"},
}
labels["bismuth"] = {
type = "related-to",
parents = {"pnictogens"},
}
labels["boron"] = {
type = "related-to",
parents = {"boron group elements"},
}
labels["boron group elements"] = {
breadcrumb = "boron group",
type = "set",
description = "{{{langname}}} terms for chemical elements in [[w:Group (periodic table)|group]] 13 of the [[periodic table]], which all have three [[valence electron]]s.",
parents = {"chemical elements"},
}
labels["bosons"] = {
type = "set",
parents = {"subatomic particles"},
}
labels["bromine"] = {
type = "related-to",
parents = {"halogens"},
}
labels["cadmium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["calcium"] = {
type = "related-to",
parents = {"alkaline earth metals"},
}
labels["carbohydrates"] = {
type = "set",
parents = {"organic compounds"},
}
labels["carbon"] = {
type = "related-to",
parents = {"carbon group elements"},
}
labels["carbon group elements"] = {
breadcrumb = "carbon group",
type = "set",
description = "{{{langname}}} terms for chemical elements in [[w:Group (periodic table)|group]] 14 of the [[periodic table]], which all have four [[valence electron]]s.",
parents = {"chemical elements"},
}
labels["carboxylic acids"] = {
type = "set",
parents = {"acids", "organic compounds"},
}
labels["celestial bodies"] = {
type = "set",
description = "{{{langname}}} terms for varous [[celestial body|celestial bodies]]; things found in outer space.",
parents = {"space"},
}
labels["cerium"] = {
type = "related-to",
parents = {"lanthanide series chemical elements"},
}
labels["cesium"] = {
type = "related-to",
parents = {"alkali metals"},
}
labels["chalcogens"] = {
type = "set",
description = "{{{langname}}} terms for chemical elements in [[w:Group (periodic table)|group]] 16 of the [[periodic table]], which all have 6 [[valence electron]]s.",
parents = {"chemical elements"},
}
labels["chemical elements"] = {
type = "set",
parents = {"matter"},
}
for period = 1, 7 do
local key = ("Period %d elements"):format(period)
if not labels[key] then
labels[key] = {
type = "set",
description = ("{{{langname}}} terms for [[chemical element]]s in period %d of the [[periodic table]]."):format(period),
parents = {"chemical elements"},
}
end
end
for group = 1, 12 do
local key = ("Group %d elements"):format(group)
if not labels[key] then
labels[key] = {
type = "set",
description = ("{{{langname}}} terms for [[chemical element]]s in [[w:Group (periodic table)|group]] %d of the [[periodic table]]."):format(group),
parents = {"chemical elements"},
}
end
end
for _, block in ipairs({"s", "p", "d", "f"}) do
local key = block:upper() .. "-block elements"
if not labels[key] then
labels[key] = {
type = "set",
description = ("{{{langname}}} terms for [[chemical element]]s in the %s-block of the [[periodic table]]."):format(block),
parents = {"chemical elements"},
}
end
end
if not labels["Nonmetals"] then
labels["Nonmetals"] = {
type = "set",
description = "{{{langname}}} terms for [[nonmetal]] [[chemical element]]s.",
parents = {"chemical elements"},
}
end
if not labels["Post-transition metals"] then
labels["Post-transition metals"] = {
type = "set",
description = "{{{langname}}} terms for [[post-transition metal]] [[chemical element]]s.",
parents = {"chemical elements", "metals"},
}
end
labels["chemical isomers"] = {
type = "related-to",
parents = {"matter", "physical chemistry", "shapes"},
}
labels["chemical processes"] = {
type = "set",
description = "=[[chemical]] [[process]]es",
parents = {"nature"},
}
labels["chlorine"] = {
type = "related-to",
parents = {"halogens"},
}
labels["chromium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["classical planets"] = {
type = "name",
description = "{{{langname}}} names for the [[classical planet]]s of our Solar System.",
parents = {"celestial bodies"},
}
labels["climate change"] = {
type = "related-to",
description = "=[[anthropogenic]] [[climate change]]",
parents = {"nature"},
}
labels["clouds"] = {
type = "set",
parents = {"atmospheric phenomena"},
}
labels["coal"] = {
type = "related-to",
parents = {"fossil fuels"},
}
labels["cobalt"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["coenzymes"] = {
type = "set",
parents = {"enzymes"},
}
labels["colors"] = {
type = "set",
parents = {"light", "vision"},
}
for _, color_etc in ipairs {
{"black"},
{"blue"},
{"brown"},
{"green"},
{"grey", "[[grey]] ([[gray]])"},
{"orange"},
{"pink"},
{"purple"},
{"red"},
{"violet"},
{"white"},
{"yellow"},
} do
local color, desc = unpack(color_etc)
desc = desc or ("[[%s]]"):format(color)
labels[color .. "s"] = {
type = "set",
description = ("=shades of the [[color]] %s"):format(desc),
parents = {"colors"},
}
end
labels["colors of the rainbow"] = {
type = "set",
description = "=[[color]]s of the [[rainbow]]",
parents = {"colors"},
}
labels["combustion"] = {
type = "related-to",
parents = {"chemical processes"},
}
labels["compass points"] = {
type = "set",
parents = {"directions", "navigation"},
}
labels["copper"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["crystal"] = {
type = "related-to",
parents = {"matter", "physical chemistry"},
}
labels["darkness"] = {
type = "related-to",
parents = {"light"},
}
labels["deuterium"] = {
type = "related-to",
parents = {"hydrogen", "two", "isotopes"}
}
labels["directions"] = {
type = "set",
parents = {"nature"},
}
labels["distance"] = {
type = "related-to",
parents = {"nature"},
}
labels["drugs"] = {
type = "set",
parents = {"matter", "pharmacology"},
}
labels["dryness"] = {
type = "related-to",
parents = {"liquids"},
}
labels["dwarf planets of the Solar System"] = {
type = "name",
description = "=[[dwarf planet]]s of our [[Solar System]]",
parents = {"celestial bodies"},
}
labels["dyes"] = {
type = "set",
parents = {"matter", "pigments"},
}
labels["energy"] = {
type = "related-to",
parents = {"nature"},
}
labels["enzymes"] = {
type = "set",
parents = {"proteins", "catalysis"},
}
labels["europium"] = {
type = "related-to",
parents = {"lanthanide series chemical elements"},
}
labels["explosives"] = {
type = "set",
parents = {"matter", "weapons"},
}
labels["eye colors"] = {
type = "set",
description = "=[[color]]s that are mostly or exclusively used of [[eye]]s",
parents = {"colors", "eye"},
}
labels["fatty acids"] = {
type = "set",
parents = {"carboxylic acids"},
}
labels["fermions"] = {
type = "set",
parents = {"subatomic particles"},
}
labels["fictional materials"] = {
type = "set",
parents = {"science fiction", "materials"},
}
labels["fire"] = {
type = "related-to",
parents = {"combustion", "light sources"},
wp = "Fire",
}
labels["fluorine"] = {
type = "related-to",
parents = {"halogens"},
}
labels["fog"] = {
type = "related-to",
parents = {"weather", "water"},
}
labels["fossil fuels"] = {
type = "set",
parents = {"carbon", "energy", "natural resources"},
}
labels["francium"] = {
type = "related-to",
parents = {"alkali metals"},
}
labels["gadolinium"] = {
type = "related-to",
parents = {"lanthanide series chemical elements"},
}
labels["galaxies"] = {
type = "set",
parents = {"celestial bodies"},
}
labels["gallium"] = {
type = "related-to",
parents = {"boron group elements"},
}
labels["gases"] = {
type = "set",
parents = {"matter"},
}
labels["germanium"] = {
type = "related-to",
parents = {"carbon group elements"},
}
labels["gold"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["hadrons"] = {
type = "set",
parents = {"subatomic particles"},
}
labels["hair colors"] = {
type = "set",
description = "=[[color]]s that are mostly or exclusively used of [[hair]]",
parents = {"colors", "hair"},
}
labels["halogens"] = {
type = "set",
description = "=[[chemical element]]s in [[w:Group (periodic table)|group]] 17 of the [[periodic table]], which all have 7 [[valence electron]]s",
parents = {"chemical elements"},
}
labels["helium"] = {
type = "related-to",
parents = {"noble gases"},
}
labels["heroin"] = {
type = "related-to",
parents = {"recreational drugs"},
}
labels["height"] = {
type = "related-to",
parents = {"distance"},
}
labels["horse colors"] = {
type = "set",
description = "=[[color]]s that are mostly or exclusively used of [[horse]]s",
parents = {"colors", "horses"},
}
labels["hydrogen"] = {
type = "related-to",
parents = {"chemical elements"},
}
labels["hypothetical chemical elements"] = {
breadcrumb = "hypothetical",
type = "set",
description = "{{{langname}}} terms for types or instances of [[chemical element]]s which have been hypothesized to exist, but not (yet) discovered.",
parents = {"chemical elements", "fictional materials"},
}
labels["ice"] = {
type = "related-to",
parents = {"water"},
}
labels["indium"] = {
type = "related-to",
parents = {"boron group elements"},
}
labels["inorganic compounds"] = {
type = "set",
parents = {"matter"},
}
labels["iodine"] = {
type = "related-to",
parents = {"halogens"},
}
labels["ions"] = {
type = "set",
parents = {"matter", "chemistry", "electricity"},
}
labels["iridium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["iron"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["isotopes"] = {
type = "set",
parents = {"chemical elements"},
}
labels["krypton"] = {
type = "related-to",
parents = {"noble gases"},
}
labels["lanthanide series chemical elements"] = {
breadcrumb = "lanthanide series",
type = "set",
description = "=[[chemical element]]s in the {{w|f-block}} of the [[periodic table]] with [[atomic number]]s from 57 to 71",
parents = {"chemical elements"},
}
labels["lanthanum"] = {
type = "related-to",
parents = {"lanthanide series chemical elements"},
}
labels["lead"] = {
type = "related-to",
parents = {"carbon group elements"},
}
labels["length"] = {
type = "related-to",
parents = {"distance"},
}
labels["leptons"] = {
type = "set",
parents = {"fermions"},
}
labels["life"] = {
type = "related-to",
parents = {"nature"},
}
labels["light"] = {
type = "related-to",
parents = {"energy"},
}
labels["light sources"] = {
type = "set",
parents = {"light"},
}
labels["lightning"] = {
type = "related-to",
parents = {"atmospheric phenomena", "weather", "electricity", "light sources"},
}
labels["liquids"] = {
type = "set",
-- description = "default", -- At what temperature?
parents = {"matter"},
}
labels["lithium"] = {
type = "related-to",
parents = {"alkali metals"},
}
labels["magnesium"] = {
type = "related-to",
parents = {"alkaline earth metals"},
}
labels["manganese"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["Mars (planet)"] = {
type = "related-to",
description = "=the planet [[Mars]]",
parents = {"planets of the Solar System"},
}
labels["marijuana"] = {
type = "related-to",
parents = {"hemp family plants", "recreational drugs"},
}
labels["matter"] = {
type = "related-to",
description = "=physical [[matter]]",
parents = {"nature", "chemistry"},
}
labels["mercury (element)"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["mesons"] = {
type = "set",
parents = {"hadrons"},
}
labels["metalloids"] = {
type = "set",
parents = {"chemical elements"},
}
labels["metals"] = {
type = "set",
parents = {"matter", "metallurgy"},
}
labels["minerals"] = {
type = "set",
parents = {"matter", "mineralogy"},
}
labels["molybdenum"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["moon"] = {
type = "related-to",
description = "=the [[Moon]], Earth's only natural satellite",
parents = {"nature", "light", "celestial bodies", "moons"},
}
labels["moons"] = {
type = "related-to",
parents = {"celestial bodies"},
}
for _, planet in ipairs {"Mars", "Haumea", "Jupiter", "Saturn", "Neptune", "Uranus", "Pluto"} do
labels["moons of " .. planet] = {
type = "name",
description = ("=[[moon]]s that orbit [[%s]]"):format(planet),
parents = {"moons"},
}
end
labels["natural products (Chemistry)"] = {
type = "name",
description = "=[[organic compound]]s produced by living [[organism]]s",
parents = {"organic compounds"},
}
labels["natural resources"] = {
type = "set",
parents = {"matter"},
}
labels["neodymium"] = {
type = "related-to",
parents = {"lanthanide series chemical elements"},
}
labels["neon"] = {
type = "related-to",
parents = {"noble gases"},
}
labels["neurotoxins"] = {
type = "set",
parents = {"poisons", "neuroscience"},
}
labels["nickel"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["niobium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["nitrogen"] = {
type = "related-to",
parents = {"pnictogens"},
}
labels["noble gases"] = {
type = "set",
description = "=[[chemical element]]s in [[w:Group (periodic table)|group]] 18 of the [[periodic table]], which all have a full set of [[valence electron]]s: 2 for helium and 8 for the others",
parents = {"chemical elements", "gases"},
}
labels["organic compounds"] = {
type = "set",
parents = {"matter"},
}
labels["osmium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["oxygen"] = {
type = "related-to",
parents = {"chalcogens"},
}
labels["palladium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["petroleum"] = {
type = "related-to",
parents = {"fossil fuels", "liquids"},
}
labels["pharmaceutical drugs"] = {
type = "set",
description = "{{{langname}}} names for [[pharmaceutical#Adjective|pharmaceutical]] [[drug#Noun|drugs]].",
parents = {"drugs"},
}
labels["pharmaceutical effects"] = {
type = "set",
description = "{{{langname}}} names for [[pharmaceutical#Adjective|pharmaceutical]] [[effect#Noun|effects]].",
parents = {"pharmacology"},
}
labels["phosphorus"] = {
type = "related-to",
parents = {"pnictogens"},
}
labels["pigments"] = {
type = "set",
parents = {"colors"},
}
labels["planetoids"] = {
type = "set",
parents = {"celestial bodies"},
}
labels["planets"] = {
type = "set",
parents = {"celestial bodies"},
}
labels["planets of the Solar System"] = {
type = "name",
description = "=[[planet]]s of our [[Solar System]]",
parents = {"planets"},
}
labels["platinum"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["Pluto"] = {
type = "related-to",
description = "=the dwarf planet [[Pluto]]",
parents = {"dwarf planets of the Solar System"},
}
labels["pnictogens"] = {
type = "set",
description = "=[[chemical element]]s in [[w:Group (periodic table)|group]] 15 of the [[periodic table]], which all have 5 [[valence electron]]s",
parents = {"chemical elements"},
}
labels["poisons"] = {
type = "set",
parents = {"matter"},
}
labels["potassium"] = {
type = "related-to",
parents = {"alkali metals"},
}
labels["praseodymium"] = {
type = "related-to",
parents = {"lanthanide series chemical elements"},
}
labels["promethium"] = {
type = "related-to",
parents = {"lanthanide series chemical elements"},
}
labels["quarks"] = {
type = "set",
parents = {"fermions"},
}
labels["radiation"] = {
type = "related-to",
parents = {"energy"},
}
labels["radioactivity"] = {
type = "related-to",
parents = {"radiation", "nuclear physics"},
}
labels["radium"] = {
type = "related-to",
parents = {"alkaline earth metals"},
}
labels["radon"] = {
type = "related-to",
parents = {"noble gases"},
}
labels["rain"] = {
type = "related-to",
parents = {"weather", "water"},
}
labels["recreational drugs"] = {
type = "set",
parents = {"drugs"},
}
labels["rhodium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["rubidium"] = {
type = "related-to",
parents = {"alkali metals"},
}
labels["ruthenium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["salt"] = {
type = "related-to",
parents = {"minerals", "condiments"},
}
labels["samarium"] = {
type = "related-to",
parents = {"lanthanide series chemical elements"},
}
labels["scandium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["selenium"] = {
type = "related-to",
parents = {"chalcogens"},
}
labels["shadow"] = {
type = "related-to",
parents = {"darkness"},
}
labels["silence"] = {
type = "related-to",
parents = {"sound"},
}
labels["silicon"] = {
type = "related-to",
parents = {"carbon group elements"},
}
labels["silver"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["size"] = {
type = "related-to",
parents = {"nature"},
}
labels["snow"] = {
type = "related-to",
parents = {"weather", "water"},
}
labels["sodium"] = {
type = "related-to",
parents = {"alkali metals"},
}
labels["sound"] = {
type = "related-to",
parents = {"energy"},
}
labels["sounds"] = {
type = "set",
parents = {"sound"},
}
labels["space"] = {
type = "related-to",
parents = {"nature"},
}
labels["spring"] = {
type = "related-to",
parents = {"seasons"},
}
labels["squarks"] = {
type = "set",
parents = {"fermions"},
}
labels["stars"] = {
type = "set",
description = "{{{langname}}} names of individual [[star]]s, not including the [[Sun]].",
parents = {"celestial bodies"},
}
labels["steel"] = {
type = "related-to",
parents = {"iron", "alloys"},
}
labels["steroids"] = {
type = "set",
parents = {"organic compounds"},
}
labels["strength"] = {
type = "related-to",
parents = {"nature", "health"},
}
labels["strontium"] = {
type = "related-to",
parents = {"alkaline earth metals"},
}
labels["subatomic particles"] = {
type = "set",
parents = {"matter", "particle physics"},
}
labels["sugar acids"] = {
type = "set",
parents = {"carboxylic acids", "carbohydrates"},
}
labels["sugars"] = {
type = "set",
parents = {"carbohydrates"},
}
labels["sulfur"] = {
type = "related-to",
parents = {"chalcogens"},
}
labels["summer"] = {
type = "related-to",
parents = {"seasons"},
}
labels["sun"] = {
type = "related-to",
description = "=the [[Sun]]",
parents = {"nature", "light", "celestial bodies"},
}
labels["supposed chemical elements"] = {
breadcrumb = "supposed",
type = "set",
description = "{{{langname}}} terms for types or instances of [[chemical element]]s which were erroneously identified.",
parents = {"chemical elements", "obsolete scientific theories"},
}
labels["tantalum"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["technetium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["tellurium"] = {
type = "related-to",
parents = {"chalcogens"},
}
labels["temperature"] = {
type = "related-to",
parents = {"nature", "weather"},
}
labels["teratogens"] = {
type = "set",
parents = {"poisons"},
}
labels["thalium"] = {
type = "related-to",
parents = {"boron group elements"},
}
labels["thorium"] = {
type = "related-to",
parents = {"actinide series chemical elements"},
}
labels["tin"] = {
type = "related-to",
parents = {"carbon group elements"},
}
labels["titanium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["tobacco"] = {
type = "related-to",
parents = {"nightshades", "recreational drugs", "smoking"},
}
labels["transition metals"] = {
type = "set",
description = "{{{langname}}} terms for [[chemical element]]s in [[w:Group (periodic table)|group]]s 3 to 12 of the [[periodic table]], which are also in the {{w|d-block}} of the [[periodic table]] ",
parents = {"chemical elements", "metals"},
}
labels["tritium"] = {
type = "related-to",
parents = {"hydrogen", "three", "isotopes"},
}
labels["tungsten"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["types of chemical elements"] = {
breadcrumb = "types",
type = "type",
topic = "chemical elements",
description = "=[[chemical element]]s, e.g. metal, nonmetal, noble gas, etc.",
parents = {"chemical elements", "chemistry"},
}
labels["types of planets"] = {
type = "type",
topic = "planets",
description = "=[[planet]]s",
parents = {"planets"},
}
labels["uranium"] = {
type = "related-to",
parents = {"actinide series chemical elements"},
}
labels["vanadium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["vocalizations"] = {
type = "set",
parents = {"sounds", "communication"},
}
labels["water"] = {
type = "related-to",
parents = {"liquids"},
}
labels["waterfalls"] = {
type = "related-to",
parents = {"water"},
}
labels["weather"] = {
type = "related-to",
parents = {"atmosphere"},
}
labels["weight"] = {
type = "related-to",
parents = {"nature"},
}
labels["wetlands"] = {
type = "related-to",
parents = {"bodies of water", "ecosystems"},
}
labels["wetness"] = {
type = "related-to",
parents = {"liquids"},
}
labels["wind"] = {
type = "related-to",
parents = {"weather"},
}
labels["winter"] = {
type = "related-to",
parents = {"seasons"},
}
labels["xenon"] = {
type = "related-to",
parents = {"noble gases"},
}
labels["yttrium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["zinc"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["zirconium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["exoplanets"] = {
type = "name",
parents = {"planets"},
}
return labels
cu4vnxr2me8qddcpar8xtalgqgmvndj
88795
88794
2026-08-06T20:13:24Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/topic/Nature]]
88794
Scribunto
text/plain
local labels = {}
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
labels["nature"] = {
type = "related-to",
parents = {"all topics"},
}
labels["acids"] = {
type = "set",
parents = {"matter"},
}
labels["actinide series chemical elements"] = {
breadcrumb = "actinide series",
type = "set",
description = "{{{langname}}} terms for those chemical elements in the {{w|f-block}} of the [[periodic table]] with [[atomic number]]s from 89 to 103.",
parents = {"chemical elements", "metals", "radioactivity"},
}
labels["air"] = {
type = "related-to",
parents = {"atmosphere"},
}
labels["alkali metals"] = {
type = "set",
description = "{{{langname}}} terms for [[alkali metal]]s, chemical elements in [[w:Group (periodic table)|group]] 1 of the [[periodic table]], which all have one [[valence electron]].",
parents = {"chemical elements", "metals"},
}
labels["alkaline earth metals"] = {
type = "set",
description = "{{{langname}}} terms for [[alkaline earth metal]]s, chemical elements in [[w:Group (periodic table)|group]] 2, which all have two [[valence electron]]s.",
parents = {"chemical elements", "metals"},
}
labels["alkaloids"] = {
type = "set",
parents = {"organic compounds"},
}
labels["alloys"] = {
type = "set",
parents = {"metals"},
}
labels["aluminium"] = {
type = "related-to",
parents = {"boron group elements"},
}
labels["amino acids"] = {
type = "set",
parents = {"carboxylic acids"},
}
labels["animal sounds"] = {
type = "set",
parents = {"sounds", "vocalizations"},
}
labels["animal welfare"] = {
type = "related-to",
description = "{{{langname}}} terms closely associated with [[animal welfare]].",
parents = {"ethics"},
}
labels["antimatter"] = {
type = "related-to",
parents = {"matter"},
}
labels["antimony"] = {
type = "related-to",
parents = {"pnictogens"},
}
labels["argon"] = {
type = "related-to",
parents = {"noble gases"},
}
labels["arsenic"] = {
type = "related-to",
parents = {"pnictogens"},
}
labels["astatine"] = {
type = "related-to",
parents = {"halogens"},
}
labels["asteroids"] = {
type = "set",
parents = {"celestial bodies"},
}
labels["atmosphere"] = {
type = "related-to",
parents = {"nature"},
}
labels["atmospheric phenomena"] = {
type = "set",
parents = {"atmosphere"},
}
labels["autumn"] = {
type = "related-to",
parents = {"seasons"},
}
labels["barium"] = {
type = "related-to",
parents = {"alkaline earth metals"},
}
labels["baryons"] = {
type = "set",
parents = {"hadrons"},
}
labels["beryllium"] = {
type = "related-to",
parents = {"alkaline earth metals"},
}
labels["birth"] = {
type = "related-to",
parents = {"reproduction"},
}
labels["bismuth"] = {
type = "related-to",
parents = {"pnictogens"},
}
labels["boron"] = {
type = "related-to",
parents = {"boron group elements"},
}
labels["boron group elements"] = {
breadcrumb = "boron group",
type = "set",
description = "{{{langname}}} terms for chemical elements in [[w:Group (periodic table)|group]] 13 of the [[periodic table]], which all have three [[valence electron]]s.",
parents = {"chemical elements"},
}
labels["bosons"] = {
type = "set",
parents = {"subatomic particles"},
}
labels["bromine"] = {
type = "related-to",
parents = {"halogens"},
}
labels["cadmium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["calcium"] = {
type = "related-to",
parents = {"alkaline earth metals"},
}
labels["carbohydrates"] = {
type = "set",
parents = {"organic compounds"},
}
labels["carbon"] = {
type = "related-to",
parents = {"carbon group elements"},
}
labels["carbon group elements"] = {
breadcrumb = "carbon group",
type = "set",
description = "{{{langname}}} terms for chemical elements in [[w:Group (periodic table)|group]] 14 of the [[periodic table]], which all have four [[valence electron]]s.",
parents = {"chemical elements"},
}
labels["carboxylic acids"] = {
type = "set",
parents = {"acids", "organic compounds"},
}
labels["celestial bodies"] = {
type = "set",
description = "{{{langname}}} terms for varous [[celestial body|celestial bodies]]; things found in outer space.",
parents = {"space"},
}
labels["cerium"] = {
type = "related-to",
parents = {"lanthanide series chemical elements"},
}
labels["cesium"] = {
type = "related-to",
parents = {"alkali metals"},
}
labels["chalcogens"] = {
type = "set",
description = "{{{langname}}} terms for chemical elements in [[w:Group (periodic table)|group]] 16 of the [[periodic table]], which all have 6 [[valence electron]]s.",
parents = {"chemical elements"},
}
labels["chemical elements"] = {
type = "set",
parents = {"matter"},
}
for period = 1, 7 do
local key = ("Period %d elements"):format(period)
if not labels[key] then
labels[key] = {
type = "set",
description = ("{{{langname}}} terms for [[chemical element]]s in period %d of the [[periodic table]]."):format(period),
parents = {"chemical elements"},
}
end
end
for group = 1, 12 do
local key = ("Group %d elements"):format(group)
if not labels[key] then
labels[key] = {
type = "set",
description = ("{{{langname}}} terms for [[chemical element]]s in [[w:Group (periodic table)|group]] %d of the [[periodic table]]."):format(group),
parents = {"chemical elements"},
}
end
end
for _, block in ipairs({"s", "p", "d", "f"}) do
local key = block:upper() .. "-block elements"
if not labels[key] then
labels[key] = {
type = "set",
description = ("{{{langname}}} terms for [[chemical element]]s in the %s-block of the [[periodic table]]."):format(block),
parents = {"chemical elements"},
}
end
end
if not labels["Nonmetals"] then
labels["Nonmetals"] = {
type = "set",
description = "{{{langname}}} terms for [[nonmetal]] [[chemical element]]s.",
parents = {"chemical elements"},
}
end
if not labels["Post-transition metals"] then
labels["Post-transition metals"] = {
type = "set",
description = "{{{langname}}} terms for [[post-transition metal]] [[chemical element]]s.",
parents = {"chemical elements", "metals"},
}
end
labels["chemical isomers"] = {
type = "related-to",
parents = {"matter", "physical chemistry", "shapes"},
}
labels["chemical processes"] = {
type = "set",
description = "=[[chemical]] [[process]]es",
parents = {"nature"},
}
labels["chlorine"] = {
type = "related-to",
parents = {"halogens"},
}
labels["chromium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["classical planets"] = {
type = "name",
description = "{{{langname}}} names for the [[classical planet]]s of our Solar System.",
parents = {"celestial bodies"},
}
labels["climate change"] = {
type = "related-to",
description = "=[[anthropogenic]] [[climate change]]",
parents = {"nature"},
}
labels["clouds"] = {
type = "set",
parents = {"atmospheric phenomena"},
}
labels["coal"] = {
type = "related-to",
parents = {"fossil fuels"},
}
labels["cobalt"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["coenzymes"] = {
type = "set",
parents = {"enzymes"},
}
labels["colors"] = {
type = "set",
parents = {"light", "vision"},
}
for _, color_etc in ipairs {
{"black"},
{"blue"},
{"brown"},
{"green"},
{"grey", "[[grey]] ([[gray]])"},
{"orange"},
{"pink"},
{"purple"},
{"red"},
{"violet"},
{"white"},
{"yellow"},
} do
local color, desc = unpack(color_etc)
desc = desc or ("[[%s]]"):format(color)
labels[color .. "s"] = {
type = "set",
description = ("=shades of the [[color]] %s"):format(desc),
parents = {"colors"},
}
end
labels["colors of the rainbow"] = {
type = "set",
description = "=[[color]]s of the [[rainbow]]",
parents = {"colors"},
}
labels["combustion"] = {
type = "related-to",
parents = {"chemical processes"},
}
labels["compass points"] = {
type = "set",
parents = {"directions", "navigation"},
}
labels["copper"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["crystal"] = {
type = "related-to",
parents = {"matter", "physical chemistry"},
}
labels["darkness"] = {
type = "related-to",
parents = {"light"},
}
labels["deuterium"] = {
type = "related-to",
parents = {"hydrogen", "two", "isotopes"}
}
labels["directions"] = {
type = "set",
parents = {"nature"},
}
labels["distance"] = {
type = "related-to",
parents = {"nature"},
}
labels["drugs"] = {
type = "set",
parents = {"matter", "pharmacology"},
}
labels["dryness"] = {
type = "related-to",
parents = {"liquids"},
}
labels["dwarf planets of the Solar System"] = {
type = "name",
description = "=[[dwarf planet]]s of our [[Solar System]]",
parents = {"celestial bodies"},
}
labels["dyes"] = {
type = "set",
parents = {"matter", "pigments"},
}
labels["energy"] = {
type = "related-to",
parents = {"nature"},
}
labels["enzymes"] = {
type = "set",
parents = {"proteins", "catalysis"},
}
labels["europium"] = {
type = "related-to",
parents = {"lanthanide series chemical elements"},
}
labels["explosives"] = {
type = "set",
parents = {"matter", "weapons"},
}
labels["eye colors"] = {
type = "set",
description = "=[[color]]s that are mostly or exclusively used of [[eye]]s",
parents = {"colors", "eye"},
}
labels["fatty acids"] = {
type = "set",
parents = {"carboxylic acids"},
}
labels["fermions"] = {
type = "set",
parents = {"subatomic particles"},
}
labels["fictional materials"] = {
type = "set",
parents = {"science fiction", "materials"},
}
labels["fire"] = {
type = "related-to",
parents = {"combustion", "light sources"},
wp = "Fire",
}
labels["fluorine"] = {
type = "related-to",
parents = {"halogens"},
}
labels["fog"] = {
type = "related-to",
parents = {"weather", "water"},
}
labels["fossil fuels"] = {
type = "set",
parents = {"carbon", "energy", "natural resources"},
}
labels["francium"] = {
type = "related-to",
parents = {"alkali metals"},
}
labels["gadolinium"] = {
type = "related-to",
parents = {"lanthanide series chemical elements"},
}
labels["galaxies"] = {
type = "set",
parents = {"celestial bodies"},
}
labels["gallium"] = {
type = "related-to",
parents = {"boron group elements"},
}
labels["gases"] = {
type = "set",
parents = {"matter"},
}
labels["germanium"] = {
type = "related-to",
parents = {"carbon group elements"},
}
labels["gold"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["hadrons"] = {
type = "set",
parents = {"subatomic particles"},
}
labels["hair colors"] = {
type = "set",
description = "=[[color]]s that are mostly or exclusively used of [[hair]]",
parents = {"colors", "hair"},
}
labels["halogens"] = {
type = "set",
description = "=[[chemical element]]s in [[w:Group (periodic table)|group]] 17 of the [[periodic table]], which all have 7 [[valence electron]]s",
parents = {"chemical elements"},
}
labels["helium"] = {
type = "related-to",
parents = {"noble gases"},
}
labels["heroin"] = {
type = "related-to",
parents = {"recreational drugs"},
}
labels["height"] = {
type = "related-to",
parents = {"distance"},
}
labels["horse colors"] = {
type = "set",
description = "=[[color]]s that are mostly or exclusively used of [[horse]]s",
parents = {"colors", "horses"},
}
labels["hydrogen"] = {
type = "related-to",
parents = {"chemical elements"},
}
labels["hypothetical chemical elements"] = {
breadcrumb = "hypothetical",
type = "set",
description = "{{{langname}}} terms for types or instances of [[chemical element]]s which have been hypothesized to exist, but not (yet) discovered.",
parents = {"chemical elements", "fictional materials"},
}
labels["ice"] = {
type = "related-to",
parents = {"water"},
}
labels["indium"] = {
type = "related-to",
parents = {"boron group elements"},
}
labels["inorganic compounds"] = {
type = "set",
parents = {"matter"},
}
labels["iodine"] = {
type = "related-to",
parents = {"halogens"},
}
labels["ions"] = {
type = "set",
parents = {"matter", "chemistry", "electricity"},
}
labels["iridium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["iron"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["isotopes"] = {
type = "set",
parents = {"chemical elements"},
}
labels["krypton"] = {
type = "related-to",
parents = {"noble gases"},
}
labels["lanthanide series chemical elements"] = {
breadcrumb = "lanthanide series",
type = "set",
description = "=[[chemical element]]s in the {{w|f-block}} of the [[periodic table]] with [[atomic number]]s from 57 to 71",
parents = {"chemical elements"},
}
labels["lanthanum"] = {
type = "related-to",
parents = {"lanthanide series chemical elements"},
}
labels["lead"] = {
type = "related-to",
parents = {"carbon group elements"},
}
labels["length"] = {
type = "related-to",
parents = {"distance"},
}
labels["leptons"] = {
type = "set",
parents = {"fermions"},
}
labels["life"] = {
type = "related-to",
parents = {"nature"},
}
labels["light"] = {
type = "related-to",
parents = {"energy"},
}
labels["light sources"] = {
type = "set",
parents = {"light"},
}
labels["lightning"] = {
type = "related-to",
parents = {"atmospheric phenomena", "weather", "electricity", "light sources"},
}
labels["liquids"] = {
type = "set",
-- description = "default", -- At what temperature?
parents = {"matter"},
}
labels["lithium"] = {
type = "related-to",
parents = {"alkali metals"},
}
labels["magnesium"] = {
type = "related-to",
parents = {"alkaline earth metals"},
}
labels["manganese"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["Mars (planet)"] = {
type = "related-to",
description = "=the planet [[Mars]]",
parents = {"planets of the Solar System"},
}
labels["marijuana"] = {
type = "related-to",
parents = {"hemp family plants", "recreational drugs"},
}
labels["matter"] = {
type = "related-to",
description = "=physical [[matter]]",
parents = {"nature", "chemistry"},
}
labels["mercury (element)"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["mesons"] = {
type = "set",
parents = {"hadrons"},
}
labels["metalloids"] = {
type = "set",
parents = {"chemical elements"},
}
labels["metals"] = {
type = "set",
parents = {"matter", "metallurgy"},
}
labels["minerals"] = {
type = "set",
parents = {"matter", "mineralogy"},
}
labels["molybdenum"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["moon"] = {
type = "related-to",
description = "=the [[Moon]], Earth's only natural satellite",
parents = {"nature", "light", "celestial bodies", "moons"},
}
labels["moons"] = {
type = "related-to",
parents = {"celestial bodies"},
}
for _, planet in ipairs {"Mars", "Haumea", "Jupiter", "Saturn", "Neptune", "Uranus", "Pluto"} do
labels["moons of " .. planet] = {
type = "name",
description = ("=[[moon]]s that orbit [[%s]]"):format(planet),
parents = {"moons"},
}
end
labels["natural products (Chemistry)"] = {
type = "name",
description = "=[[organic compound]]s produced by living [[organism]]s",
parents = {"organic compounds"},
}
labels["natural resources"] = {
type = "set",
parents = {"matter"},
}
labels["neodymium"] = {
type = "related-to",
parents = {"lanthanide series chemical elements"},
}
labels["neon"] = {
type = "related-to",
parents = {"noble gases"},
}
labels["neurotoxins"] = {
type = "set",
parents = {"poisons", "neuroscience"},
}
labels["nickel"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["niobium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["nitrogen"] = {
type = "related-to",
parents = {"pnictogens"},
}
labels["noble gases"] = {
type = "set",
description = "=[[chemical element]]s in [[w:Group (periodic table)|group]] 18 of the [[periodic table]], which all have a full set of [[valence electron]]s: 2 for helium and 8 for the others",
parents = {"chemical elements", "gases"},
}
labels["organic compounds"] = {
type = "set",
parents = {"matter"},
}
labels["osmium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["oxygen"] = {
type = "related-to",
parents = {"chalcogens"},
}
labels["palladium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["petroleum"] = {
type = "related-to",
parents = {"fossil fuels", "liquids"},
}
labels["pharmaceutical drugs"] = {
type = "set",
description = "{{{langname}}} names for [[pharmaceutical#Adjective|pharmaceutical]] [[drug#Noun|drugs]].",
parents = {"drugs"},
}
labels["pharmaceutical effects"] = {
type = "set",
description = "{{{langname}}} names for [[pharmaceutical#Adjective|pharmaceutical]] [[effect#Noun|effects]].",
parents = {"pharmacology"},
}
labels["phosphorus"] = {
type = "related-to",
parents = {"pnictogens"},
}
labels["pigments"] = {
type = "set",
parents = {"colors"},
}
labels["planetoids"] = {
type = "set",
parents = {"celestial bodies"},
}
labels["planets"] = {
type = "set",
parents = {"celestial bodies"},
}
labels["planets of the Solar System"] = {
type = "name",
description = "=[[planet]]s of our [[Solar System]]",
parents = {"planets"},
}
labels["platinum"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["Pluto"] = {
type = "related-to",
description = "=the dwarf planet [[Pluto]]",
parents = {"dwarf planets of the Solar System"},
}
labels["pnictogens"] = {
type = "set",
description = "=[[chemical element]]s in [[w:Group (periodic table)|group]] 15 of the [[periodic table]], which all have 5 [[valence electron]]s",
parents = {"chemical elements"},
}
labels["poisons"] = {
type = "set",
parents = {"matter"},
}
labels["potassium"] = {
type = "related-to",
parents = {"alkali metals"},
}
labels["praseodymium"] = {
type = "related-to",
parents = {"lanthanide series chemical elements"},
}
labels["promethium"] = {
type = "related-to",
parents = {"lanthanide series chemical elements"},
}
labels["quarks"] = {
type = "set",
parents = {"fermions"},
}
labels["radiation"] = {
type = "related-to",
parents = {"energy"},
}
labels["radioactivity"] = {
type = "related-to",
parents = {"radiation", "nuclear physics"},
}
labels["radium"] = {
type = "related-to",
parents = {"alkaline earth metals"},
}
labels["radon"] = {
type = "related-to",
parents = {"noble gases"},
}
labels["rain"] = {
type = "related-to",
parents = {"weather", "water"},
}
labels["recreational drugs"] = {
type = "set",
parents = {"drugs"},
}
labels["rhodium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["rubidium"] = {
type = "related-to",
parents = {"alkali metals"},
}
labels["ruthenium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["salt"] = {
type = "related-to",
parents = {"minerals", "condiments"},
}
labels["samarium"] = {
type = "related-to",
parents = {"lanthanide series chemical elements"},
}
labels["scandium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["selenium"] = {
type = "related-to",
parents = {"chalcogens"},
}
labels["shadow"] = {
type = "related-to",
parents = {"darkness"},
}
labels["silence"] = {
type = "related-to",
parents = {"sound"},
}
labels["silicon"] = {
type = "related-to",
parents = {"carbon group elements"},
}
labels["silver"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["size"] = {
type = "related-to",
parents = {"nature"},
}
labels["snow"] = {
type = "related-to",
parents = {"weather", "water"},
}
labels["sodium"] = {
type = "related-to",
parents = {"alkali metals"},
}
labels["sound"] = {
type = "related-to",
parents = {"energy"},
}
labels["sounds"] = {
type = "set",
parents = {"sound"},
}
labels["space"] = {
type = "related-to",
parents = {"nature"},
}
labels["spring"] = {
type = "related-to",
parents = {"seasons"},
}
labels["squarks"] = {
type = "set",
parents = {"fermions"},
}
labels["stars"] = {
type = "set",
description = "{{{langname}}} names of individual [[star]]s, not including the [[Sun]].",
parents = {"celestial bodies"},
}
labels["steel"] = {
type = "related-to",
parents = {"iron", "alloys"},
}
labels["steroids"] = {
type = "set",
parents = {"organic compounds"},
}
labels["strength"] = {
type = "related-to",
parents = {"nature", "health"},
}
labels["strontium"] = {
type = "related-to",
parents = {"alkaline earth metals"},
}
labels["subatomic particles"] = {
type = "set",
parents = {"matter", "particle physics"},
}
labels["sugar acids"] = {
type = "set",
parents = {"carboxylic acids", "carbohydrates"},
}
labels["sugars"] = {
type = "set",
parents = {"carbohydrates"},
}
labels["sulfur"] = {
type = "related-to",
parents = {"chalcogens"},
}
labels["summer"] = {
type = "related-to",
parents = {"seasons"},
}
labels["sun"] = {
type = "related-to",
description = "=the [[Sun]]",
parents = {"nature", "light", "celestial bodies"},
}
labels["supposed chemical elements"] = {
breadcrumb = "supposed",
type = "set",
description = "{{{langname}}} terms for types or instances of [[chemical element]]s which were erroneously identified.",
parents = {"chemical elements", "obsolete scientific theories"},
}
labels["tantalum"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["technetium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["tellurium"] = {
type = "related-to",
parents = {"chalcogens"},
}
labels["temperature"] = {
type = "related-to",
parents = {"nature", "weather"},
}
labels["teratogens"] = {
type = "set",
parents = {"poisons"},
}
labels["thalium"] = {
type = "related-to",
parents = {"boron group elements"},
}
labels["thorium"] = {
type = "related-to",
parents = {"actinide series chemical elements"},
}
labels["tin"] = {
type = "related-to",
parents = {"carbon group elements"},
}
labels["titanium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["tobacco"] = {
type = "related-to",
parents = {"nightshades", "recreational drugs", "smoking"},
}
labels["transition metals"] = {
type = "set",
description = "{{{langname}}} terms for [[chemical element]]s in [[w:Group (periodic table)|group]]s 3 to 12 of the [[periodic table]], which are also in the {{w|d-block}} of the [[periodic table]] ",
parents = {"chemical elements", "metals"},
}
labels["tritium"] = {
type = "related-to",
parents = {"hydrogen", "three", "isotopes"},
}
labels["tungsten"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["types of chemical elements"] = {
breadcrumb = "types",
type = "type",
topic = "chemical elements",
description = "=[[chemical element]]s, e.g. metal, nonmetal, noble gas, etc.",
parents = {"chemical elements", "chemistry"},
}
labels["types of planets"] = {
type = "type",
topic = "planets",
description = "=[[planet]]s",
parents = {"planets"},
}
labels["uranium"] = {
type = "related-to",
parents = {"actinide series chemical elements"},
}
labels["vanadium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["vocalizations"] = {
type = "set",
parents = {"sounds", "communication"},
}
labels["water"] = {
type = "related-to",
parents = {"liquids"},
}
labels["waterfalls"] = {
type = "related-to",
parents = {"water"},
}
labels["weather"] = {
type = "related-to",
parents = {"atmosphere"},
}
labels["weight"] = {
type = "related-to",
parents = {"nature"},
}
labels["wetlands"] = {
type = "related-to",
parents = {"bodies of water", "ecosystems"},
}
labels["wetness"] = {
type = "related-to",
parents = {"liquids"},
}
labels["wind"] = {
type = "related-to",
parents = {"weather"},
}
labels["winter"] = {
type = "related-to",
parents = {"seasons"},
}
labels["xenon"] = {
type = "related-to",
parents = {"noble gases"},
}
labels["yttrium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["zinc"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["zirconium"] = {
type = "related-to",
parents = {"transition metals"},
}
labels["exoplanets"] = {
type = "name",
parents = {"planets"},
}
return labels
cu4vnxr2me8qddcpar8xtalgqgmvndj
Mòideal:category tree/topic/Numbers
828
17899
88796
2026-07-23T19:36:37Z
en>WingerBot
0
explicit 'description = "default"' unnecessary; remove it (manually assisted)
88796
Scribunto
text/plain
local labels = {}
labels["numbers"] = {
type = "related-to",
parents = {"all topics"},
}
for _, number in ipairs {
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten",
"eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen",
"twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety", "one hundred", "one thousand",
} do
local bare_number = number:gsub("^one ", "")
labels[bare_number] = {
type = "related-to",
description = ("=the number [[%s]] ([[etymologically]] or [[semantically]])"):format(number),
parents = {"numbers"},
}
end
labels["historical numbers"] = {
type = "set",
parents = {"numbers"},
}
return labels
ts3yhwgullub3u9kmik658nnibpnc27
88797
88796
2026-08-06T20:13:42Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/topic/Numbers]]
88796
Scribunto
text/plain
local labels = {}
labels["numbers"] = {
type = "related-to",
parents = {"all topics"},
}
for _, number in ipairs {
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten",
"eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen",
"twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety", "one hundred", "one thousand",
} do
local bare_number = number:gsub("^one ", "")
labels[bare_number] = {
type = "related-to",
description = ("=the number [[%s]] ([[etymologically]] or [[semantically]])"):format(number),
parents = {"numbers"},
}
end
labels["historical numbers"] = {
type = "set",
parents = {"numbers"},
}
return labels
ts3yhwgullub3u9kmik658nnibpnc27
Mòideal:category tree/topic/Philosophy
828
17900
88798
2026-07-23T19:36:40Z
en>WingerBot
0
explicit 'description = "default"' unnecessary; remove it (manually assisted)
88798
Scribunto
text/plain
local labels = {}
labels["philosophy"] = {
type = "related-to",
parents = {"all topics"},
}
labels["aesthetics"] = {
type = "related-to",
description = "default no singularize",
parents = {"philosophy"},
}
labels["Ancient Greek philosophy"] = {
type = "related-to",
breadcrumb = "Ancient Greek",
parents = {"philosophy", "Ancient Greece"},
}
labels["Chinese philosophy"] = {
type = "related-to",
breadcrumb = "Chinese",
parents = {"philosophy", "China"},
}
labels["Confucianism"] = {
type = "related-to",
parents = {"Chinese philosophy", "religion"},
}
labels["epistemology"] = {
type = "related-to",
parents = {"philosophy"},
}
labels["ethics"] = {
type = "related-to",
description = "default no singularize",
parents = {"philosophy"},
}
labels["jurisprudence"] = {
type = "related-to",
parents = {"law", "philosophy", "humanities"},
}
labels["metaphysics"] = {
type = "related-to",
description = "default no singularize",
parents = {"philosophy"},
}
labels["memetics"] = {
type = "related-to",
description = "default no singularize",
parents = {"philosophy"},
}
labels["Platonism"] = {
type = "related-to",
parents = {"Ancient Greek philosophy"},
}
labels["theology"] = {
type = "related-to",
parents = {"philosophy", "religion"},
}
labels["transhumanism"] = {
type = "related-to",
parents = {"philosophy", "technology"},
}
labels["virtue"] = {
type = "related-to",
parents = {"ethics"},
}
return labels
lar1gs424tu59s579z1k8zr9qdyobor
88799
88798
2026-08-06T20:14:03Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/topic/Philosophy]]
88798
Scribunto
text/plain
local labels = {}
labels["philosophy"] = {
type = "related-to",
parents = {"all topics"},
}
labels["aesthetics"] = {
type = "related-to",
description = "default no singularize",
parents = {"philosophy"},
}
labels["Ancient Greek philosophy"] = {
type = "related-to",
breadcrumb = "Ancient Greek",
parents = {"philosophy", "Ancient Greece"},
}
labels["Chinese philosophy"] = {
type = "related-to",
breadcrumb = "Chinese",
parents = {"philosophy", "China"},
}
labels["Confucianism"] = {
type = "related-to",
parents = {"Chinese philosophy", "religion"},
}
labels["epistemology"] = {
type = "related-to",
parents = {"philosophy"},
}
labels["ethics"] = {
type = "related-to",
description = "default no singularize",
parents = {"philosophy"},
}
labels["jurisprudence"] = {
type = "related-to",
parents = {"law", "philosophy", "humanities"},
}
labels["metaphysics"] = {
type = "related-to",
description = "default no singularize",
parents = {"philosophy"},
}
labels["memetics"] = {
type = "related-to",
description = "default no singularize",
parents = {"philosophy"},
}
labels["Platonism"] = {
type = "related-to",
parents = {"Ancient Greek philosophy"},
}
labels["theology"] = {
type = "related-to",
parents = {"philosophy", "religion"},
}
labels["transhumanism"] = {
type = "related-to",
parents = {"philosophy", "technology"},
}
labels["virtue"] = {
type = "related-to",
parents = {"ethics"},
}
return labels
lar1gs424tu59s579z1k8zr9qdyobor
Mòideal:category tree/topic/Sciences
828
17901
88800
2026-07-23T19:36:47Z
en>WingerBot
0
explicit 'description = "default"' unnecessary; remove it (manually assisted)
88800
Scribunto
text/plain
local labels = {}
labels["sciences"] = {
type = "related-to",
description = "default with the",
parents = {"all topics"},
}
labels["acceleration"] = {
type = "related-to",
parents = {"physics"},
}
labels["acoustics"] = {
type = "related-to",
description = "default no singularize",
parents = {"applied sciences", "physics", "sound"},
}
labels["adjectives"] = {
type = "type",
parents = {"parts of speech"},
}
labels["aeronautics"] = {
type = "related-to",
description = "default no singularize",
parents = {"sciences"},
}
labels["aerospace"] = {
type = "related-to",
parents = {"sciences", "space", "astronautics", "aeronautics"},
}
labels["agriculture"] = {
type = "related-to",
parents = {"applied sciences"},
}
labels["alchemy"] = {
type = "related-to",
parents = {"paranormal", "pseudoscience", "obsolete scientific theories"},
}
labels["alternative medicine"] = {
type = "related-to",
parents = {"medicine", "pseudoscience"},
}
labels["analytical chemistry"] = {
type = "related-to",
parents = {"chemistry"},
}
labels["anatomy"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[anatomy]], the study of the [[body]] and its parts.",
additional = "{{also|Category:{{{langcode}}}:Body parts}}",
parents = {"biology", "medicine"},
}
labels["andrology"] = {
type = "related-to",
parents = {"medicine", "male"},
}
labels["anthropology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[anthropology]], the study of [[human]]s.",
additional = "{{also|Category:{{{langcode}}}:Human}}",
parents = {"zoology", "social sciences"},
}
labels["anti-gravity"] = {
type = "related-to",
parents = {"fictional abilities", "gravity", "science fiction", "space access"},
}
labels["applied sciences"] = {
type = "related-to",
parents = {"sciences"},
}
labels["arachnology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[arachnology]], the study of [[spider]]s.",
additional = "{{also|Category:{{{langcode}}}:Spiders}}",
parents = {"zoology", "arthropodology"},
}
labels["archaeology"] = {
type = "related-to",
parents = {"anthropology", "sciences"},
commonscat = true,
wpcat = true,
}
labels["architectural elements"] = {
type = "set",
parents = {"architecture"},
}
labels["architecture"] = {
type = "related-to",
parents = {"applied sciences", "art"},
}
labels["arthropodology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[arthropodology]], the study of [[arthropod]]s.",
additional = "{{also|Category:{{{langcode}}}:Arthropods}}",
parents = {"zoology"},
}
labels["artificial intelligence"] = {
type = "related-to",
parents = {"computer science", "cybernetics"},
}
labels["asterisms"] = {
type = "name",
parents = {"astronomy"},
}
labels["astronautics"] = {
type = "related-to",
description = "default no singularize",
parents = {"applied sciences", "space"},
}
labels["astronomy"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[astronomy]], the study of [[stars]] and other [[celestial bodies]].",
additional = "{{also|Category:{{{langcode}}}:Celestial bodies}}",
parents = {"sciences", "space"},
}
labels["astrophysics"] = {
type = "related-to",
description = "default no singularize",
parents = {"physics", "astronomy"},
}
labels["aviation"] = {
type = "related-to",
parents = {"aeronautics", "transport"},
}
labels["avionics"] = {
type = "related-to",
description = "default no singularize",
parents = {"aeronautics", "electronics"},
}
labels["Ayurveda"] = {
type = "related-to",
parents = {"alternative medicine", "India"},
}
labels["bacteriology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[bacteriology]], the study of [[bacteria]].",
additional = "{{also|Category:{{{langcode}}}:Bacteria}}",
parents = {"medicine", "microbiology"},
}
labels["ballistics"] = {
type = "related-to",
description = "default no singularize",
parents = {"mechanics"},
}
labels["biases"] = {
type = "set",
description = "{{{langname}}} terms for [[bias|biases]], [[inclination]]s of views towards [[inaccurate]] [[representation]].",
additional = "{{also|Category:{{{langcode}}}:Logical fallacies|Category:{{{langcode}}}:Forms of discrimination}}",
parents = {"psychology", "statistics"},
}
labels["biochemistry"] = {
type = "related-to",
parents = {"chemistry", "biology"},
}
labels["biology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[biology]], the study of [[life]].",
additional = "{{also|Category:{{{langcode}}}:Lifeforms}}",
parents = {"sciences"},
}
labels["black holes"] = {
type = "related-to",
parents = {"astronomy", "astrophysics", "gravity", "relativity"},
}
labels["botany"] = {
type = "related-to",
description = "=[[botany]], the study of [[plants]]",
additional = "{{also|Category:{{{langcode}}}:Plants}}",
parents = {"biology"},
}
labels["bryology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[bryology]], the study of [[moss]]es and other lower plants.",
additional = "{{also|Category:{{{langcode}}}:Mosses}}",
parents = {"biology", "botany"},
}
labels["cardiology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[cardiology]], the study of the [[heart]].",
parents = {"medicine", "circulatory system"},
}
labels["carpentry"] = {
type = "related-to",
parents = {"construction", "woodworking"},
}
labels["cartography"] = {
type = "related-to",
parents = {"geography"},
}
labels["catalysis"] = {
type = "related-to",
parents = {"chemical processes", "chemical reactions", "physical chemistry"},
}
labels["chemical engineering"] = {
type = "related-to",
parents = {"engineering", "chemistry"},
}
labels["chemical formulae"] = {
type = "name",
parents = {"chemical notation"},
}
labels["chemical notation"] = {
type = "related-to",
parents = {"chemistry", "names", "symbols"},
}
labels["chemical reactions"] = {
type = "related-to,set",
parents = {"chemistry"},
}
labels["chemical reagents"] = {
type = "set",
parents = {"chemistry"},
}
labels["chemistry"] = {
type = "related-to",
parents = {"sciences"},
}
labels["Chinese astronomy"] = {
type = "related-to",
parents = {"astronomy"},
}
labels["Chinese phonetics"] = {
type = "related-to",
description = "default no singularize",
parents = {"phonetics", "Chinese"},
}
labels["classical mechanics"] = {
type = "related-to",
description = "default no singularize",
parents = {"mechanics"},
}
labels["classical studies"] = {
type = "related-to",
description = "default no singularize",
parents = {"linguistics", "literature", "history"},
}
labels["climatology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[climatology]], the study of [[climate]].",
parents = {"earth sciences"},
}
labels["clinical psychology"] = {
type = "related-to",
parents = {"psychology", "pathology"},
}
labels["computational linguistics"] = {
type = "related-to",
description = "default no singularize",
parents = {"linguistics", "computer science"},
}
labels["computer science"] = {
type = "related-to",
parents = {"sciences", "computing"},
}
labels["conchology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[conchology]], the study of mollusc [[shell]]s.",
parents = {"malacology"},
}
labels["constellations"] = {
type = "name",
parents = {"astronomy"},
}
labels["construction"] = {
type = "related-to",
parents = {"engineering", "architecture"},
}
labels["control theory"] = {
type = "related-to",
parents = {"sciences", "systems"},
}
labels["cosmology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[cosmology]], the study of the [[universe]].",
parents = {"astronomy"},
}
labels["criminology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[criminology]], the study of [[crime]].",
additional = "{{also|Category:{{{langcode}}}:Crime}}",
parents = {"sociology", "crime"},
}
labels["cryptography"] = {
type = "related-to",
parents = {"formal sciences", "mathematics", "computer science"},
}
labels["cryptozoology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[cryptozoology]], the [[pseudoscientific]] study of mythological creatures.",
additional = "{{also|Category:{{{langcode}}}:Mythological creatures}}",
parents = {"zoology", "paranormal"},
}
labels["crystallography"] = {
type = "related-to",
parents = {"physics", "crystal"},
}
labels["cultural anthropology"] = {
type = "related-to",
parents = {"anthropology", "culture"},
}
labels["cybernetics"] = {
type = "related-to",
description = "default no singularize",
parents = {"applied mathematics", "systems theory"},
}
labels["cytology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[cytology]], the study of cell biology, cell structure, formation, classification and related topics.",
parents = {"biology"},
}
labels["data management"] = {
type = "related-to",
parents = {"information science", "computer science"},
}
labels["demarcation lines"] = {
type = "name",
description = "=political [[demarcation line]]s",
parents = {"politics"},
}
labels["decision theory"] = {
type = "related-to",
parents = {"probability theory", "epistemology"},
}
labels["demography"] = {
type = "related-to",
parents = {"sciences", "statistics"},
}
labels["dentistry"] = {
type = "related-to",
parents = {"medicine", "teeth"},
}
labels["dermatology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[dermatology]], the study of the [[skin]].",
additional = "{{also|Category:{{{langcode}}}:Skin}}",
parents = {"medicine"},
}
labels["developmental biology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[developmental biology]], the study of the [[development]] of [[lifeform]]s.",
parents = {"biology"},
}
labels["earth sciences"] = {
type = "related-to",
parents = {"sciences"},
}
labels["earthquake engineering"] = {
type = "related-to",
parents = {"engineering"},
}
labels["ecology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[ecology]], the study of interaction between [[life]] and its environment.",
parents = {"biology"},
}
labels["economics"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[economics]], the study of the [[economy]].",
parents = {"social sciences"},
}
labels["ecosystems"] = {
type = "related-to",
parents = {"ecology", "landforms"},
}
labels["electrencephalography"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[electrencephalography]], the electrical measurement of the [[brain]].",
additional = "{{also|Category:{{{langcode}}}:Brain}}",
parents = {"neuroscience"},
}
labels["electrical engineering"] = {
type = "related-to",
parents = {"engineering", "electricity"},
}
labels["electricity"] = {
type = "related-to",
parents = {"electromagnetism"},
}
labels["electrocardiography"] = {
type = "related-to",
parents = {"cardiology"},
}
labels["electrodynamics"] = {
type = "related-to",
description = "default no singularize",
parents = {"electromagnetism"},
}
labels["electrochemistry"] = {
type = "related-to",
parents = {"physical chemistry", "electricity"},
}
labels["electromagnetism"] = {
type = "related-to",
parents = {"physics"},
}
labels["element nomenclature"] = {
type = "related-to",
parents = {"chemical notation", "names", "chemical elements"},
}
labels["embryology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[embryology]], the study of the [[embryo]].",
parents = {"biology", "developmental biology", "medicine"},
}
labels["emergency medicine"] = {
type = "related-to",
parents = {"medicine"},
}
labels["endocrinology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[endocrinology]], the study of [[hormones]] and the [[endocrine system]].",
additional = "{{also|Category:{{{langcode}}}:Hormones}}",
parents = {"medicine"},
}
labels["engineering"] = {
type = "related-to",
parents = {"applied sciences", "technology"},
}
labels["enterprise engineering"] = {
type = "related-to",
parents = {"engineering"},
}
labels["entomology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[entomology]], the study of [[insect]]s.",
additional = "{{also|Category:{{{langcode}}}:Insects}}",
parents = {"zoology", "arthropodology"},
}
labels["epidemiology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[epidemiology]].",
parents = {"medicine"},
}
labels["ethnography"] = {
type = "related-to",
parents = {"anthropology"},
}
labels["ethnology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[ethnology]], the study of [[people]]s.",
parents = {"anthropology"},
}
labels["ethology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[ethology]], the study of [[animal]] [[behaviour]].",
parents = {"zoology"},
}
labels["evolutionary theory"] = {
type = "related-to",
parents = {"biology"},
}
labels["flat earth"] = {
type = "related-to",
description = "{{{langname}}} terms used by believers in a flat earth or related to the concept.",
parents = {"pseudoscience", "obsolete scientific theories"},
}
labels["flax"] = {
type = "related-to",
parents = {"agriculture", "Malpighiales order plants"},
}
labels["fluid dynamics"] = {
type = "related-to",
description = "default no singularize",
parents = {"physics"},
}
labels["forestry"] = {
type = "related-to",
parents = {"applied sciences"},
}
labels["functional group prefixes"] = {
type = "set",
parents = {"organic chemistry"},
}
labels["functional group suffixes"] = {
type = "set",
parents = {"organic chemistry"},
}
labels["gardens"] = {
type = "type",
description = "=places where plants are grown",
parents = {"home", "horticulture"},
}
labels["gastroenterology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[gastroenterology]], the study of the [[digestive system]].",
parents = {"medicine"},
}
labels["genetics"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[genetics]], the study of [[gene]]s.",
parents = {"biology"},
}
labels["geography"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[geography]], the study of various phenomena found on [[Earth]]'s surface.",
additional = "{{also|Category:{{{langcode}}}:Landforms|Category:{{{langcode}}}:Polities}}",
parents = {"earth sciences"},
}
labels["geological periods"] = {
type = "related-to",
parents = {"geology"},
}
labels["geology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[geology]], the study of the solid parts of [[Earth]].",
parents = {"earth sciences"},
}
labels["geomorphology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[geomorphology]].",
parents = {"geology"},
}
labels["geopolitics"] = {
type = "related-to",
description = "default no singularize",
parents = {"geography", "politics"},
}
labels["geospatial science"] = {
type = "related-to",
parents = {"earth sciences"},
}
labels["gerontology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[gerontology]], the study of [[aging]].",
additional = "{{also|Category:{{{langcode}}}:Age}}",
parents = {"medicine"},
}
labels["grammar"] = {
type = "related-to",
parents = {"linguistics"},
}
labels["grammatical cases"] = {
type = "set",
parents = {"grammar"},
}
labels["grammatical moods"] = {
type = "set",
parents = {"grammar"},
}
labels["gravity"] = {
type = "related-to",
parents = {"acceleration", "astrophysics", "mechanics"},
}
labels["gynaecology"] = {
type = "related-to",
parents = {"medicine", "female"},
}
labels["helminthology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[helminthology]], the study of [[helminth]]s ([[parasitic]] [[worm#Noun|worms]]).",
additional = "{{also|Category:{{{langcode}}}:Flatworms}}",
parents = {"zoology"},
}
labels["hematology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[hematology]], the study of [[blood]].",
parents = {"medicine"},
}
labels["hepatology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[hepatology]], the study of the [[liver]].",
parents = {"medicine"},
}
labels["herpetology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[herpetology]], the study of [[reptile]]s and amphibians.",
additional = "{{also|Category:{{{langcode}}}:Reptiles|Category:{{{langcode}}}:Amphibians}}",
parents = {"zoology"},
}
labels["homeopathy"] = {
type = "related-to",
parents = {"alternative medicine"},
}
labels["horticulture"] = {
type = "related-to",
parents = {"agriculture", "botany"},
}
labels["hydroacoustics"] = {
type = "related-to",
description = "default no singularize",
parents = {"acoustics", "hydrology"},
}
labels["hydrocarbon chain prefixes"] = {
type = "set",
parents = {"organic chemistry"},
}
labels["hydrocarbon chain suffixes"] = {
type = "set",
parents = {"organic chemistry"},
}
labels["hydrology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[hydrology]], the study of [[water]], its movement and distribution.",
additional = "{{also|Category:{{{langcode}}}:Water}}",
parents = {"earth sciences", "water"},
}
labels["ichthyology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[ichthyology]], the study of [[fish]].",
additional = "{{also|Category:{{{langcode}}}:Fish}}",
parents = {"zoology"},
}
labels["immunochemistry"] = {
type = "related-to",
parents = {"biochemistry", "immunology"},
}
labels["immunology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[immunology]], the study of the [[immune system]].",
parents = {"medicine"},
}
labels["Indo-European studies"] = {
type = "related-to",
description = "default no singularize",
parents = {"linguistics"},
}
labels["information science"] = {
type = "related-to",
parents = {"applied sciences"},
}
labels["inorganic chemistry"] = {
type = "related-to",
parents = {"chemistry"},
}
labels["interdisciplinary fields"] = {
type = "related-to",
parents = {"sciences"},
}
labels["isolines"] = {
type = "related-to",
parents = {"cartography", "meteorology"},
}
labels["Lagrange points"] = {
type = "related-to",
parents = {"orbits"},
}
labels["lexicography"] = {
type = "related-to",
parents = {"linguistics"},
}
labels["library science"] = {
type = "related-to",
parents = {"sciences", "education", "information science"},
}
labels["lichenology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[lichenology]], the study of [[lichen]]s.",
additional = "{{also|Category:{{{langcode}}}:Lichens}}",
parents = {"mycology", "phycology"},
}
labels["linguistic morphology"] = {
type = "related-to",
parents = {"linguistics"},
}
labels["linguistics"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[linguistics]], the study and analysis of [[language]].",
additional = "{{also|Category:{{{langcode}}}:Language}}",
parents = {"language", "social sciences"},
}
labels["locksmithing"] = {
type = "related-to",
parents = {"technology"},
}
labels["maize (crop)"] = {
type = "related-to",
description = "=[[maize]] (called [[corn]] in North America) as a crop",
additional = "For terms related to maize as a food, see [[:Category:Maize (food)]] and for maize as a plant, see [[:Category:Maize (plant)]].",
parents = {"agriculture", "grains"},
}
labels["malacology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[malacology]], the study of [[mollusk]]s.",
additional = "{{also|Category:{{{langcode}}}:Mollusks}}",
parents = {"zoology"},
}
labels["mammalogy"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[mammalogy]], the study of [[mammal]]s.",
additional = "{{also|Category:{{{langcode}}}:Mammals}}",
parents = {"zoology"},
}
labels["marine biology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[marine biology]], the study of [[life]] in the [[sea]].",
parents = {"biology"},
}
labels["masonry"] = {
type = "related-to",
parents = {"construction"},
}
labels["materials science"] = {
type = "related-to",
parents = {"sciences", "engineering"},
}
labels["mechanical engineering"] = {
type = "related-to",
parents = {"engineering"},
}
labels["mechanics"] = {
type = "related-to",
description = "default no singularize",
parents = {"physics"},
}
labels["medical genetics"] = {
type = "related-to",
description = "default no singularize",
parents = {"medicine", "genetics"},
}
labels["medicine"] = {
type = "related-to",
description = "{{{langname}}} terms that pertain to the the science and practice of [[medicine]].",
parents = {"biology", "healthcare"},
}
labels["metamaterials"] = {
type = "related-to",
parents = {"physics"},
}
labels["physical constants"] = {
type = "set",
parents = {"physics"},
}
labels["machine learning"] = {
type = "related-to",
parents = {"artificial intelligence"},
}
labels["meteorology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[meteorology]], the study of [[weather]].",
additional = "{{also|Category:{{{langcode}}}:Weather}}",
parents = {"earth sciences", "atmosphere"},
}
labels["metrology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[metrology]], the science of [[measure|measuring]].",
parents = {"applied sciences"},
wpcat = true,
commonscat=true,
}
labels["microbiology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[microbiology]], the study of [[life]] at [[microscopic]] scales, too small for the [[human]] [[eye]].",
parents = {"biology"},
}
labels["microscopy"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[microscopy]], the field of using [[microscopes]] for study.",
parents = {"optics", "microbiology"},
}
labels["mineralogy"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[mineralogy]], the study of [[mineral]]s.",
additional = "{{also|Category:{{{langcode}}}:Minerals}}",
parents = {"geology"},
}
labels["molecular biology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[molecular biology]], the study of the [[molecules]] found in [[life]], their functions and effects.",
parents = {"biochemistry", "biology", "genetics"},
}
labels["mycology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[mycology]], the study of [[fungi]].",
additional = "{{also|Category:{{{langcode}}}:Fungi}}",
parents = {"biology"},
}
labels["NASA"] = {
type = "related-to",
description = "=the United States National Aeronautics and Space Administration and the US space program",
parents = {"astronautics", "aviation"},
}
labels["nephrology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[nephrology]], the study of the [[kidney]]s.",
parents = {"medicine"},
}
labels["neuroanatomy"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[neuroanatomy]], the study of the structure and organization of the [[nervous system]].",
parents = {"anatomy", "neurology", "neuroscience"},
}
labels["neurology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[neurology]], the study of [[nerves]] and [[neurons]].",
parents = {"medicine", "neuroscience"},
}
labels["neuroscience"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[neuroscience]], the study of the [[nervous system]].",
parents = {"biology"},
}
labels["neurosurgery"] = {
type = "related-to",
parents = {"surgery", "neurology"},
}
labels["neutron"] = {
type = "related-to",
parents = {"nuclear physics"},
}
labels["neutron stars"] = {
type = "related-to",
parents = {"neutron", "gravity", "astrophysics", "relativity", "stars"},
}
labels["nouns"] = {
type = "related-to",
parents = {"parts of speech"},
}
labels["nuclear energy"] = {
type = "related-to",
parents = {"energy", "nuclear physics", "technology"},
}
labels["nuclear fusion"] = {
type = "related-to",
parents = {"nuclear energy"},
}
labels["nuclear physics"] = {
type = "related-to",
description = "default no singularize",
parents = {"physics", "quantum mechanics"},
}
labels["nuclear rocketry"] = {
type = "related-to",
parents = {"nuclear energy", "rocketry", "space access"},
}
labels["obstetrics"] = {
type = "related-to",
description = "default no singularize",
parents = {"developmental biology", "medicine", "pregnancy"},
}
labels["obsolete scientific theories"] = {
type = "set",
description = "{{{langname}}} terms for types or instances of [[obsolete]] [[scientific]] [[theory|theories]] that are no longer, or have never been, widely accepted.",
parents = {"history of science"},
}
labels["oceanography"] = {
type = "related-to",
parents = {"sciences"},
}
labels["oenology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[oenology]], the study of [[wine]] and [[winemaking]].",
additional = "{{also|Category:{{{langcode}}}:Wine}}",
parents = {"sciences"},
}
labels["oncology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[oncology]], the study of [[cancer]].",
parents = {"biology", "medicine", "pathology"},
}
labels["onomastics"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[onomastics]], the study of [[name]]s.",
additional = "{{also|Category:{{{langcode}}}:Names|Category:{{{langname}}} names}}",
parents = {"linguistics", "names"},
}
labels["ophthalmology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[ophthalmology]], the study of the [[eye]].",
additional = "{{also|Category:{{{langcode}}}:Eye}}",
parents = {"medicine", "vision"},
}
labels["optics"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[optics]], the study of the behaviour of [[light]].",
additional = "{{also|Category:{{{langcode}}}:Light}}",
parents = {"physics"},
}
labels["orbits"] = {
type = "related-to",
parents = {"astronautics", "gravity", "space", "curves"},
}
labels["organic chemistry"] = {
type = "related-to",
parents = {"chemistry"},
}
labels["orgonomy"] = {
type = "related-to",
description = "=the [[orgone]] theory proposed by [[w:William Reich|William Reich]]",
parents = {"alternative medicine", "pseudoscience"},
}
labels["ornithology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[ornithology]], the study of [[birds]].",
additional = "{{also|Category:{{{langcode}}}:Birds}}",
parents = {"zoology"},
}
labels["paleontology"] = {
type = "related-to",
parents = {"sciences", "geology"},
}
labels["palynology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[palynology]], the study of [[particle]]s and particulate matter.",
parents = {"geology"},
}
labels["paranormal"] = {
type = "related-to",
description = "default with the",
parents = {"pseudoscience"},
}
labels["parapsychology"] = {
type = "related-to",
parents = {"paranormal", "pseudoscience"},
}
labels["parasitology"] = {
type = "related-to",
parents = {"pathology", "zoology"},
}
labels["particle physics"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[particle physics]], the study of [[subatomic]] [[particle]]s.",
additional = "{{also|Category:{{{langcode}}}:Subatomic particles}}",
parents = {"physics"},
}
labels["parts of speech"] = {
type = "related-to",
description = "=[[part of speech|parts of speech]]",
parents = {"grammar"},
}
labels["pathology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[pathology]], the study of [[disease]].",
additional = "{{also|Category:{{{langcode}}}:Disease}}",
parents = {"medicine", "disease"},
}
labels["petrochemistry"] = {
type = "related-to",
parents = {"chemistry", "chemical engineering", "oil industry", "petroleum"},
}
labels["petrology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[petrology]], the study of [[rock]].",
additional = "{{also|Category:{{{langcode}}}:Rocks}}",
parents = {"geology"},
}
labels["pharmacology"] = {
type = "related-to",
parents = {"biochemistry", "medicine"},
}
labels["pharmacy"] = {
type = "related-to",
parents = {"medicine", "pharmacology"},
}
labels["phonemes"] = {
type = "set",
description = "=contrastive sounds within a language",
parents = {"phonology"},
}
labels["phonetics"] = {
type = "related-to",
description = "default no singularize",
parents = {"linguistics"},
}
labels["phonology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[phonology]], the organisation of [[speech]] sounds within a [[language]].",
parents = {"linguistics"},
}
labels["photochemistry"] = {
type = "related-to",
parents = {"physical chemistry", "light"},
}
labels["phrenology"] = {
type = "related-to",
parents = {"psychology", "pseudoscience"},
}
labels["phycology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[phycology]], the study of [[algae]].",
additional = "{{also|Category:{{{langcode}}}:Algae}}",
parents = {"botany", "microbiology"},
}
labels["physical chemistry"] = {
type = "related-to",
parents = {"chemistry", "physics"},
}
labels["physical quantities"] = {
type = "set",
parents = {"physics"},
}
labels["physics"] = {
type = "related-to",
description = "default no singularize",
parents = {"sciences"},
commonscat = true,
wpcat = true,
}
labels["physiology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[physiology]].",
parents = {"biology", "medicine"},
}
labels["phytopathology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[phytopathology]], the study of [[disease]] in [[plant]]s.",
additional = "{{also|Category:{{{langcode}}}:Plant diseases}}",
parents = {"pathology", "botany"},
}
labels["planetary nomenclature"] = {
type = "related-to",
parents = {"planetology"},
}
labels["planetology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[planetology]], the study of [[planet]]s.",
additional = "{{also|Category:{{{langcode}}}:Planets}}",
parents = {"astronomy", "geology"},
}
labels["plant anatomy"] = {
type = "related-to",
description = "{{{langname}}} terms used in the study of the structure of plants and their parts.",
parents = {"botany", "anatomy"},
}
labels["plastic surgery"] = {
type = "related-to",
parents = {"surgery"},
}
labels["political science"] = {
type = "related-to",
parents = {"social sciences", "politics"},
}
labels["potential theory"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["pragmatics"] = {
type = "related-to",
description = "default no singularize",
parents = {"linguistics"},
}
labels["prosody"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[prosody]], the study of the [[suprasegmental]] aspects of [[speech]].",
parents = {"linguistics"},
}
labels["pseudoscience"] = {
type = "related-to",
parents = {"sciences"},
}
labels["psychiatry"] = {
type = "related-to",
parents = {"medicine"},
}
labels["psychoanalysis"] = {
type = "related-to",
description = "default no singularize",
parents = {"psychology"},
}
labels["psychology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[psychology]], the study of the [[mind]].",
additional = "{{also|Category:{{{langcode}}}:Mind}}",
parents = {"social sciences"},
}
labels["psychotherapy"] = {
type = "related-to",
parents = {"psychology"},
}
labels["pulmonology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[pulmonology]], the study of the [[respiratory system]], including the [[lung]]s.",
parents = {"medicine"},
}
labels["pyrotechnics"] = {
type = "related-to",
description = "default no singularize",
parents = {"sciences"},
}
labels["quantity"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["quantum mechanics"] = {
type = "related-to",
description = "default no singularize",
parents = {"physics"},
}
labels["relativity"] = {
type = "related-to",
parents = {"physics"},
}
labels["rheumatology"] = {
type = "related-to",
parents = {"medicine"},
}
labels["robotics"] = {
type = "related-to",
description = "default no singularize",
parents = {"engineering"},
}
labels["rocketry"] = {
type = "related-to",
parents = {"astronautics"},
}
labels["roofing"] = {
type = "related-to",
parents = {"construction"},
}
labels["rosiculture"] = {
type = "related-to",
parents = {"horticulture"},
}
labels["rotation"] = {
type = "related-to",
parents = {"mechanics", "geometry"},
}
labels["Russian space program"] = {
type = "related-to",
description = "=the Russian and Soviet space programs",
parents = {"astronautics", "aviation"},
}
labels["sales"] = {
type = "related-to",
description = "default no singularize",
parents = {"marketing"},
}
labels["seismology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[seismology]], the study of [[earthquake]]s.",
parents = {"geology"},
}
labels["semantics"] = {
type = "related-to",
description = "default no singularize",
parents = {"linguistics"},
}
labels["Semitic linguistics"] = {
type = "related-to",
description = "{{{langname}}} terms used in the study and analysis of [[Semitic]] [[language]]s.",
parents = {"linguistics"},
}
labels["semiotics"] = {
type = "related-to",
description = "default no singularize",
parents = {"social sciences", "linguistics"},
}
labels["sexology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[sexology]], the study of [[human]] [[sexuality]].",
additional = "{{also|Category:{{{langcode}}}:Sex}}",
parents = {"psychology", "sex", "sociology"},
}
labels["SI units"] = {
type = "related-to",
description = "=[[International System of Units|SI]] [[unit of measure|units of measure]]",
parents = {"units of measure"},
}
labels["signal processing"] = {
type = "related-to",
parents = {"applied mathematics", "telecommunications"},
wpcat = true,
commonscat = true,
}
labels["social sciences"] = {
type = "related-to",
description = "default with the",
parents = {"sciences", "society"},
}
labels["sociolinguistics"] = {
type = "related-to",
description = "default no singularize",
parents = {"linguistics", "sociology"},
}
labels["sociology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[sociology]], the study of [[society]].",
additional = "{{also|Category:{{{langcode}}}:Society}}",
parents = {"social sciences"},
}
labels["software engineering"] = {
type = "related-to",
parents = {"engineering", "computer science", "software"},
wpcat = true,
commonscat = true,
}
labels["soil science"] = {
type = "related-to",
parents = {"earth sciences"},
}
labels["sorting algorithms"] = {
type = "set",
parents = {"theory of computing"},
}
labels["sound engineering"] = {
type = "related-to",
parents = {"engineering", "sound"},
}
labels["space access"] = {
type = "related-to",
parents = {"astronautics"},
}
labels["space sciences"] = {
type = "related-to",
parents = {"sciences", "space"},
}
labels["spectroscopy"] = {
type = "related-to",
parents = {"analytical chemistry", "optics", "physics"},
}
labels["statistical mechanics"] = {
type = "related-to",
description = "default no singularize",
parents = {"mechanics"},
}
labels["statistics"] = {
type = "related-to",
description = "default no singularize",
parents = {"formal sciences", "mathematics"},
}
labels["surgery"] = {
type = "related-to",
parents = {"medicine"},
}
labels["surveying"] = {
type = "related-to",
parents = {"geography"},
}
labels["systems"] = {
type = "related-to",
parents = {"interdisciplinary fields", "sciences", "society"},
}
labels["systems engineering"] = {
type = "related-to",
parents = {"engineering"},
}
labels["systems theory"] = {
type = "related-to",
parents = {"sciences", "systems"},
}
labels["taxonomy"] = {
type = "related-to",
additional = "For individual [[taxa]], see [[:Category:Taxonomic names]].",
parents = {"biology", "evolutionary theory"},
}
labels["taxonomic ranks"] = {
type = "set",
parents = {"taxonomy"},
}
labels["tenses"] = {
type = "related-to",
parents = {"grammar"},
}
labels["teratology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[teratology]], the study of abnormalities in the [[development]] of the [[body]].",
parents = {"developmental biology", "medicine", "pathology", "toxicology"},
}
labels["theory of computing"] = {
type = "related-to",
description = "{{{langname}}} terms used in theoretical discussion of [[computer science]] or [[computing]].",
parents = {"computer science"},
}
labels["thermodynamics"] = {
type = "related-to",
description = "default no singularize",
parents = {"physics"},
}
labels["tides"] = {
type = "related-to",
description = "default no singularize",
parents = {"gravity", "periodic occurrences"},
}
labels["toxicology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[toxicology]], the study of [[poisons]], [[toxins]] and other substances with negative effects on the [[body]].",
additional = "{{also|Category:{{{langcode}}}:Poisons}}",
parents = {"medicine", "pharmacology"},
}
labels["traditional Chinese medicine"] = {
type = "related-to",
parents = {"alternative medicine"},
}
labels["traffic engineering"] = {
type = "related-to",
parents = {"engineering", "road transport"},
}
labels["translation studies"] = {
type = "related-to",
description = "default no singularize",
parents = {"linguistics"},
}
labels["traumatology"] = {
type = "related-to",
description = "{{{langname}}} terms used in traumatology, the study of wounds and injuries caused by accidents or violence to a person.",
parents = {"medicine", "pathology"},
}
labels["ufology"] = {
type = "related-to",
parents = {"paranormal", "anti-gravity", "celestial inhabitants"},
}
labels["units of area"] = {
type = "set",
description = "{{{langname}}} terms that are [[w:unit of area|units of area]].",
parents = {"units of measure"},
commonscat = true,
wpcat = true,
}
labels["units of energy"] = {
type = "set",
description = "{{{langname}}} terms that are [[w:unit of energy|units of energy]].",
parents = {"units of measure", "energy"},
commonscat = false,
wpcat = true,
}
labels["units of length"] = {
type = "set",
description = "{{{langname}}} terms that are [[w:unit of length|units of length]].",
parents = {"units of measure"},
commonscat = true,
wpcat = true,
}
labels["units of mass"] = {
type = "set",
description = "{{{langname}}} terms that are [[w:unit of mass|units of mass]].",
parents = {"units of measure"},
commonscat = true,
wpcat = true,
}
labels["units of measure"] = {
type = "set",
description = "{{{langname}}} terms that are [[unit of measure|units of measure]].",
parents = {"quantity", "metrology"},
commonscat = true,
wpcat = "units of measurement",
}
labels["units of time"] = {
type = "set",
description = "{{{langname}}} terms that are [[unit of time|units of time]].",
parents = {"units of measure", "time"},
commonscat = "spans of time",
wpcat = true,
}
labels["units of volume"] = {
type = "set",
description = "{{{langname}}} terms that are [[w:unit of volume|units of volume]].",
parents = {"units of measure"},
commonscat = true,
wpcat = true,
}
labels["urban studies"] = {
type = "related-to",
parents = {"applied sciences", "social sciences"},
}
labels["urology"] = {
type = "related-to",
parents = {"medicine"},
}
labels["verbs"] = {
type = "related-to",
parents = {"parts of speech"},
}
labels["veterinary medicine"] = {
type = "related-to",
parents = {"medicine", "animals"},
}
labels["virology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[virology]], the study of [[virus]]es.",
additional = "{{also|Category:{{{langcode}}}:Viruses}}",
parents = {"medicine", "microbiology", "pathology"},
}
labels["volcanology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[volcanology]], the study of [[volcano]]es.",
parents = {"geology"},
}
labels["zoology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[zoology]], the study of [[animal]]s.",
additional = "{{also|Category:{{{langcode}}}:Animals}}",
parents = {"biology"},
}
labels["zymurgy"] = {
type = "related-to",
parents = {"biochemistry", "brewing"},
}
labels["colour and pattern morphs"] = {
type = "set",
description = "{{{langname}}} terms for [[colour]] and [[pattern]] [[morph#Etymology 2|morphs]]",
parents = {"zoology"},
}
labels["geological formations"] = {
type = "set",
description = "{{{langname}}} [[w:geological formation|geological formations]]",
parents = {"geology"},
}
return labels
2b0tabbxbsjqs255dvvkhwnjt8prxx1
88801
88800
2026-08-06T20:14:14Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/topic/Sciences]]
88800
Scribunto
text/plain
local labels = {}
labels["sciences"] = {
type = "related-to",
description = "default with the",
parents = {"all topics"},
}
labels["acceleration"] = {
type = "related-to",
parents = {"physics"},
}
labels["acoustics"] = {
type = "related-to",
description = "default no singularize",
parents = {"applied sciences", "physics", "sound"},
}
labels["adjectives"] = {
type = "type",
parents = {"parts of speech"},
}
labels["aeronautics"] = {
type = "related-to",
description = "default no singularize",
parents = {"sciences"},
}
labels["aerospace"] = {
type = "related-to",
parents = {"sciences", "space", "astronautics", "aeronautics"},
}
labels["agriculture"] = {
type = "related-to",
parents = {"applied sciences"},
}
labels["alchemy"] = {
type = "related-to",
parents = {"paranormal", "pseudoscience", "obsolete scientific theories"},
}
labels["alternative medicine"] = {
type = "related-to",
parents = {"medicine", "pseudoscience"},
}
labels["analytical chemistry"] = {
type = "related-to",
parents = {"chemistry"},
}
labels["anatomy"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[anatomy]], the study of the [[body]] and its parts.",
additional = "{{also|Category:{{{langcode}}}:Body parts}}",
parents = {"biology", "medicine"},
}
labels["andrology"] = {
type = "related-to",
parents = {"medicine", "male"},
}
labels["anthropology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[anthropology]], the study of [[human]]s.",
additional = "{{also|Category:{{{langcode}}}:Human}}",
parents = {"zoology", "social sciences"},
}
labels["anti-gravity"] = {
type = "related-to",
parents = {"fictional abilities", "gravity", "science fiction", "space access"},
}
labels["applied sciences"] = {
type = "related-to",
parents = {"sciences"},
}
labels["arachnology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[arachnology]], the study of [[spider]]s.",
additional = "{{also|Category:{{{langcode}}}:Spiders}}",
parents = {"zoology", "arthropodology"},
}
labels["archaeology"] = {
type = "related-to",
parents = {"anthropology", "sciences"},
commonscat = true,
wpcat = true,
}
labels["architectural elements"] = {
type = "set",
parents = {"architecture"},
}
labels["architecture"] = {
type = "related-to",
parents = {"applied sciences", "art"},
}
labels["arthropodology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[arthropodology]], the study of [[arthropod]]s.",
additional = "{{also|Category:{{{langcode}}}:Arthropods}}",
parents = {"zoology"},
}
labels["artificial intelligence"] = {
type = "related-to",
parents = {"computer science", "cybernetics"},
}
labels["asterisms"] = {
type = "name",
parents = {"astronomy"},
}
labels["astronautics"] = {
type = "related-to",
description = "default no singularize",
parents = {"applied sciences", "space"},
}
labels["astronomy"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[astronomy]], the study of [[stars]] and other [[celestial bodies]].",
additional = "{{also|Category:{{{langcode}}}:Celestial bodies}}",
parents = {"sciences", "space"},
}
labels["astrophysics"] = {
type = "related-to",
description = "default no singularize",
parents = {"physics", "astronomy"},
}
labels["aviation"] = {
type = "related-to",
parents = {"aeronautics", "transport"},
}
labels["avionics"] = {
type = "related-to",
description = "default no singularize",
parents = {"aeronautics", "electronics"},
}
labels["Ayurveda"] = {
type = "related-to",
parents = {"alternative medicine", "India"},
}
labels["bacteriology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[bacteriology]], the study of [[bacteria]].",
additional = "{{also|Category:{{{langcode}}}:Bacteria}}",
parents = {"medicine", "microbiology"},
}
labels["ballistics"] = {
type = "related-to",
description = "default no singularize",
parents = {"mechanics"},
}
labels["biases"] = {
type = "set",
description = "{{{langname}}} terms for [[bias|biases]], [[inclination]]s of views towards [[inaccurate]] [[representation]].",
additional = "{{also|Category:{{{langcode}}}:Logical fallacies|Category:{{{langcode}}}:Forms of discrimination}}",
parents = {"psychology", "statistics"},
}
labels["biochemistry"] = {
type = "related-to",
parents = {"chemistry", "biology"},
}
labels["biology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[biology]], the study of [[life]].",
additional = "{{also|Category:{{{langcode}}}:Lifeforms}}",
parents = {"sciences"},
}
labels["black holes"] = {
type = "related-to",
parents = {"astronomy", "astrophysics", "gravity", "relativity"},
}
labels["botany"] = {
type = "related-to",
description = "=[[botany]], the study of [[plants]]",
additional = "{{also|Category:{{{langcode}}}:Plants}}",
parents = {"biology"},
}
labels["bryology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[bryology]], the study of [[moss]]es and other lower plants.",
additional = "{{also|Category:{{{langcode}}}:Mosses}}",
parents = {"biology", "botany"},
}
labels["cardiology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[cardiology]], the study of the [[heart]].",
parents = {"medicine", "circulatory system"},
}
labels["carpentry"] = {
type = "related-to",
parents = {"construction", "woodworking"},
}
labels["cartography"] = {
type = "related-to",
parents = {"geography"},
}
labels["catalysis"] = {
type = "related-to",
parents = {"chemical processes", "chemical reactions", "physical chemistry"},
}
labels["chemical engineering"] = {
type = "related-to",
parents = {"engineering", "chemistry"},
}
labels["chemical formulae"] = {
type = "name",
parents = {"chemical notation"},
}
labels["chemical notation"] = {
type = "related-to",
parents = {"chemistry", "names", "symbols"},
}
labels["chemical reactions"] = {
type = "related-to,set",
parents = {"chemistry"},
}
labels["chemical reagents"] = {
type = "set",
parents = {"chemistry"},
}
labels["chemistry"] = {
type = "related-to",
parents = {"sciences"},
}
labels["Chinese astronomy"] = {
type = "related-to",
parents = {"astronomy"},
}
labels["Chinese phonetics"] = {
type = "related-to",
description = "default no singularize",
parents = {"phonetics", "Chinese"},
}
labels["classical mechanics"] = {
type = "related-to",
description = "default no singularize",
parents = {"mechanics"},
}
labels["classical studies"] = {
type = "related-to",
description = "default no singularize",
parents = {"linguistics", "literature", "history"},
}
labels["climatology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[climatology]], the study of [[climate]].",
parents = {"earth sciences"},
}
labels["clinical psychology"] = {
type = "related-to",
parents = {"psychology", "pathology"},
}
labels["computational linguistics"] = {
type = "related-to",
description = "default no singularize",
parents = {"linguistics", "computer science"},
}
labels["computer science"] = {
type = "related-to",
parents = {"sciences", "computing"},
}
labels["conchology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[conchology]], the study of mollusc [[shell]]s.",
parents = {"malacology"},
}
labels["constellations"] = {
type = "name",
parents = {"astronomy"},
}
labels["construction"] = {
type = "related-to",
parents = {"engineering", "architecture"},
}
labels["control theory"] = {
type = "related-to",
parents = {"sciences", "systems"},
}
labels["cosmology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[cosmology]], the study of the [[universe]].",
parents = {"astronomy"},
}
labels["criminology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[criminology]], the study of [[crime]].",
additional = "{{also|Category:{{{langcode}}}:Crime}}",
parents = {"sociology", "crime"},
}
labels["cryptography"] = {
type = "related-to",
parents = {"formal sciences", "mathematics", "computer science"},
}
labels["cryptozoology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[cryptozoology]], the [[pseudoscientific]] study of mythological creatures.",
additional = "{{also|Category:{{{langcode}}}:Mythological creatures}}",
parents = {"zoology", "paranormal"},
}
labels["crystallography"] = {
type = "related-to",
parents = {"physics", "crystal"},
}
labels["cultural anthropology"] = {
type = "related-to",
parents = {"anthropology", "culture"},
}
labels["cybernetics"] = {
type = "related-to",
description = "default no singularize",
parents = {"applied mathematics", "systems theory"},
}
labels["cytology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[cytology]], the study of cell biology, cell structure, formation, classification and related topics.",
parents = {"biology"},
}
labels["data management"] = {
type = "related-to",
parents = {"information science", "computer science"},
}
labels["demarcation lines"] = {
type = "name",
description = "=political [[demarcation line]]s",
parents = {"politics"},
}
labels["decision theory"] = {
type = "related-to",
parents = {"probability theory", "epistemology"},
}
labels["demography"] = {
type = "related-to",
parents = {"sciences", "statistics"},
}
labels["dentistry"] = {
type = "related-to",
parents = {"medicine", "teeth"},
}
labels["dermatology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[dermatology]], the study of the [[skin]].",
additional = "{{also|Category:{{{langcode}}}:Skin}}",
parents = {"medicine"},
}
labels["developmental biology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[developmental biology]], the study of the [[development]] of [[lifeform]]s.",
parents = {"biology"},
}
labels["earth sciences"] = {
type = "related-to",
parents = {"sciences"},
}
labels["earthquake engineering"] = {
type = "related-to",
parents = {"engineering"},
}
labels["ecology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[ecology]], the study of interaction between [[life]] and its environment.",
parents = {"biology"},
}
labels["economics"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[economics]], the study of the [[economy]].",
parents = {"social sciences"},
}
labels["ecosystems"] = {
type = "related-to",
parents = {"ecology", "landforms"},
}
labels["electrencephalography"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[electrencephalography]], the electrical measurement of the [[brain]].",
additional = "{{also|Category:{{{langcode}}}:Brain}}",
parents = {"neuroscience"},
}
labels["electrical engineering"] = {
type = "related-to",
parents = {"engineering", "electricity"},
}
labels["electricity"] = {
type = "related-to",
parents = {"electromagnetism"},
}
labels["electrocardiography"] = {
type = "related-to",
parents = {"cardiology"},
}
labels["electrodynamics"] = {
type = "related-to",
description = "default no singularize",
parents = {"electromagnetism"},
}
labels["electrochemistry"] = {
type = "related-to",
parents = {"physical chemistry", "electricity"},
}
labels["electromagnetism"] = {
type = "related-to",
parents = {"physics"},
}
labels["element nomenclature"] = {
type = "related-to",
parents = {"chemical notation", "names", "chemical elements"},
}
labels["embryology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[embryology]], the study of the [[embryo]].",
parents = {"biology", "developmental biology", "medicine"},
}
labels["emergency medicine"] = {
type = "related-to",
parents = {"medicine"},
}
labels["endocrinology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[endocrinology]], the study of [[hormones]] and the [[endocrine system]].",
additional = "{{also|Category:{{{langcode}}}:Hormones}}",
parents = {"medicine"},
}
labels["engineering"] = {
type = "related-to",
parents = {"applied sciences", "technology"},
}
labels["enterprise engineering"] = {
type = "related-to",
parents = {"engineering"},
}
labels["entomology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[entomology]], the study of [[insect]]s.",
additional = "{{also|Category:{{{langcode}}}:Insects}}",
parents = {"zoology", "arthropodology"},
}
labels["epidemiology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[epidemiology]].",
parents = {"medicine"},
}
labels["ethnography"] = {
type = "related-to",
parents = {"anthropology"},
}
labels["ethnology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[ethnology]], the study of [[people]]s.",
parents = {"anthropology"},
}
labels["ethology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[ethology]], the study of [[animal]] [[behaviour]].",
parents = {"zoology"},
}
labels["evolutionary theory"] = {
type = "related-to",
parents = {"biology"},
}
labels["flat earth"] = {
type = "related-to",
description = "{{{langname}}} terms used by believers in a flat earth or related to the concept.",
parents = {"pseudoscience", "obsolete scientific theories"},
}
labels["flax"] = {
type = "related-to",
parents = {"agriculture", "Malpighiales order plants"},
}
labels["fluid dynamics"] = {
type = "related-to",
description = "default no singularize",
parents = {"physics"},
}
labels["forestry"] = {
type = "related-to",
parents = {"applied sciences"},
}
labels["functional group prefixes"] = {
type = "set",
parents = {"organic chemistry"},
}
labels["functional group suffixes"] = {
type = "set",
parents = {"organic chemistry"},
}
labels["gardens"] = {
type = "type",
description = "=places where plants are grown",
parents = {"home", "horticulture"},
}
labels["gastroenterology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[gastroenterology]], the study of the [[digestive system]].",
parents = {"medicine"},
}
labels["genetics"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[genetics]], the study of [[gene]]s.",
parents = {"biology"},
}
labels["geography"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[geography]], the study of various phenomena found on [[Earth]]'s surface.",
additional = "{{also|Category:{{{langcode}}}:Landforms|Category:{{{langcode}}}:Polities}}",
parents = {"earth sciences"},
}
labels["geological periods"] = {
type = "related-to",
parents = {"geology"},
}
labels["geology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[geology]], the study of the solid parts of [[Earth]].",
parents = {"earth sciences"},
}
labels["geomorphology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[geomorphology]].",
parents = {"geology"},
}
labels["geopolitics"] = {
type = "related-to",
description = "default no singularize",
parents = {"geography", "politics"},
}
labels["geospatial science"] = {
type = "related-to",
parents = {"earth sciences"},
}
labels["gerontology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[gerontology]], the study of [[aging]].",
additional = "{{also|Category:{{{langcode}}}:Age}}",
parents = {"medicine"},
}
labels["grammar"] = {
type = "related-to",
parents = {"linguistics"},
}
labels["grammatical cases"] = {
type = "set",
parents = {"grammar"},
}
labels["grammatical moods"] = {
type = "set",
parents = {"grammar"},
}
labels["gravity"] = {
type = "related-to",
parents = {"acceleration", "astrophysics", "mechanics"},
}
labels["gynaecology"] = {
type = "related-to",
parents = {"medicine", "female"},
}
labels["helminthology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[helminthology]], the study of [[helminth]]s ([[parasitic]] [[worm#Noun|worms]]).",
additional = "{{also|Category:{{{langcode}}}:Flatworms}}",
parents = {"zoology"},
}
labels["hematology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[hematology]], the study of [[blood]].",
parents = {"medicine"},
}
labels["hepatology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[hepatology]], the study of the [[liver]].",
parents = {"medicine"},
}
labels["herpetology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[herpetology]], the study of [[reptile]]s and amphibians.",
additional = "{{also|Category:{{{langcode}}}:Reptiles|Category:{{{langcode}}}:Amphibians}}",
parents = {"zoology"},
}
labels["homeopathy"] = {
type = "related-to",
parents = {"alternative medicine"},
}
labels["horticulture"] = {
type = "related-to",
parents = {"agriculture", "botany"},
}
labels["hydroacoustics"] = {
type = "related-to",
description = "default no singularize",
parents = {"acoustics", "hydrology"},
}
labels["hydrocarbon chain prefixes"] = {
type = "set",
parents = {"organic chemistry"},
}
labels["hydrocarbon chain suffixes"] = {
type = "set",
parents = {"organic chemistry"},
}
labels["hydrology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[hydrology]], the study of [[water]], its movement and distribution.",
additional = "{{also|Category:{{{langcode}}}:Water}}",
parents = {"earth sciences", "water"},
}
labels["ichthyology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[ichthyology]], the study of [[fish]].",
additional = "{{also|Category:{{{langcode}}}:Fish}}",
parents = {"zoology"},
}
labels["immunochemistry"] = {
type = "related-to",
parents = {"biochemistry", "immunology"},
}
labels["immunology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[immunology]], the study of the [[immune system]].",
parents = {"medicine"},
}
labels["Indo-European studies"] = {
type = "related-to",
description = "default no singularize",
parents = {"linguistics"},
}
labels["information science"] = {
type = "related-to",
parents = {"applied sciences"},
}
labels["inorganic chemistry"] = {
type = "related-to",
parents = {"chemistry"},
}
labels["interdisciplinary fields"] = {
type = "related-to",
parents = {"sciences"},
}
labels["isolines"] = {
type = "related-to",
parents = {"cartography", "meteorology"},
}
labels["Lagrange points"] = {
type = "related-to",
parents = {"orbits"},
}
labels["lexicography"] = {
type = "related-to",
parents = {"linguistics"},
}
labels["library science"] = {
type = "related-to",
parents = {"sciences", "education", "information science"},
}
labels["lichenology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[lichenology]], the study of [[lichen]]s.",
additional = "{{also|Category:{{{langcode}}}:Lichens}}",
parents = {"mycology", "phycology"},
}
labels["linguistic morphology"] = {
type = "related-to",
parents = {"linguistics"},
}
labels["linguistics"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[linguistics]], the study and analysis of [[language]].",
additional = "{{also|Category:{{{langcode}}}:Language}}",
parents = {"language", "social sciences"},
}
labels["locksmithing"] = {
type = "related-to",
parents = {"technology"},
}
labels["maize (crop)"] = {
type = "related-to",
description = "=[[maize]] (called [[corn]] in North America) as a crop",
additional = "For terms related to maize as a food, see [[:Category:Maize (food)]] and for maize as a plant, see [[:Category:Maize (plant)]].",
parents = {"agriculture", "grains"},
}
labels["malacology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[malacology]], the study of [[mollusk]]s.",
additional = "{{also|Category:{{{langcode}}}:Mollusks}}",
parents = {"zoology"},
}
labels["mammalogy"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[mammalogy]], the study of [[mammal]]s.",
additional = "{{also|Category:{{{langcode}}}:Mammals}}",
parents = {"zoology"},
}
labels["marine biology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[marine biology]], the study of [[life]] in the [[sea]].",
parents = {"biology"},
}
labels["masonry"] = {
type = "related-to",
parents = {"construction"},
}
labels["materials science"] = {
type = "related-to",
parents = {"sciences", "engineering"},
}
labels["mechanical engineering"] = {
type = "related-to",
parents = {"engineering"},
}
labels["mechanics"] = {
type = "related-to",
description = "default no singularize",
parents = {"physics"},
}
labels["medical genetics"] = {
type = "related-to",
description = "default no singularize",
parents = {"medicine", "genetics"},
}
labels["medicine"] = {
type = "related-to",
description = "{{{langname}}} terms that pertain to the the science and practice of [[medicine]].",
parents = {"biology", "healthcare"},
}
labels["metamaterials"] = {
type = "related-to",
parents = {"physics"},
}
labels["physical constants"] = {
type = "set",
parents = {"physics"},
}
labels["machine learning"] = {
type = "related-to",
parents = {"artificial intelligence"},
}
labels["meteorology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[meteorology]], the study of [[weather]].",
additional = "{{also|Category:{{{langcode}}}:Weather}}",
parents = {"earth sciences", "atmosphere"},
}
labels["metrology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[metrology]], the science of [[measure|measuring]].",
parents = {"applied sciences"},
wpcat = true,
commonscat=true,
}
labels["microbiology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[microbiology]], the study of [[life]] at [[microscopic]] scales, too small for the [[human]] [[eye]].",
parents = {"biology"},
}
labels["microscopy"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[microscopy]], the field of using [[microscopes]] for study.",
parents = {"optics", "microbiology"},
}
labels["mineralogy"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[mineralogy]], the study of [[mineral]]s.",
additional = "{{also|Category:{{{langcode}}}:Minerals}}",
parents = {"geology"},
}
labels["molecular biology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[molecular biology]], the study of the [[molecules]] found in [[life]], their functions and effects.",
parents = {"biochemistry", "biology", "genetics"},
}
labels["mycology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[mycology]], the study of [[fungi]].",
additional = "{{also|Category:{{{langcode}}}:Fungi}}",
parents = {"biology"},
}
labels["NASA"] = {
type = "related-to",
description = "=the United States National Aeronautics and Space Administration and the US space program",
parents = {"astronautics", "aviation"},
}
labels["nephrology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[nephrology]], the study of the [[kidney]]s.",
parents = {"medicine"},
}
labels["neuroanatomy"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[neuroanatomy]], the study of the structure and organization of the [[nervous system]].",
parents = {"anatomy", "neurology", "neuroscience"},
}
labels["neurology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[neurology]], the study of [[nerves]] and [[neurons]].",
parents = {"medicine", "neuroscience"},
}
labels["neuroscience"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[neuroscience]], the study of the [[nervous system]].",
parents = {"biology"},
}
labels["neurosurgery"] = {
type = "related-to",
parents = {"surgery", "neurology"},
}
labels["neutron"] = {
type = "related-to",
parents = {"nuclear physics"},
}
labels["neutron stars"] = {
type = "related-to",
parents = {"neutron", "gravity", "astrophysics", "relativity", "stars"},
}
labels["nouns"] = {
type = "related-to",
parents = {"parts of speech"},
}
labels["nuclear energy"] = {
type = "related-to",
parents = {"energy", "nuclear physics", "technology"},
}
labels["nuclear fusion"] = {
type = "related-to",
parents = {"nuclear energy"},
}
labels["nuclear physics"] = {
type = "related-to",
description = "default no singularize",
parents = {"physics", "quantum mechanics"},
}
labels["nuclear rocketry"] = {
type = "related-to",
parents = {"nuclear energy", "rocketry", "space access"},
}
labels["obstetrics"] = {
type = "related-to",
description = "default no singularize",
parents = {"developmental biology", "medicine", "pregnancy"},
}
labels["obsolete scientific theories"] = {
type = "set",
description = "{{{langname}}} terms for types or instances of [[obsolete]] [[scientific]] [[theory|theories]] that are no longer, or have never been, widely accepted.",
parents = {"history of science"},
}
labels["oceanography"] = {
type = "related-to",
parents = {"sciences"},
}
labels["oenology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[oenology]], the study of [[wine]] and [[winemaking]].",
additional = "{{also|Category:{{{langcode}}}:Wine}}",
parents = {"sciences"},
}
labels["oncology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[oncology]], the study of [[cancer]].",
parents = {"biology", "medicine", "pathology"},
}
labels["onomastics"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[onomastics]], the study of [[name]]s.",
additional = "{{also|Category:{{{langcode}}}:Names|Category:{{{langname}}} names}}",
parents = {"linguistics", "names"},
}
labels["ophthalmology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[ophthalmology]], the study of the [[eye]].",
additional = "{{also|Category:{{{langcode}}}:Eye}}",
parents = {"medicine", "vision"},
}
labels["optics"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[optics]], the study of the behaviour of [[light]].",
additional = "{{also|Category:{{{langcode}}}:Light}}",
parents = {"physics"},
}
labels["orbits"] = {
type = "related-to",
parents = {"astronautics", "gravity", "space", "curves"},
}
labels["organic chemistry"] = {
type = "related-to",
parents = {"chemistry"},
}
labels["orgonomy"] = {
type = "related-to",
description = "=the [[orgone]] theory proposed by [[w:William Reich|William Reich]]",
parents = {"alternative medicine", "pseudoscience"},
}
labels["ornithology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[ornithology]], the study of [[birds]].",
additional = "{{also|Category:{{{langcode}}}:Birds}}",
parents = {"zoology"},
}
labels["paleontology"] = {
type = "related-to",
parents = {"sciences", "geology"},
}
labels["palynology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[palynology]], the study of [[particle]]s and particulate matter.",
parents = {"geology"},
}
labels["paranormal"] = {
type = "related-to",
description = "default with the",
parents = {"pseudoscience"},
}
labels["parapsychology"] = {
type = "related-to",
parents = {"paranormal", "pseudoscience"},
}
labels["parasitology"] = {
type = "related-to",
parents = {"pathology", "zoology"},
}
labels["particle physics"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[particle physics]], the study of [[subatomic]] [[particle]]s.",
additional = "{{also|Category:{{{langcode}}}:Subatomic particles}}",
parents = {"physics"},
}
labels["parts of speech"] = {
type = "related-to",
description = "=[[part of speech|parts of speech]]",
parents = {"grammar"},
}
labels["pathology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[pathology]], the study of [[disease]].",
additional = "{{also|Category:{{{langcode}}}:Disease}}",
parents = {"medicine", "disease"},
}
labels["petrochemistry"] = {
type = "related-to",
parents = {"chemistry", "chemical engineering", "oil industry", "petroleum"},
}
labels["petrology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[petrology]], the study of [[rock]].",
additional = "{{also|Category:{{{langcode}}}:Rocks}}",
parents = {"geology"},
}
labels["pharmacology"] = {
type = "related-to",
parents = {"biochemistry", "medicine"},
}
labels["pharmacy"] = {
type = "related-to",
parents = {"medicine", "pharmacology"},
}
labels["phonemes"] = {
type = "set",
description = "=contrastive sounds within a language",
parents = {"phonology"},
}
labels["phonetics"] = {
type = "related-to",
description = "default no singularize",
parents = {"linguistics"},
}
labels["phonology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[phonology]], the organisation of [[speech]] sounds within a [[language]].",
parents = {"linguistics"},
}
labels["photochemistry"] = {
type = "related-to",
parents = {"physical chemistry", "light"},
}
labels["phrenology"] = {
type = "related-to",
parents = {"psychology", "pseudoscience"},
}
labels["phycology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[phycology]], the study of [[algae]].",
additional = "{{also|Category:{{{langcode}}}:Algae}}",
parents = {"botany", "microbiology"},
}
labels["physical chemistry"] = {
type = "related-to",
parents = {"chemistry", "physics"},
}
labels["physical quantities"] = {
type = "set",
parents = {"physics"},
}
labels["physics"] = {
type = "related-to",
description = "default no singularize",
parents = {"sciences"},
commonscat = true,
wpcat = true,
}
labels["physiology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[physiology]].",
parents = {"biology", "medicine"},
}
labels["phytopathology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[phytopathology]], the study of [[disease]] in [[plant]]s.",
additional = "{{also|Category:{{{langcode}}}:Plant diseases}}",
parents = {"pathology", "botany"},
}
labels["planetary nomenclature"] = {
type = "related-to",
parents = {"planetology"},
}
labels["planetology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[planetology]], the study of [[planet]]s.",
additional = "{{also|Category:{{{langcode}}}:Planets}}",
parents = {"astronomy", "geology"},
}
labels["plant anatomy"] = {
type = "related-to",
description = "{{{langname}}} terms used in the study of the structure of plants and their parts.",
parents = {"botany", "anatomy"},
}
labels["plastic surgery"] = {
type = "related-to",
parents = {"surgery"},
}
labels["political science"] = {
type = "related-to",
parents = {"social sciences", "politics"},
}
labels["potential theory"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["pragmatics"] = {
type = "related-to",
description = "default no singularize",
parents = {"linguistics"},
}
labels["prosody"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[prosody]], the study of the [[suprasegmental]] aspects of [[speech]].",
parents = {"linguistics"},
}
labels["pseudoscience"] = {
type = "related-to",
parents = {"sciences"},
}
labels["psychiatry"] = {
type = "related-to",
parents = {"medicine"},
}
labels["psychoanalysis"] = {
type = "related-to",
description = "default no singularize",
parents = {"psychology"},
}
labels["psychology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[psychology]], the study of the [[mind]].",
additional = "{{also|Category:{{{langcode}}}:Mind}}",
parents = {"social sciences"},
}
labels["psychotherapy"] = {
type = "related-to",
parents = {"psychology"},
}
labels["pulmonology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[pulmonology]], the study of the [[respiratory system]], including the [[lung]]s.",
parents = {"medicine"},
}
labels["pyrotechnics"] = {
type = "related-to",
description = "default no singularize",
parents = {"sciences"},
}
labels["quantity"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["quantum mechanics"] = {
type = "related-to",
description = "default no singularize",
parents = {"physics"},
}
labels["relativity"] = {
type = "related-to",
parents = {"physics"},
}
labels["rheumatology"] = {
type = "related-to",
parents = {"medicine"},
}
labels["robotics"] = {
type = "related-to",
description = "default no singularize",
parents = {"engineering"},
}
labels["rocketry"] = {
type = "related-to",
parents = {"astronautics"},
}
labels["roofing"] = {
type = "related-to",
parents = {"construction"},
}
labels["rosiculture"] = {
type = "related-to",
parents = {"horticulture"},
}
labels["rotation"] = {
type = "related-to",
parents = {"mechanics", "geometry"},
}
labels["Russian space program"] = {
type = "related-to",
description = "=the Russian and Soviet space programs",
parents = {"astronautics", "aviation"},
}
labels["sales"] = {
type = "related-to",
description = "default no singularize",
parents = {"marketing"},
}
labels["seismology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[seismology]], the study of [[earthquake]]s.",
parents = {"geology"},
}
labels["semantics"] = {
type = "related-to",
description = "default no singularize",
parents = {"linguistics"},
}
labels["Semitic linguistics"] = {
type = "related-to",
description = "{{{langname}}} terms used in the study and analysis of [[Semitic]] [[language]]s.",
parents = {"linguistics"},
}
labels["semiotics"] = {
type = "related-to",
description = "default no singularize",
parents = {"social sciences", "linguistics"},
}
labels["sexology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[sexology]], the study of [[human]] [[sexuality]].",
additional = "{{also|Category:{{{langcode}}}:Sex}}",
parents = {"psychology", "sex", "sociology"},
}
labels["SI units"] = {
type = "related-to",
description = "=[[International System of Units|SI]] [[unit of measure|units of measure]]",
parents = {"units of measure"},
}
labels["signal processing"] = {
type = "related-to",
parents = {"applied mathematics", "telecommunications"},
wpcat = true,
commonscat = true,
}
labels["social sciences"] = {
type = "related-to",
description = "default with the",
parents = {"sciences", "society"},
}
labels["sociolinguistics"] = {
type = "related-to",
description = "default no singularize",
parents = {"linguistics", "sociology"},
}
labels["sociology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[sociology]], the study of [[society]].",
additional = "{{also|Category:{{{langcode}}}:Society}}",
parents = {"social sciences"},
}
labels["software engineering"] = {
type = "related-to",
parents = {"engineering", "computer science", "software"},
wpcat = true,
commonscat = true,
}
labels["soil science"] = {
type = "related-to",
parents = {"earth sciences"},
}
labels["sorting algorithms"] = {
type = "set",
parents = {"theory of computing"},
}
labels["sound engineering"] = {
type = "related-to",
parents = {"engineering", "sound"},
}
labels["space access"] = {
type = "related-to",
parents = {"astronautics"},
}
labels["space sciences"] = {
type = "related-to",
parents = {"sciences", "space"},
}
labels["spectroscopy"] = {
type = "related-to",
parents = {"analytical chemistry", "optics", "physics"},
}
labels["statistical mechanics"] = {
type = "related-to",
description = "default no singularize",
parents = {"mechanics"},
}
labels["statistics"] = {
type = "related-to",
description = "default no singularize",
parents = {"formal sciences", "mathematics"},
}
labels["surgery"] = {
type = "related-to",
parents = {"medicine"},
}
labels["surveying"] = {
type = "related-to",
parents = {"geography"},
}
labels["systems"] = {
type = "related-to",
parents = {"interdisciplinary fields", "sciences", "society"},
}
labels["systems engineering"] = {
type = "related-to",
parents = {"engineering"},
}
labels["systems theory"] = {
type = "related-to",
parents = {"sciences", "systems"},
}
labels["taxonomy"] = {
type = "related-to",
additional = "For individual [[taxa]], see [[:Category:Taxonomic names]].",
parents = {"biology", "evolutionary theory"},
}
labels["taxonomic ranks"] = {
type = "set",
parents = {"taxonomy"},
}
labels["tenses"] = {
type = "related-to",
parents = {"grammar"},
}
labels["teratology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[teratology]], the study of abnormalities in the [[development]] of the [[body]].",
parents = {"developmental biology", "medicine", "pathology", "toxicology"},
}
labels["theory of computing"] = {
type = "related-to",
description = "{{{langname}}} terms used in theoretical discussion of [[computer science]] or [[computing]].",
parents = {"computer science"},
}
labels["thermodynamics"] = {
type = "related-to",
description = "default no singularize",
parents = {"physics"},
}
labels["tides"] = {
type = "related-to",
description = "default no singularize",
parents = {"gravity", "periodic occurrences"},
}
labels["toxicology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[toxicology]], the study of [[poisons]], [[toxins]] and other substances with negative effects on the [[body]].",
additional = "{{also|Category:{{{langcode}}}:Poisons}}",
parents = {"medicine", "pharmacology"},
}
labels["traditional Chinese medicine"] = {
type = "related-to",
parents = {"alternative medicine"},
}
labels["traffic engineering"] = {
type = "related-to",
parents = {"engineering", "road transport"},
}
labels["translation studies"] = {
type = "related-to",
description = "default no singularize",
parents = {"linguistics"},
}
labels["traumatology"] = {
type = "related-to",
description = "{{{langname}}} terms used in traumatology, the study of wounds and injuries caused by accidents or violence to a person.",
parents = {"medicine", "pathology"},
}
labels["ufology"] = {
type = "related-to",
parents = {"paranormal", "anti-gravity", "celestial inhabitants"},
}
labels["units of area"] = {
type = "set",
description = "{{{langname}}} terms that are [[w:unit of area|units of area]].",
parents = {"units of measure"},
commonscat = true,
wpcat = true,
}
labels["units of energy"] = {
type = "set",
description = "{{{langname}}} terms that are [[w:unit of energy|units of energy]].",
parents = {"units of measure", "energy"},
commonscat = false,
wpcat = true,
}
labels["units of length"] = {
type = "set",
description = "{{{langname}}} terms that are [[w:unit of length|units of length]].",
parents = {"units of measure"},
commonscat = true,
wpcat = true,
}
labels["units of mass"] = {
type = "set",
description = "{{{langname}}} terms that are [[w:unit of mass|units of mass]].",
parents = {"units of measure"},
commonscat = true,
wpcat = true,
}
labels["units of measure"] = {
type = "set",
description = "{{{langname}}} terms that are [[unit of measure|units of measure]].",
parents = {"quantity", "metrology"},
commonscat = true,
wpcat = "units of measurement",
}
labels["units of time"] = {
type = "set",
description = "{{{langname}}} terms that are [[unit of time|units of time]].",
parents = {"units of measure", "time"},
commonscat = "spans of time",
wpcat = true,
}
labels["units of volume"] = {
type = "set",
description = "{{{langname}}} terms that are [[w:unit of volume|units of volume]].",
parents = {"units of measure"},
commonscat = true,
wpcat = true,
}
labels["urban studies"] = {
type = "related-to",
parents = {"applied sciences", "social sciences"},
}
labels["urology"] = {
type = "related-to",
parents = {"medicine"},
}
labels["verbs"] = {
type = "related-to",
parents = {"parts of speech"},
}
labels["veterinary medicine"] = {
type = "related-to",
parents = {"medicine", "animals"},
}
labels["virology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[virology]], the study of [[virus]]es.",
additional = "{{also|Category:{{{langcode}}}:Viruses}}",
parents = {"medicine", "microbiology", "pathology"},
}
labels["volcanology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[volcanology]], the study of [[volcano]]es.",
parents = {"geology"},
}
labels["zoology"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[zoology]], the study of [[animal]]s.",
additional = "{{also|Category:{{{langcode}}}:Animals}}",
parents = {"biology"},
}
labels["zymurgy"] = {
type = "related-to",
parents = {"biochemistry", "brewing"},
}
labels["colour and pattern morphs"] = {
type = "set",
description = "{{{langname}}} terms for [[colour]] and [[pattern]] [[morph#Etymology 2|morphs]]",
parents = {"zoology"},
}
labels["geological formations"] = {
type = "set",
description = "{{{langname}}} [[w:geological formation|geological formations]]",
parents = {"geology"},
}
return labels
2b0tabbxbsjqs255dvvkhwnjt8prxx1
Mòideal:category tree/topic/Mathematics
828
17902
88802
2026-07-23T19:36:33Z
en>WingerBot
0
explicit 'description = "default"' unnecessary; remove it (manually assisted)
88802
Scribunto
text/plain
local labels = {}
labels["formal sciences"] = {
type = "related-to",
description = "=the [[formal science]]s, the study of non-physical systems and abstractions",
parents = {"sciences"},
}
labels["mathematics"] = {
type = "related-to",
description = "default no singularize",
parents = {"formal sciences"},
}
labels["actuarial notation"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["algebra"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["algebraic geometry"] = {
type = "related-to",
parents = {"geometry", "algebra"},
}
labels["algebraic topology"] = {
type = "related-to",
parents = {"topology", "algebra"},
}
labels["analysis"] = {
type = "related-to",
description = "default no singularize",
parents = {"mathematics"},
}
labels["applied mathematics"] = {
type = "related-to",
description = "default no singularize",
parents = {"mathematics"},
}
labels["arithmetic"] = {
type = "related-to",
parents = {"mathematics", "applied sciences"},
}
labels["calculus"] = {
type = "related-to",
description = "default no singularize",
parents = {"mathematical analysis"},
}
labels["category theory"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["cellular automata"] = {
type = "related-to",
parents = {"mathematics", "theory of computing"},
}
labels["chaos theory"] = {
type = "related-to",
parents = {"systems theory", "calculus"},
}
labels["circle"] = {
type = "related-to",
parents = {"curves"},
}
labels["coding theory"] = {
type = "related-to",
parents = {"information theory"},
}
labels["combinatorics"] = {
type = "related-to",
description = "default no singularize",
parents = {"mathematics"},
}
labels["complex analysis"] = {
type = "related-to",
description = "default no singularize",
parents = {"mathematics"},
}
labels["curves"] = {
type = "related-to",
parents = {"shapes"},
}
labels["differential geometry"] = {
type = "related-to",
parents = {"geometry", "mathematical analysis"},
}
labels["functional analysis"] = {
type = "related-to",
description = "default no singularize",
parents = {"mathematical analysis"},
}
labels["functions"] = {
type = "related-to",
parents = {"algebra", "calculus", "geometry", "mathematical analysis"},
}
labels["fuzzy logic"] = {
type = "related-to",
parents = {"logic"},
}
labels["game theory"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["geometry"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["graph theory"] = {
type = "related-to",
parents = {"mathematics", "visualization"},
}
labels["group theory"] = {
type = "related-to",
parents = {"algebra"},
}
labels["higher-dimensional geometry"] = {
type = "related-to",
parents = {"geometry"},
}
labels["hyperbolic functions"] = {
type = "related-to",
parents = {"functions"},
}
labels["information theory"] = {
type = "related-to",
parents = {"applied mathematics"},
}
labels["linear algebra"] = {
type = "related-to",
parents = {"algebra"},
}
labels["logic"] = {
type = "related-to",
parents = {"formal sciences", "philosophy"},
}
labels["mathematical analysis"] = {
type = "related-to",
description = "default no singularize",
parents = {"mathematics"},
}
labels["measure theory"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["number theory"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["numerical analysis"] = {
type = "related-to",
description = "default no singularize",
parents = {"mathematical analysis"},
}
labels["order theory"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["polygons"] = {
type = "related-to",
parents = {"shapes"},
}
labels["polyhedra"] = {
type = "related-to",
description = "=[[polyhedron|polyhedra]], i.e. solid figures with straight edges and flat faces",
parents = {"shapes"},
}
labels["probability theory"] = {
type = "related-to",
parents = {"mathematical analysis"},
}
labels["ring theory"] = {
type = "related-to",
parents = {"algebra"},
}
labels["set theory"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["shapes"] = {
type = "related-to",
parents = {"geometry"},
}
labels["surfaces"] = {
type = "related-to",
description = "=geometric [[surface]]s",
parents = {"shapes"},
}
labels["topology"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["trigonometry"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["trigonometric functions"] = {
type = "related-to",
parents = {"functions", "trigonometry", "circle"},
}
return labels
8gutluqo514djwtharuf3yg3wu6971p
88803
88802
2026-08-06T20:14:33Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/topic/Mathematics]]
88802
Scribunto
text/plain
local labels = {}
labels["formal sciences"] = {
type = "related-to",
description = "=the [[formal science]]s, the study of non-physical systems and abstractions",
parents = {"sciences"},
}
labels["mathematics"] = {
type = "related-to",
description = "default no singularize",
parents = {"formal sciences"},
}
labels["actuarial notation"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["algebra"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["algebraic geometry"] = {
type = "related-to",
parents = {"geometry", "algebra"},
}
labels["algebraic topology"] = {
type = "related-to",
parents = {"topology", "algebra"},
}
labels["analysis"] = {
type = "related-to",
description = "default no singularize",
parents = {"mathematics"},
}
labels["applied mathematics"] = {
type = "related-to",
description = "default no singularize",
parents = {"mathematics"},
}
labels["arithmetic"] = {
type = "related-to",
parents = {"mathematics", "applied sciences"},
}
labels["calculus"] = {
type = "related-to",
description = "default no singularize",
parents = {"mathematical analysis"},
}
labels["category theory"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["cellular automata"] = {
type = "related-to",
parents = {"mathematics", "theory of computing"},
}
labels["chaos theory"] = {
type = "related-to",
parents = {"systems theory", "calculus"},
}
labels["circle"] = {
type = "related-to",
parents = {"curves"},
}
labels["coding theory"] = {
type = "related-to",
parents = {"information theory"},
}
labels["combinatorics"] = {
type = "related-to",
description = "default no singularize",
parents = {"mathematics"},
}
labels["complex analysis"] = {
type = "related-to",
description = "default no singularize",
parents = {"mathematics"},
}
labels["curves"] = {
type = "related-to",
parents = {"shapes"},
}
labels["differential geometry"] = {
type = "related-to",
parents = {"geometry", "mathematical analysis"},
}
labels["functional analysis"] = {
type = "related-to",
description = "default no singularize",
parents = {"mathematical analysis"},
}
labels["functions"] = {
type = "related-to",
parents = {"algebra", "calculus", "geometry", "mathematical analysis"},
}
labels["fuzzy logic"] = {
type = "related-to",
parents = {"logic"},
}
labels["game theory"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["geometry"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["graph theory"] = {
type = "related-to",
parents = {"mathematics", "visualization"},
}
labels["group theory"] = {
type = "related-to",
parents = {"algebra"},
}
labels["higher-dimensional geometry"] = {
type = "related-to",
parents = {"geometry"},
}
labels["hyperbolic functions"] = {
type = "related-to",
parents = {"functions"},
}
labels["information theory"] = {
type = "related-to",
parents = {"applied mathematics"},
}
labels["linear algebra"] = {
type = "related-to",
parents = {"algebra"},
}
labels["logic"] = {
type = "related-to",
parents = {"formal sciences", "philosophy"},
}
labels["mathematical analysis"] = {
type = "related-to",
description = "default no singularize",
parents = {"mathematics"},
}
labels["measure theory"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["number theory"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["numerical analysis"] = {
type = "related-to",
description = "default no singularize",
parents = {"mathematical analysis"},
}
labels["order theory"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["polygons"] = {
type = "related-to",
parents = {"shapes"},
}
labels["polyhedra"] = {
type = "related-to",
description = "=[[polyhedron|polyhedra]], i.e. solid figures with straight edges and flat faces",
parents = {"shapes"},
}
labels["probability theory"] = {
type = "related-to",
parents = {"mathematical analysis"},
}
labels["ring theory"] = {
type = "related-to",
parents = {"algebra"},
}
labels["set theory"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["shapes"] = {
type = "related-to",
parents = {"geometry"},
}
labels["surfaces"] = {
type = "related-to",
description = "=geometric [[surface]]s",
parents = {"shapes"},
}
labels["topology"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["trigonometry"] = {
type = "related-to",
parents = {"mathematics"},
}
labels["trigonometric functions"] = {
type = "related-to",
parents = {"functions", "trigonometry", "circle"},
}
return labels
8gutluqo514djwtharuf3yg3wu6971p
Mòideal:category tree/topic/Sex
828
17903
88804
2026-07-23T19:36:48Z
en>WingerBot
0
explicit 'description = "default"' unnecessary; remove it (manually assisted)
88804
Scribunto
text/plain
local labels = {}
labels["sex"] = {
type = "related-to",
description = "=[[sexual intercourse]] or [[sexuality]]",
additional = "For terms related to sex and gender, see [[:Category:Gender]].",
parents = {"all topics", "reproduction"},
}
labels["ABDL"] = {
type = "related-to",
description = "=the [[ABDL]] ([[adult baby]]/[[diaper lover]]) community",
parents = {"paraphilias", "diapers"},
}
labels["BDSM"] = {
type = "related-to",
parents = {"sexuality"},
}
labels["birth control"] = {
type = "related-to",
parents = {"sex", "pregnancy"},
}
labels["incest"] = {
type = "related-to",
parents = {"sex"},
}
labels["LGBTQ"] = {
type = "related-to",
description = "=[[LGBTQ]] ([[lesbian]], [[gay]], [[bisexual]], [[transgender]], [[queer]]/[[questioning]])",
parents = {"sexuality"}, -- for the T 'gender' should perhaps be a parent, but then nesting gets circularish
}
labels["masturbation"] = {
type = "related-to",
parents = {"sex"},
}
labels["pedophilia"] = {
type = "related-to",
parents = {"paraphilias"},
}
labels["paraphilias"] = {
type = "set",
parents = {"sexuality", "sexology", "philias"},
}
labels["polyamory"] = {
type = "related-to",
parents = {"love", "romantic orientations", "sexual orientations"},
}
labels["pornography"] = {
type = "related-to",
parents = {"sex"},
}
labels["Japanese pornography"] = {
type = "related-to",
parents = {"pornography", "Japanese fiction"},
}
labels["prostitution"] = {
type = "related-to",
parents = {"sexuality"},
}
labels["seduction community"] = {
type = "related-to",
description = "default with the wikify",
parents = {"masculism", "sex"},
}
labels["sex positions"] = {
type = "set",
parents = {"sex"},
}
labels["sexuality"] = {
type = "related-to",
parents = {"sex", "human behaviour"},
}
labels["sexual orientations"] = {
type = "set",
parents = {"sexuality", "LGBTQ", "love"},
}
labels["romantic orientations"] = {
type = "set",
parents = {"sexuality", "LGBTQ", "love"},
}
labels["vorarephilia"] = {
type = "related-to",
parents = {"paraphilias"},
}
return labels
byzge5ammo3til1g7mbg9g4dtmljaz4
88805
88804
2026-08-06T20:14:47Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/topic/Sex]]
88804
Scribunto
text/plain
local labels = {}
labels["sex"] = {
type = "related-to",
description = "=[[sexual intercourse]] or [[sexuality]]",
additional = "For terms related to sex and gender, see [[:Category:Gender]].",
parents = {"all topics", "reproduction"},
}
labels["ABDL"] = {
type = "related-to",
description = "=the [[ABDL]] ([[adult baby]]/[[diaper lover]]) community",
parents = {"paraphilias", "diapers"},
}
labels["BDSM"] = {
type = "related-to",
parents = {"sexuality"},
}
labels["birth control"] = {
type = "related-to",
parents = {"sex", "pregnancy"},
}
labels["incest"] = {
type = "related-to",
parents = {"sex"},
}
labels["LGBTQ"] = {
type = "related-to",
description = "=[[LGBTQ]] ([[lesbian]], [[gay]], [[bisexual]], [[transgender]], [[queer]]/[[questioning]])",
parents = {"sexuality"}, -- for the T 'gender' should perhaps be a parent, but then nesting gets circularish
}
labels["masturbation"] = {
type = "related-to",
parents = {"sex"},
}
labels["pedophilia"] = {
type = "related-to",
parents = {"paraphilias"},
}
labels["paraphilias"] = {
type = "set",
parents = {"sexuality", "sexology", "philias"},
}
labels["polyamory"] = {
type = "related-to",
parents = {"love", "romantic orientations", "sexual orientations"},
}
labels["pornography"] = {
type = "related-to",
parents = {"sex"},
}
labels["Japanese pornography"] = {
type = "related-to",
parents = {"pornography", "Japanese fiction"},
}
labels["prostitution"] = {
type = "related-to",
parents = {"sexuality"},
}
labels["seduction community"] = {
type = "related-to",
description = "default with the wikify",
parents = {"masculism", "sex"},
}
labels["sex positions"] = {
type = "set",
parents = {"sex"},
}
labels["sexuality"] = {
type = "related-to",
parents = {"sex", "human behaviour"},
}
labels["sexual orientations"] = {
type = "set",
parents = {"sexuality", "LGBTQ", "love"},
}
labels["romantic orientations"] = {
type = "set",
parents = {"sexuality", "LGBTQ", "love"},
}
labels["vorarephilia"] = {
type = "related-to",
parents = {"paraphilias"},
}
return labels
byzge5ammo3til1g7mbg9g4dtmljaz4
Mòideal:category tree/topic/Society
828
17904
88806
2026-07-23T19:36:49Z
en>WingerBot
0
explicit 'description = "default"' unnecessary; remove it (manually assisted)
88806
Scribunto
text/plain
local labels = {}
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
labels["society"] = {
type = "related-to",
parents = {"all topics"},
}
labels["academic degrees"] = {
type = "name",
parents = {"education"},
}
labels["academic grades"] = {
type = "set",
parents = {"education"},
}
labels["accounting"] = {
type = "related-to",
parents = {"finance"},
}
labels["administrative divisions"] = {
type = "set",
parents = {"government"},
}
labels["advertising"] = {
type = "related-to",
parents = {"business", "marketing"},
}
labels["alt-right"] = {
type = "related-to",
description = "=the [[alt-right]], a loosely connected [[far-right]], [[white nationalist]] movement",
parents = {"conservatism", "fascism", "ideologies", "white supremacist ideology"},
}
labels["anarchism"] = {
type = "related-to",
parents = {"ideologies", "leftism"},
}
labels["anti-Semitism"] = {
type = "related-to",
parents = {"forms of discrimination"},
}
labels["awards"] = {
type = "name,type",
parents = {"society"},
}
labels["banking"] = {
type = "related-to",
parents = {"finance", "industries"},
}
labels["bars"] = {
type = "type",
parents = {"businesses", "drinking"},
}
labels["Basque nationalism"] = {
type = "related-to",
parents = {"Basque Country, Spain", "nationalism"},
}
labels["bedding"] = {
type = "related-to",
parents = {"home"},
}
labels["blacksmithing"] = {
type = "related-to",
parents = {"metalworking"},
}
labels["bond market"] = {
type = "related-to",
description = "default with the",
parents = {"finance"},
}
labels["bookbinding"] = {
type = "related-to",
parents = {"publishing"},
}
labels["book sizes"] = {
type = "name",
parents = {"bookbinding"},
}
labels["Brexit"] = {
type = "related-to",
description = "={{w|Brexit}}, i.e. the withdrawal of the {{w|United Kingdom}} from the {{w|European Union}}",
parents = {"nationalism", "EU politics", "UK politics"},
}
labels["burial"] = {
type = "related-to",
parents = {"society", "death"},
}
labels["business"] = {
type = "related-to",
parents = {"economics", "society"},
}
labels["businesses"] = {
type = "type",
description = "=[[business]]es (specific commercial enterprises or establishments)",
parents = {"business"},
}
labels["capitalism"] = {
type = "related-to",
parents = {"economics", "ideologies"},
}
labels["chairs"] = {
type = "related-to",
parents = {"furniture", "sitting"},
}
labels["child abuse"] = {
type = "related-to",
parents = {"crime", "children", "violence"},
}
labels["Chinese restaurants"] = {
type = "related-to",
breadcrumb = "Chinese",
parents = {"restaurants", "China"},
}
labels["cleaning"] = {
type = "related-to",
parents = {"home"},
}
labels["coins"] = {
type = "set,related-to",
parents = {"money"},
}
labels["conservatism"] = {
type = "related-to",
description = "=[[conservatism]] or [[traditionalist]] beliefs",
parents = {"ideologies"},
}
labels["commerce"] = {
type = "related-to",
parents = {"business"},
}
labels["commercial documents"] = {
type = "set",
parents = {"commerce"},
}
labels["commercial law"] = {
type = "related-to",
breacrumb = "commercial",
parents = {"law", "commerce"},
}
labels["competition law"] = {
type = "related-to",
breacrumb = "competition",
parents = {"law"},
}
labels["antitrust law"] = {
breacrumb = "antitrust",
parents = {"competition law"},
}
labels["law of unfair competition"] = {
description = "default with the",
breacrumb = "unfair",
parents = {{name = "competition law", sort = "unfair"}},
}
labels["communism"] = {
type = "related-to",
parents = {"ideologies", "socialism", "leftism"},
}
labels["constitutional law"] = {
type = "related-to",
breadcrumb = "constitutional",
parents = {"law"},
}
labels["copyright"] = {
type = "related-to",
parents = {"intellectual property"},
}
labels["copyright licenses"] = {
type = "name",
description = "=[[license]]s of [[copyright]]",
breadcrumb_and_first_sort_key = "licenses",
parents = {"copyright"},
}
labels["corporate law"] = {
type = "related-to",
breadcrumb = "corporate",
parents = {"law"},
}
labels["corruption"] = {
type = "related-to",
parents = {"crime", "politics"},
}
labels["crafts"] = {
type = "type",
parents = {"society"},
}
labels["crime"] = {
type = "related-to",
parents = {"society", "criminal law"},
}
labels["crime prevention"] = {
type = "related-to",
parents = {"public safety", "crime"},
}
labels["criminal law"] = {
type = "related-to",
breadcrumb = "criminal",
parents = {"law"},
}
labels["crochet"] = {
type = "related-to",
parents = {"crafts"},
}
labels["cryptocurrency"] = {
type = "related-to",
parents = {"currency", "cryptography", "technology"},
}
labels["currencies"] = {
type = "set",
parents = {"money", "currency"},
}
labels["currency"] = {
type = "related-to",
parents = {"money"},
}
labels["dairy farming"] = {
type = "related-to",
parents = {"agriculture", "industries"},
}
labels["democracy"] = {
type = "related-to",
parents = {"forms of government"},
}
labels["diplomacy"] = {
type = "related-to",
parents = {"society"},
}
labels["discrimination"] = {
type = "related-to",
parents = {"society"},
}
labels["drug trafficking"] = {
type = "related-to",
parents = {"crime", "drugs"},
}
labels["education"] = {
type = "related-to",
parents = {"society"},
}
labels["emergency services"] = {
type = "related-to",
parents = {"public safety"},
}
labels["employment"] = {
type = "related-to",
parents = {"business", "work"},
}
labels["espionage"] = {
type = "related-to",
parents = {"security", "deception", "secrecy"},
}
labels["evil"] = {
type = "related-to",
parents = {"ethics", "religion"},
}
labels["fame"] = {
type = "related-to",
parents = {"society", "knowledge"},
}
labels["family law"] = {
type = "related-to",
breadcrumb = "family",
parents = {"law"},
}
labels["fascism"] = {
type = "related-to",
parents = {"ideologies"},
}
labels["farriery"] = {
type = "related-to",
parents = {"blacksmithing", "horses"},
}
labels["feminism"] = {
type = "related-to",
parents = {"gender", "female", "ideologies", "society", "sociology"},
}
labels["feudalism"] = {
type = "related-to",
parents = {"forms of government"},
}
labels["finance"] = {
type = "related-to",
parents = {"business"},
}
labels["firefighting"] = {
type = "related-to",
parents = {"emergency services", "fire"},
}
labels["forms of discrimination"] = {
type = "type",
description = "{{{langname}}} terms for [[form]]s of [[discrimination]].",
additional = "{{also|Category:{{{langcode}}}:Biases|Category:{{{langcode}}}:Conspiracy theories|Category:{{{langcode}}}:Ideologies}}",
breadcrumb = "forms",
parents = {"discrimination"},
}
labels["forms of government"] = {
type = "type",
description = "{{{langname}}} terms for [[form]]s of [[government]].",
breadcrumb = "forms",
parents = {"government"},
}
labels["freedom"] = {
type = "related-to",
parents = {"society"},
}
labels["freedom of speech"] = {
type = "related-to",
breadcrumb = "speech",
parents = {{name = "freedom", sort = "speech"}, "law"},
}
labels["freemasonry"] = {
type = "related-to",
parents = {"organizations"},
}
labels["funeral"] = {
type = "related-to",
parents = {"society", "death", "industries"},
}
labels["furniture"] = {
type = "related-to",
parents = {"home"},
commonscat = true,
wpcat = true,
}
labels["gender-critical feminism"] = {
type = "related-to",
breadcrumb = "gender-critical",
parents = {"feminism", "gender", "transphobia"},
}
labels["glassblowing"] = {
type = "related-to",
parents = {"crafts", "glass"},
}
labels["good"] = {
type = "related-to",
parents = {"ethics", "religion"},
}
labels["government"] = {
type = "related-to",
parents = {"society", "politics"},
}
labels["hairdressing"] = {
type = "related-to",
parents = {"hair", "crafts"},
}
labels["high society"] = {
type = "related-to",
description = "=royalty and nobility",
parents = {"society"},
}
labels["Hindutva"] = {
type = "related-to",
description = "=[[Hindutva]] or {{w|Hindu nationalism}}",
parents = {"conservatism", "Hinduism", "ideologies", "Indian politics", "nationalism", "theocracy"},
}
labels["historical currencies"] = {
type = "set",
breadcrumb = "historical",
parents = {"currencies"},
}
labels["home"] = {
type = "related-to",
description = "default with the",
parents = {"society"},
}
labels["homophobia"] = {
type = "related-to",
parents = {"queerphobia"},
}
labels["hospitality"] = {
type = "related-to",
parents = {"business"},
}
labels["host industry"] = {
type = "related-to",
parents = {"hospitality", "businesses"},
}
labels["hotels"] = {
type = "type",
parents = {"businesses", "tourism", "hospitality"},
}
labels["household"] = {
type = "related-to",
parents = {"home"},
}
labels["housing"] = {
type = "related-to",
parents = {"home", "buildings"},
}
labels["human resources"] = {
type = "related-to",
description = "default no singularize",
parents = {"business", "sociology"},
}
labels["ideologies"] = {
type = "related-to",
parents = {"society", "politics"},
}
labels["imperialism"] = {
type = "related-to",
parents = {"ideologies"},
}
labels["import/export"] = {
type = "related-to",
description = "=[[import]]s and [[export]]s",
parents = {"trading", "transport"},
}
labels["incel community"] = {
type = "related-to",
description = "=the [[incel]] community",
parents = {"masculism", "sex"},
}
labels["incoterms"] = {
type = "related-to",
description = "=[[Incoterm]]s",
parents = {"business", "import/export"},
}
labels["industries"] = {
type = "related-to",
parents = {"business"},
}
labels["inheritance law"] = {
type = "related-to",
breadcrumb = "inheritance",
parents = {"law"},
}
labels["insurance"] = {
type = "related-to",
parents = {"finance", "industries"},
}
labels["intellectual property"] = {
type = "related-to",
description = "=[[intellectual property]] [[law]]",
parents = {"law"},
}
labels["international law"] = {
type = "related-to",
breadcrumb = "international",
parents = {"law"},
}
labels["international relations"] = {
type = "related-to",
description = "default wikify",
parents = {"politics", "Earth"},
}
labels["Islamic finance"] = {
type = "related-to",
description = "default wikify",
breadcrumb = "Islamic",
parents = {"finance", "banking", "Islam"},
}
labels["Islamic law"] = {
type = "related-to",
description = "default wikify",
breadcrumb = "law",
parents = {{name = "Islam", sort = "law"}, "law"},
}
labels["Islamism"] = {
type = "related-to",
parents = {"ideologies", "conservatism", "Islam", "theocracy"},
}
labels["Juche"] = {
type = "related-to",
parents = {"North Korea", "communism", "nationalism"},
}
labels["justice"] = {
type = "related-to",
parents = {"society"},
}
labels["Kentucky Fried Chicken"] = {
type = "related-to",
description = "=the {{w|Kentucky Fried Chicken}} [[chain]] of [[fast-food]] [[restaurant]]s",
parents = {"restaurants"},
}
labels["knitting"] = {
type = "related-to",
parents = {"crafts"},
}
labels["Ku Klux Klan"] = {
type = "related-to",
description = "default with the",
parents = {"organizations", "white supremacist ideology"},
}
labels["kyabakura industry"] = {
type = "related-to",
parents = {"hospitality", "businesses"},
}
labels["labour"] = {
type = "related-to",
description = "=[[labour]] or the {{w|labour movement}}",
parents = {"work", "leftism"},
}
labels["laundry"] = {
type = "related-to",
parents = {"cleaning"},
}
labels["law"] = {
type = "related-to",
description = "=the [[science]] and [[practice]] of [[law]]",
parents = {"justice"},
}
labels["law enforcement"] = {
type = "related-to",
parents = {"crime prevention", "emergency services", "law"},
}
labels["law of obligations"] = {
type = "related-to",
description = "default with the no singularize",
breadcrumb = "obligations",
parents = {{name = "law", sort = "obligations"}},
}
labels["leatherworking"] = {
type = "related-to",
parents = {"crafts"},
}
labels["leftism"] = {
type = "related-to",
parents = {"ideologies"},
}
labels["liberalism"] = {
type = "related-to",
parents = {"ideologies"},
}
labels["libertarianism"] = {
type = "related-to",
parents = {"ideologies"},
}
labels["logistics"] = {
type = "related-to",
description = "default no singularize",
parents = {"operations"},
}
labels["management"] = {
type = "related-to",
parents = {"business"},
}
labels["Maoism"] = {
type = "related-to",
parents = {"ideologies", "communism", "Marxism"},
}
labels["marketing"] = {
type = "related-to",
parents = {"business"},
}
labels["Marxism"] = {
type = "related-to",
parents = {"ideologies", "socialism"},
}
labels["masculism"] = {
type = "related-to",
parents = {"ideologies", "male"},
}
labels["metalworking"] = {
type = "related-to",
parents = {"crafts", "metallurgy"},
}
labels["McDonald's"] = {
type = "related-to",
description = "=the {{w|McDonald's}} [[chain]] of [[fast-food]] [[restaurant]]s",
parents = {"restaurants"},
}
labels["micronationalism"] = {
type = "related-to",
parents = {"forms of government", "ideologies"},
}
labels["military"] = {
type = "related-to",
description = "default with the",
parents = {"society"},
}
labels["military units"] = {
type = "related-to",
parents = {"military", "occupations"},
}
labels["mining"] = {
type = "related-to",
parents = {"industries"},
}
labels["monarchism"] = {
type = "related-to",
parents = {"ideologies", "monarchy"},
}
labels["monarchy"] = {
type = "related-to",
parents = {"forms of government", "high society"},
}
labels["money"] = {
type = "related-to",
parents = {"business"},
}
labels["museums"] = {
type = "related-to",
parents = {"businesses", "tourism", "art"},
}
labels["nationalism"] = {
type = "related-to",
parents = {"ideologies"},
}
labels["Nazism"] = {
type = "related-to",
parents = {"fascism", "white supremacist ideology", "ideologies"},
}
labels["neo-Nazism"] = { -- Adjacent to Nazism, but not quite the same thing.
type = "related-to",
parents = {"Nazism", "fascism", "white supremacist ideology", "ideologies"},
}
labels["Nobel Prize"] = {
type = "related-to",
description = "default with the",
parents = {"awards"},
}
labels["Objectivism"] = {
type = "related-to",
description = "=the political philosophy of {{w|Objectivism}} developed by {{w|Ayn Rand}}",
parents = {"ideologies", "libertarianism"},
}
labels["offices"] = {
type = "type",
description = "=offices, in the sense \"position of responsibility of some authority within an organisation\"",
parents = {"government"},
}
labels["oil industry"] = {
type = "related-to",
description = "default with the",
breadcrumb = "oil",
parents = {"industries", "petroleum"},
}
labels["operations"] = {
type = "related-to",
description = "{{{langname}}} terms covering all operational matters in [[production]], [[logistics]], or [[services]].",
parents = {"management", "systems theory"},
}
labels["organizations"] = {
type = "name",
parents = {"society"},
}
labels["papermaking"] = {
type = "related-to",
parents = {"crafts", "industries"},
}
labels["patent law"] = {
type = "related-to",
breadcrumb = "patent",
parents = {"law"},
}
labels["peace"] = {
type = "related-to",
parents = {"security"},
}
labels["pensions"] = {
type = "related-to",
parents = {"finance"},
}
labels["philanthropy"] = {
type = "related-to",
parents = {"society"},
}
labels["Philmont Scout Ranch"] = {
type = "related-to",
description = "={{w|Philmont Scout Ranch}}, a Scouting ranch in the United States",
parents = {"Scouting"},
}
labels["piracy"] = {
type = "related-to",
parents = {"crime", "nautical"},
}
labels["politics"] = {
type = "related-to",
description = "default no singularize",
parents = {"society"},
}
labels["poverty"] = {
type = "related-to",
parents = {"wealth"},
}
for _, country_demonym in ipairs {
{"Argentina", "Argentine"},
{"Australia", "Australian"},
{"Bangladesh", "Bangladeshi"},
{"Brazil", "Brazilian"},
{"Bulgaria", "Bulgarian"},
{"Canada", "Canadian"},
{"Chile", "Chilean"},
{"China", "Chinese"},
{"Europe", "European"},
{"European Union", "EU", "the European Union", "European politics"},
{"France", "French", nil, "European politics"},
{"Germany", "German", nil, "European politics"},
{"Hong Kong", "Hong Kong"},
{"Hungary", "Hungarian", nil, "European politics"},
{"India", "Indian"},
{"Indonesia", "Indonesian"},
{"Ireland", "Irish", nil, "European politics"},
{"Japan", "Japanese"},
{"Malaysia", "Malaysian"},
{"Mexico", "Mexican"},
{"New Zealand", "New Zealand"},
{"Nigeria", "Nigerian"},
{"Pakistan", "Pakistani"},
{"Palestine", "Palestinian"},
{"Peru", "Peruvian"},
{"Philippines", "Philippine", "the Philippines"},
{"Poland", "Polish", nil, "European politics"},
{"Portugal", "Portuguese", nil, "European politics"},
{"Russia", "Russian"},
{"Singapore", "Singaporean"},
{"South Africa", "South African"},
{"South Korea", "South Korean"},
{"Spain", "Spanish", nil, "European politics"},
{"Switzerland", "Swiss", nil, "European politics"},
{"Taiwan", "Taiwanese"},
{"Ukraine", "Ukrainian"},
{"United Kingdom", "UK", "the United Kingdom", "European politics"},
{"United States", "US", "the United States"},
{"Venezuela", "Venezuelan"},
{"Vietnam", "Vietnamese"},
} do
local country, demonym, full_country, parent = unpack(country_demonym)
labels[demonym .. " politics"] = {
type = "related-to",
description = ("=the {{w|politics of %s}}"):format(full_country or country),
parents = {parent or "politics", country},
}
end
labels["printing"] = {
type = "related-to",
parents = {"industries"},
}
labels["prison"] = {
type = "related-to",
parents = {"law enforcement", "buildings"},
}
labels["procedural law"] = {
type = "related-to",
breadcrumb = "procedural",
parents = {"law"},
}
labels["property law"] = {
type = "related-to",
breadcrumb = "property",
parents = {"law"},
}
labels["public administration"] = {
type = "related-to",
description = "=the field of [[public]] [[administration]]",
parents = {"government"},
}
labels["public safety"] = {
type = "related-to",
description = "=the field of [[public]] [[safety]]",
parents = {"public administration", "security"},
}
labels["publishing"] = {
type = "related-to",
parents = {"industries", "mass media"},
}
labels["QAnon"] = {
type = "related-to",
description = "=the [[QAnon]] movement",
parents = {"alt-right", "conspiracy theories", "Donald Trump", "pedophilia"},
}
labels["queerphobia"] = {
type = "related-to",
parents = {"forms of discrimination", "LGBTQ"},
}
labels["racism"] = {
type = "related-to",
parents = {"forms of discrimination"},
}
labels["rape"] = {
type = "related-to",
description = "=the field of [[sexual violence]]",
parents = {"sex", "crime", "violence"},
}
labels["real estate"] = {
type = "related-to",
parents = {"industries", "housing"},
}
labels["restaurants"] = {
type = "related-to",
description = "=[[restaurant]]s (including [[pub]]s, [[café]]s etc.)",
parents = {"businesses", "food and drink"},
}
labels["royal residences"] = {
type = "related-to",
parents = {"housing", "monarchy"},
}
labels["schools"] = {
type = "related-to",
parents = {"education", "buildings"},
}
-- Note: this is the usual term, not "Scottish law".
labels["Scots law"] = {
type = "related-to",
breadcrumb = "Scots",
parents = {"law", "Scotland"},
}
labels["Scouting"] = {
type = "related-to",
parents = {"society"},
}
labels["security"] = {
type = "related-to",
parents = {"society"},
}
labels["sexism"] = {
type = "related-to",
parents = {"forms of discrimination", "gender"},
}
labels["sewing"] = {
type = "related-to",
description = "=[[sewing]], sewing tools, sewing [[technique]]s and so on",
parents = {"crafts"},
}
labels["shoemaking"] = {
type = "related-to",
parents = {"crafts"},
}
labels["slavery"] = {
type = "related-to",
parents = {"society", "work"},
}
labels["socialism"] = {
type = "related-to",
parents = {"economics", "ideologies", "leftism"},
}
labels["social justice"] = {
type = "related-to",
parents = {"politics", "society", "sociology", "leftism"},
}
labels["social security"] = {
type = "related-to",
parents = {"government", "law", "money"},
}
labels["spinning"] = {
type = "related-to",
description = "=[[spinning]], the process of making [[yarn]] or [[string]] from raw [[fiber]]",
parents = {"crafts"},
}
labels["square dancing"] = {
type = "related-to",
parents = {"dance"},
}
labels["standards of identity"] = {
type = "related-to",
parents = {"law", "food and drink"},
}
labels["stock market"] = {
type = "related-to",
description = "default with the",
parents = {"finance"},
}
labels["stock symbols for companies"] = {
type = "name",
description = "=[[stock symbol]]s for [[company|companies]]",
parents = {"trading"},
}
labels["supply chain"] = {
type = "related-to",
description = "default no singularize",
parents = {"operations"},
}
labels["taxation"] = {
type = "related-to",
parents = {"government", "law", "money"},
}
labels["theft"] = {
type = "related-to",
parents = {"crime"},
}
labels["theocracy"] = {
type = "related-to",
parents = {"ideologies", "religion"},
}
labels["timber industry"] = {
type = "related-to",
description = "default with the",
breadcrumb = "timber",
parents = {"industries"},
}
labels["trademark"] = {
type = "related-to",
description = "=[[trademark]] [[law]]",
parents = {"intellectual property"},
}
labels["trading"] = {
type = "related-to",
parents = {"business"},
}
labels["transphobia"] = {
type = "related-to",
parents = {"forms of discrimination", "transgender"},
}
labels["trust"] = {
type = "related-to",
parents = {"security"},
}
labels["types of settlements"] = {
type = "type",
topic = "settlements",
description = "=[[settlement]]s",
parents = {"government"},
}
labels["United Nations"] = {
type = "related-to",
description = "=the [[United Nations Organization]]",
parents = {"organizations"},
}
labels["universities"] = {
type = "related-to",
parents = {"schools"},
}
labels["voting systems"] = {
type = "related-to",
parents = {"democracy", "systems"},
}
labels["wealth"] = {
type = "related-to",
parents = {"economics"},
}
labels["weaving"] = {
type = "related-to",
parents = {"crafts"},
}
labels["white supremacist ideology"] = {
type = "related-to",
parents = {"racism", "anti-Semitism", "ideologies"},
}
labels["woodworking"] = {
type = "related-to",
parents = {"crafts"},
}
labels["Zionism"] = {
type = "related-to",
parents = {"ideologies", "Judaism", "Israel", "nationalism"},
}
return labels
snpr4mk3ymmjcvcyb5ttksd00pkkk8r
88807
88806
2026-08-06T20:15:11Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/topic/Society]]
88806
Scribunto
text/plain
local labels = {}
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
labels["society"] = {
type = "related-to",
parents = {"all topics"},
}
labels["academic degrees"] = {
type = "name",
parents = {"education"},
}
labels["academic grades"] = {
type = "set",
parents = {"education"},
}
labels["accounting"] = {
type = "related-to",
parents = {"finance"},
}
labels["administrative divisions"] = {
type = "set",
parents = {"government"},
}
labels["advertising"] = {
type = "related-to",
parents = {"business", "marketing"},
}
labels["alt-right"] = {
type = "related-to",
description = "=the [[alt-right]], a loosely connected [[far-right]], [[white nationalist]] movement",
parents = {"conservatism", "fascism", "ideologies", "white supremacist ideology"},
}
labels["anarchism"] = {
type = "related-to",
parents = {"ideologies", "leftism"},
}
labels["anti-Semitism"] = {
type = "related-to",
parents = {"forms of discrimination"},
}
labels["awards"] = {
type = "name,type",
parents = {"society"},
}
labels["banking"] = {
type = "related-to",
parents = {"finance", "industries"},
}
labels["bars"] = {
type = "type",
parents = {"businesses", "drinking"},
}
labels["Basque nationalism"] = {
type = "related-to",
parents = {"Basque Country, Spain", "nationalism"},
}
labels["bedding"] = {
type = "related-to",
parents = {"home"},
}
labels["blacksmithing"] = {
type = "related-to",
parents = {"metalworking"},
}
labels["bond market"] = {
type = "related-to",
description = "default with the",
parents = {"finance"},
}
labels["bookbinding"] = {
type = "related-to",
parents = {"publishing"},
}
labels["book sizes"] = {
type = "name",
parents = {"bookbinding"},
}
labels["Brexit"] = {
type = "related-to",
description = "={{w|Brexit}}, i.e. the withdrawal of the {{w|United Kingdom}} from the {{w|European Union}}",
parents = {"nationalism", "EU politics", "UK politics"},
}
labels["burial"] = {
type = "related-to",
parents = {"society", "death"},
}
labels["business"] = {
type = "related-to",
parents = {"economics", "society"},
}
labels["businesses"] = {
type = "type",
description = "=[[business]]es (specific commercial enterprises or establishments)",
parents = {"business"},
}
labels["capitalism"] = {
type = "related-to",
parents = {"economics", "ideologies"},
}
labels["chairs"] = {
type = "related-to",
parents = {"furniture", "sitting"},
}
labels["child abuse"] = {
type = "related-to",
parents = {"crime", "children", "violence"},
}
labels["Chinese restaurants"] = {
type = "related-to",
breadcrumb = "Chinese",
parents = {"restaurants", "China"},
}
labels["cleaning"] = {
type = "related-to",
parents = {"home"},
}
labels["coins"] = {
type = "set,related-to",
parents = {"money"},
}
labels["conservatism"] = {
type = "related-to",
description = "=[[conservatism]] or [[traditionalist]] beliefs",
parents = {"ideologies"},
}
labels["commerce"] = {
type = "related-to",
parents = {"business"},
}
labels["commercial documents"] = {
type = "set",
parents = {"commerce"},
}
labels["commercial law"] = {
type = "related-to",
breacrumb = "commercial",
parents = {"law", "commerce"},
}
labels["competition law"] = {
type = "related-to",
breacrumb = "competition",
parents = {"law"},
}
labels["antitrust law"] = {
breacrumb = "antitrust",
parents = {"competition law"},
}
labels["law of unfair competition"] = {
description = "default with the",
breacrumb = "unfair",
parents = {{name = "competition law", sort = "unfair"}},
}
labels["communism"] = {
type = "related-to",
parents = {"ideologies", "socialism", "leftism"},
}
labels["constitutional law"] = {
type = "related-to",
breadcrumb = "constitutional",
parents = {"law"},
}
labels["copyright"] = {
type = "related-to",
parents = {"intellectual property"},
}
labels["copyright licenses"] = {
type = "name",
description = "=[[license]]s of [[copyright]]",
breadcrumb_and_first_sort_key = "licenses",
parents = {"copyright"},
}
labels["corporate law"] = {
type = "related-to",
breadcrumb = "corporate",
parents = {"law"},
}
labels["corruption"] = {
type = "related-to",
parents = {"crime", "politics"},
}
labels["crafts"] = {
type = "type",
parents = {"society"},
}
labels["crime"] = {
type = "related-to",
parents = {"society", "criminal law"},
}
labels["crime prevention"] = {
type = "related-to",
parents = {"public safety", "crime"},
}
labels["criminal law"] = {
type = "related-to",
breadcrumb = "criminal",
parents = {"law"},
}
labels["crochet"] = {
type = "related-to",
parents = {"crafts"},
}
labels["cryptocurrency"] = {
type = "related-to",
parents = {"currency", "cryptography", "technology"},
}
labels["currencies"] = {
type = "set",
parents = {"money", "currency"},
}
labels["currency"] = {
type = "related-to",
parents = {"money"},
}
labels["dairy farming"] = {
type = "related-to",
parents = {"agriculture", "industries"},
}
labels["democracy"] = {
type = "related-to",
parents = {"forms of government"},
}
labels["diplomacy"] = {
type = "related-to",
parents = {"society"},
}
labels["discrimination"] = {
type = "related-to",
parents = {"society"},
}
labels["drug trafficking"] = {
type = "related-to",
parents = {"crime", "drugs"},
}
labels["education"] = {
type = "related-to",
parents = {"society"},
}
labels["emergency services"] = {
type = "related-to",
parents = {"public safety"},
}
labels["employment"] = {
type = "related-to",
parents = {"business", "work"},
}
labels["espionage"] = {
type = "related-to",
parents = {"security", "deception", "secrecy"},
}
labels["evil"] = {
type = "related-to",
parents = {"ethics", "religion"},
}
labels["fame"] = {
type = "related-to",
parents = {"society", "knowledge"},
}
labels["family law"] = {
type = "related-to",
breadcrumb = "family",
parents = {"law"},
}
labels["fascism"] = {
type = "related-to",
parents = {"ideologies"},
}
labels["farriery"] = {
type = "related-to",
parents = {"blacksmithing", "horses"},
}
labels["feminism"] = {
type = "related-to",
parents = {"gender", "female", "ideologies", "society", "sociology"},
}
labels["feudalism"] = {
type = "related-to",
parents = {"forms of government"},
}
labels["finance"] = {
type = "related-to",
parents = {"business"},
}
labels["firefighting"] = {
type = "related-to",
parents = {"emergency services", "fire"},
}
labels["forms of discrimination"] = {
type = "type",
description = "{{{langname}}} terms for [[form]]s of [[discrimination]].",
additional = "{{also|Category:{{{langcode}}}:Biases|Category:{{{langcode}}}:Conspiracy theories|Category:{{{langcode}}}:Ideologies}}",
breadcrumb = "forms",
parents = {"discrimination"},
}
labels["forms of government"] = {
type = "type",
description = "{{{langname}}} terms for [[form]]s of [[government]].",
breadcrumb = "forms",
parents = {"government"},
}
labels["freedom"] = {
type = "related-to",
parents = {"society"},
}
labels["freedom of speech"] = {
type = "related-to",
breadcrumb = "speech",
parents = {{name = "freedom", sort = "speech"}, "law"},
}
labels["freemasonry"] = {
type = "related-to",
parents = {"organizations"},
}
labels["funeral"] = {
type = "related-to",
parents = {"society", "death", "industries"},
}
labels["furniture"] = {
type = "related-to",
parents = {"home"},
commonscat = true,
wpcat = true,
}
labels["gender-critical feminism"] = {
type = "related-to",
breadcrumb = "gender-critical",
parents = {"feminism", "gender", "transphobia"},
}
labels["glassblowing"] = {
type = "related-to",
parents = {"crafts", "glass"},
}
labels["good"] = {
type = "related-to",
parents = {"ethics", "religion"},
}
labels["government"] = {
type = "related-to",
parents = {"society", "politics"},
}
labels["hairdressing"] = {
type = "related-to",
parents = {"hair", "crafts"},
}
labels["high society"] = {
type = "related-to",
description = "=royalty and nobility",
parents = {"society"},
}
labels["Hindutva"] = {
type = "related-to",
description = "=[[Hindutva]] or {{w|Hindu nationalism}}",
parents = {"conservatism", "Hinduism", "ideologies", "Indian politics", "nationalism", "theocracy"},
}
labels["historical currencies"] = {
type = "set",
breadcrumb = "historical",
parents = {"currencies"},
}
labels["home"] = {
type = "related-to",
description = "default with the",
parents = {"society"},
}
labels["homophobia"] = {
type = "related-to",
parents = {"queerphobia"},
}
labels["hospitality"] = {
type = "related-to",
parents = {"business"},
}
labels["host industry"] = {
type = "related-to",
parents = {"hospitality", "businesses"},
}
labels["hotels"] = {
type = "type",
parents = {"businesses", "tourism", "hospitality"},
}
labels["household"] = {
type = "related-to",
parents = {"home"},
}
labels["housing"] = {
type = "related-to",
parents = {"home", "buildings"},
}
labels["human resources"] = {
type = "related-to",
description = "default no singularize",
parents = {"business", "sociology"},
}
labels["ideologies"] = {
type = "related-to",
parents = {"society", "politics"},
}
labels["imperialism"] = {
type = "related-to",
parents = {"ideologies"},
}
labels["import/export"] = {
type = "related-to",
description = "=[[import]]s and [[export]]s",
parents = {"trading", "transport"},
}
labels["incel community"] = {
type = "related-to",
description = "=the [[incel]] community",
parents = {"masculism", "sex"},
}
labels["incoterms"] = {
type = "related-to",
description = "=[[Incoterm]]s",
parents = {"business", "import/export"},
}
labels["industries"] = {
type = "related-to",
parents = {"business"},
}
labels["inheritance law"] = {
type = "related-to",
breadcrumb = "inheritance",
parents = {"law"},
}
labels["insurance"] = {
type = "related-to",
parents = {"finance", "industries"},
}
labels["intellectual property"] = {
type = "related-to",
description = "=[[intellectual property]] [[law]]",
parents = {"law"},
}
labels["international law"] = {
type = "related-to",
breadcrumb = "international",
parents = {"law"},
}
labels["international relations"] = {
type = "related-to",
description = "default wikify",
parents = {"politics", "Earth"},
}
labels["Islamic finance"] = {
type = "related-to",
description = "default wikify",
breadcrumb = "Islamic",
parents = {"finance", "banking", "Islam"},
}
labels["Islamic law"] = {
type = "related-to",
description = "default wikify",
breadcrumb = "law",
parents = {{name = "Islam", sort = "law"}, "law"},
}
labels["Islamism"] = {
type = "related-to",
parents = {"ideologies", "conservatism", "Islam", "theocracy"},
}
labels["Juche"] = {
type = "related-to",
parents = {"North Korea", "communism", "nationalism"},
}
labels["justice"] = {
type = "related-to",
parents = {"society"},
}
labels["Kentucky Fried Chicken"] = {
type = "related-to",
description = "=the {{w|Kentucky Fried Chicken}} [[chain]] of [[fast-food]] [[restaurant]]s",
parents = {"restaurants"},
}
labels["knitting"] = {
type = "related-to",
parents = {"crafts"},
}
labels["Ku Klux Klan"] = {
type = "related-to",
description = "default with the",
parents = {"organizations", "white supremacist ideology"},
}
labels["kyabakura industry"] = {
type = "related-to",
parents = {"hospitality", "businesses"},
}
labels["labour"] = {
type = "related-to",
description = "=[[labour]] or the {{w|labour movement}}",
parents = {"work", "leftism"},
}
labels["laundry"] = {
type = "related-to",
parents = {"cleaning"},
}
labels["law"] = {
type = "related-to",
description = "=the [[science]] and [[practice]] of [[law]]",
parents = {"justice"},
}
labels["law enforcement"] = {
type = "related-to",
parents = {"crime prevention", "emergency services", "law"},
}
labels["law of obligations"] = {
type = "related-to",
description = "default with the no singularize",
breadcrumb = "obligations",
parents = {{name = "law", sort = "obligations"}},
}
labels["leatherworking"] = {
type = "related-to",
parents = {"crafts"},
}
labels["leftism"] = {
type = "related-to",
parents = {"ideologies"},
}
labels["liberalism"] = {
type = "related-to",
parents = {"ideologies"},
}
labels["libertarianism"] = {
type = "related-to",
parents = {"ideologies"},
}
labels["logistics"] = {
type = "related-to",
description = "default no singularize",
parents = {"operations"},
}
labels["management"] = {
type = "related-to",
parents = {"business"},
}
labels["Maoism"] = {
type = "related-to",
parents = {"ideologies", "communism", "Marxism"},
}
labels["marketing"] = {
type = "related-to",
parents = {"business"},
}
labels["Marxism"] = {
type = "related-to",
parents = {"ideologies", "socialism"},
}
labels["masculism"] = {
type = "related-to",
parents = {"ideologies", "male"},
}
labels["metalworking"] = {
type = "related-to",
parents = {"crafts", "metallurgy"},
}
labels["McDonald's"] = {
type = "related-to",
description = "=the {{w|McDonald's}} [[chain]] of [[fast-food]] [[restaurant]]s",
parents = {"restaurants"},
}
labels["micronationalism"] = {
type = "related-to",
parents = {"forms of government", "ideologies"},
}
labels["military"] = {
type = "related-to",
description = "default with the",
parents = {"society"},
}
labels["military units"] = {
type = "related-to",
parents = {"military", "occupations"},
}
labels["mining"] = {
type = "related-to",
parents = {"industries"},
}
labels["monarchism"] = {
type = "related-to",
parents = {"ideologies", "monarchy"},
}
labels["monarchy"] = {
type = "related-to",
parents = {"forms of government", "high society"},
}
labels["money"] = {
type = "related-to",
parents = {"business"},
}
labels["museums"] = {
type = "related-to",
parents = {"businesses", "tourism", "art"},
}
labels["nationalism"] = {
type = "related-to",
parents = {"ideologies"},
}
labels["Nazism"] = {
type = "related-to",
parents = {"fascism", "white supremacist ideology", "ideologies"},
}
labels["neo-Nazism"] = { -- Adjacent to Nazism, but not quite the same thing.
type = "related-to",
parents = {"Nazism", "fascism", "white supremacist ideology", "ideologies"},
}
labels["Nobel Prize"] = {
type = "related-to",
description = "default with the",
parents = {"awards"},
}
labels["Objectivism"] = {
type = "related-to",
description = "=the political philosophy of {{w|Objectivism}} developed by {{w|Ayn Rand}}",
parents = {"ideologies", "libertarianism"},
}
labels["offices"] = {
type = "type",
description = "=offices, in the sense \"position of responsibility of some authority within an organisation\"",
parents = {"government"},
}
labels["oil industry"] = {
type = "related-to",
description = "default with the",
breadcrumb = "oil",
parents = {"industries", "petroleum"},
}
labels["operations"] = {
type = "related-to",
description = "{{{langname}}} terms covering all operational matters in [[production]], [[logistics]], or [[services]].",
parents = {"management", "systems theory"},
}
labels["organizations"] = {
type = "name",
parents = {"society"},
}
labels["papermaking"] = {
type = "related-to",
parents = {"crafts", "industries"},
}
labels["patent law"] = {
type = "related-to",
breadcrumb = "patent",
parents = {"law"},
}
labels["peace"] = {
type = "related-to",
parents = {"security"},
}
labels["pensions"] = {
type = "related-to",
parents = {"finance"},
}
labels["philanthropy"] = {
type = "related-to",
parents = {"society"},
}
labels["Philmont Scout Ranch"] = {
type = "related-to",
description = "={{w|Philmont Scout Ranch}}, a Scouting ranch in the United States",
parents = {"Scouting"},
}
labels["piracy"] = {
type = "related-to",
parents = {"crime", "nautical"},
}
labels["politics"] = {
type = "related-to",
description = "default no singularize",
parents = {"society"},
}
labels["poverty"] = {
type = "related-to",
parents = {"wealth"},
}
for _, country_demonym in ipairs {
{"Argentina", "Argentine"},
{"Australia", "Australian"},
{"Bangladesh", "Bangladeshi"},
{"Brazil", "Brazilian"},
{"Bulgaria", "Bulgarian"},
{"Canada", "Canadian"},
{"Chile", "Chilean"},
{"China", "Chinese"},
{"Europe", "European"},
{"European Union", "EU", "the European Union", "European politics"},
{"France", "French", nil, "European politics"},
{"Germany", "German", nil, "European politics"},
{"Hong Kong", "Hong Kong"},
{"Hungary", "Hungarian", nil, "European politics"},
{"India", "Indian"},
{"Indonesia", "Indonesian"},
{"Ireland", "Irish", nil, "European politics"},
{"Japan", "Japanese"},
{"Malaysia", "Malaysian"},
{"Mexico", "Mexican"},
{"New Zealand", "New Zealand"},
{"Nigeria", "Nigerian"},
{"Pakistan", "Pakistani"},
{"Palestine", "Palestinian"},
{"Peru", "Peruvian"},
{"Philippines", "Philippine", "the Philippines"},
{"Poland", "Polish", nil, "European politics"},
{"Portugal", "Portuguese", nil, "European politics"},
{"Russia", "Russian"},
{"Singapore", "Singaporean"},
{"South Africa", "South African"},
{"South Korea", "South Korean"},
{"Spain", "Spanish", nil, "European politics"},
{"Switzerland", "Swiss", nil, "European politics"},
{"Taiwan", "Taiwanese"},
{"Ukraine", "Ukrainian"},
{"United Kingdom", "UK", "the United Kingdom", "European politics"},
{"United States", "US", "the United States"},
{"Venezuela", "Venezuelan"},
{"Vietnam", "Vietnamese"},
} do
local country, demonym, full_country, parent = unpack(country_demonym)
labels[demonym .. " politics"] = {
type = "related-to",
description = ("=the {{w|politics of %s}}"):format(full_country or country),
parents = {parent or "politics", country},
}
end
labels["printing"] = {
type = "related-to",
parents = {"industries"},
}
labels["prison"] = {
type = "related-to",
parents = {"law enforcement", "buildings"},
}
labels["procedural law"] = {
type = "related-to",
breadcrumb = "procedural",
parents = {"law"},
}
labels["property law"] = {
type = "related-to",
breadcrumb = "property",
parents = {"law"},
}
labels["public administration"] = {
type = "related-to",
description = "=the field of [[public]] [[administration]]",
parents = {"government"},
}
labels["public safety"] = {
type = "related-to",
description = "=the field of [[public]] [[safety]]",
parents = {"public administration", "security"},
}
labels["publishing"] = {
type = "related-to",
parents = {"industries", "mass media"},
}
labels["QAnon"] = {
type = "related-to",
description = "=the [[QAnon]] movement",
parents = {"alt-right", "conspiracy theories", "Donald Trump", "pedophilia"},
}
labels["queerphobia"] = {
type = "related-to",
parents = {"forms of discrimination", "LGBTQ"},
}
labels["racism"] = {
type = "related-to",
parents = {"forms of discrimination"},
}
labels["rape"] = {
type = "related-to",
description = "=the field of [[sexual violence]]",
parents = {"sex", "crime", "violence"},
}
labels["real estate"] = {
type = "related-to",
parents = {"industries", "housing"},
}
labels["restaurants"] = {
type = "related-to",
description = "=[[restaurant]]s (including [[pub]]s, [[café]]s etc.)",
parents = {"businesses", "food and drink"},
}
labels["royal residences"] = {
type = "related-to",
parents = {"housing", "monarchy"},
}
labels["schools"] = {
type = "related-to",
parents = {"education", "buildings"},
}
-- Note: this is the usual term, not "Scottish law".
labels["Scots law"] = {
type = "related-to",
breadcrumb = "Scots",
parents = {"law", "Scotland"},
}
labels["Scouting"] = {
type = "related-to",
parents = {"society"},
}
labels["security"] = {
type = "related-to",
parents = {"society"},
}
labels["sexism"] = {
type = "related-to",
parents = {"forms of discrimination", "gender"},
}
labels["sewing"] = {
type = "related-to",
description = "=[[sewing]], sewing tools, sewing [[technique]]s and so on",
parents = {"crafts"},
}
labels["shoemaking"] = {
type = "related-to",
parents = {"crafts"},
}
labels["slavery"] = {
type = "related-to",
parents = {"society", "work"},
}
labels["socialism"] = {
type = "related-to",
parents = {"economics", "ideologies", "leftism"},
}
labels["social justice"] = {
type = "related-to",
parents = {"politics", "society", "sociology", "leftism"},
}
labels["social security"] = {
type = "related-to",
parents = {"government", "law", "money"},
}
labels["spinning"] = {
type = "related-to",
description = "=[[spinning]], the process of making [[yarn]] or [[string]] from raw [[fiber]]",
parents = {"crafts"},
}
labels["square dancing"] = {
type = "related-to",
parents = {"dance"},
}
labels["standards of identity"] = {
type = "related-to",
parents = {"law", "food and drink"},
}
labels["stock market"] = {
type = "related-to",
description = "default with the",
parents = {"finance"},
}
labels["stock symbols for companies"] = {
type = "name",
description = "=[[stock symbol]]s for [[company|companies]]",
parents = {"trading"},
}
labels["supply chain"] = {
type = "related-to",
description = "default no singularize",
parents = {"operations"},
}
labels["taxation"] = {
type = "related-to",
parents = {"government", "law", "money"},
}
labels["theft"] = {
type = "related-to",
parents = {"crime"},
}
labels["theocracy"] = {
type = "related-to",
parents = {"ideologies", "religion"},
}
labels["timber industry"] = {
type = "related-to",
description = "default with the",
breadcrumb = "timber",
parents = {"industries"},
}
labels["trademark"] = {
type = "related-to",
description = "=[[trademark]] [[law]]",
parents = {"intellectual property"},
}
labels["trading"] = {
type = "related-to",
parents = {"business"},
}
labels["transphobia"] = {
type = "related-to",
parents = {"forms of discrimination", "transgender"},
}
labels["trust"] = {
type = "related-to",
parents = {"security"},
}
labels["types of settlements"] = {
type = "type",
topic = "settlements",
description = "=[[settlement]]s",
parents = {"government"},
}
labels["United Nations"] = {
type = "related-to",
description = "=the [[United Nations Organization]]",
parents = {"organizations"},
}
labels["universities"] = {
type = "related-to",
parents = {"schools"},
}
labels["voting systems"] = {
type = "related-to",
parents = {"democracy", "systems"},
}
labels["wealth"] = {
type = "related-to",
parents = {"economics"},
}
labels["weaving"] = {
type = "related-to",
parents = {"crafts"},
}
labels["white supremacist ideology"] = {
type = "related-to",
parents = {"racism", "anti-Semitism", "ideologies"},
}
labels["woodworking"] = {
type = "related-to",
parents = {"crafts"},
}
labels["Zionism"] = {
type = "related-to",
parents = {"ideologies", "Judaism", "Israel", "nationalism"},
}
return labels
snpr4mk3ymmjcvcyb5ttksd00pkkk8r
Mòideal:category tree/topic/Sports
828
17905
88808
2026-07-23T19:36:50Z
en>WingerBot
0
explicit 'description = "default"' unnecessary; remove it (manually assisted)
88808
Scribunto
text/plain
local labels = {}
labels["sports"] = {
type = "related-to",
parents = {"human activity"},
}
labels["archery"] = {
type = "related-to",
parents = {"sports", "weapons"},
}
labels["athletics"] = {
type = "related-to",
description = "default no singularize",
parents = {"sports"},
}
labels["Australian rules football"] = {
type = "related-to",
description = "=[[Australian rules football]], often known as [[Aussie rules]]",
parents = {"football"},
commonscat = true,
wpcat = true,
}
labels["auto racing"] = {
type = "related-to",
parents = {"motor racing", "automotive"},
}
labels["badminton"] = {
type = "related-to",
parents = {"racquet sports"},
}
labels["ball games"] = {
type = "related-to",
parents = {"sports"},
}
labels["baseball"] = {
type = "related-to",
parents = {"ball games"},
}
labels["basketball"] = {
type = "related-to",
parents = {"ball games"},
commonscat = true,
wpcat = true,
}
labels["Basque pelota"] = {
type = "related-to",
parents = {"ball games"},
}
labels["board sports"] = {
type = "related-to",
parents = {"sports"},
}
labels["bodybuilding"] = {
type = "related-to",
parents = {"sports"},
}
labels["boxing"] = {
type = "related-to",
parents = {"sports"},
wpcat = true,
commonscat = true,
}
labels["bullfighting"] = {
type = "related-to",
parents = {"sports"},
}
labels["Canadian football"] = {
type = "related-to",
parents = {"football"},
}
labels["capoeira"] = {
type = "related-to",
parents = {"martial arts"},
}
labels["caving"] = {
type = "related-to",
parents = {"sports"},
}
labels["climbing"] = {
type = "related-to",
parents = {"sports"},
wpcat = true,
commonscat = true,
}
labels["cockfighting"] = {
type = "related-to",
parents = {"sports"},
}
labels["college sports"] = {
type = "related-to",
parents = {"sports", "universities"},
}
labels["cricket"] = {
type = "related-to",
parents = {"ball games"},
}
labels["croquet"] = {
type = "related-to",
parents = {"ball games"},
}
labels["curling"] = {
type = "related-to",
parents = {"winter sports"},
}
labels["cycle racing"] = {
type = "related-to",
parents = {"sports", "cycling"},
}
labels["disc golf"] = {
type = "related-to",
parents = {"sports"},
}
labels["diving"] = {
type = "related-to",
description = "=[[diving]] (the [[sport#Noun|sport]] of [[jump#Verb|jumping]] or [[fall#Verb|falling]] into [[water#Noun|water]] from a [[platform]] or [[springboard#Noun|springboard]])",
additional = "For terms related to [[underwater]] diving, see [[:Category:Underwater diving]] or one of its subcategories.",
parents = {"water sports"},
commonscat = true,
wpcat = true,
}
labels["dressage"] = {
type = "related-to",
parents = {"equestrianism"},
}
labels["equestrianism"] = {
type = "related-to",
parents = {"animal riding", "sports"},
}
labels["exercise"] = {
type = "related-to",
parents = {"sports"},
}
labels["exercise equipment"] = {
type = "set",
description = "=devices designed for pursuing fitness goals by performing sporting activities on them",
parents = {"exercise", "tools"},
}
labels["falconry"] = {
type = "related-to",
parents = {"sports"},
}
labels["fencing"] = {
type = "related-to",
parents = {"sports"},
}
labels["field hockey"] = {
type = "related-to",
parents = {"hockey"},
}
labels["figure skating"] = {
type = "related-to",
parents = {"winter sports"},
}
labels["football"] = {
type = "related-to",
parents = {"ball games"},
}
labels["football (American)"] = {
type = "related-to",
description = "=[[American football]]",
parents = {"football"},
commonscat = "American football",
wpcat = "American football",
}
labels["football (soccer)"] = {
type = "related-to",
description = "=[[soccer]]",
parents = {"football"},
}
labels["Gaelic football"] = {
type = "related-to",
parents = {"football"},
}
labels["gladiatorial combat"] = {
type = "related-to",
parents = {"sports", "entertainment"},
}
labels["golf"] = {
type = "related-to",
parents = {"ball games"},
}
labels["gun sports"] = {
type = "related-to",
parents = {"sports", "firearms"},
}
labels["gymnastics"] = {
type = "related-to",
description = "default no singularize",
parents = {"sports"},
}
labels["handball"] = {
type = "related-to",
parents = {"ball games"},
}
labels["Highland games"] = {
type = "related-to",
parents = {"sports"},
commonscat = true,
wpcat = true,
}
labels["hockey"] = {
type = "related-to",
parents = {"sports"},
}
labels["horse racing"] = {
type = "related-to",
parents = {"equestrianism", "horses"},
commonscat = true,
wpcat = true,
}
labels["hurling"] = {
type = "related-to",
parents = {"ball games"},
}
labels["ice hockey"] = {
type = "related-to",
parents = {"hockey", "winter sports"},
commonscat = true,
wpcat = true,
}
labels["judo"] = {
type = "related-to",
parents = {"martial arts"},
}
labels["karate"] = {
type = "related-to",
parents = {"martial arts"},
}
labels["kendo"] = {
type = "related-to",
parents = {"martial arts"},
}
labels["lacrosse"] = {
type = "related-to",
parents = {"ball games", "racquet sports"},
}
labels["martial arts"] = {
type = "related-to",
parents = {"sports"},
}
labels["motor racing"] = {
type = "related-to",
parents = {"sports", "automotive"},
}
labels["netball"] = {
type = "related-to",
parents = {"ball games"},
}
labels["paintball"] = {
type = "related-to",
parents = {"sports"},
}
labels["pesäpallo"] = {
type = "related-to",
parents = {"ball games", "Finland"},
wp = true,
wpcat = true,
commonscat = true,
}
labels["professional wrestling"] = {
type = "related-to",
parents = {"sports", "entertainment"},
}
labels["racquet sports"] = {
type = "related-to",
parents = {"sports"},
wpcat = true,
commonscat = true,
}
labels["rhythmic gymnastics"] = {
type = "related-to",
parents = {"gymnastics"},
wpcat = true,
commonscat = true,
}
labels["roller derby"] = {
type = "related-to",
parents = {"sports"},
}
labels["rowing"] = {
type = "related-to",
parents = {"water sports"},
wp = "Rowing (sport)",
commonscat = true,
wpcat = true,
}
labels["rugby"] = {
type = "related-to",
parents = {"ball games"},
}
labels["rugby league"] = {
type = "related-to",
parents = {"rugby"},
}
labels["rugby union"] = {
type = "related-to",
parents = {"rugby"},
}
labels["skateboarding"] = {
type = "related-to",
parents = {"board sports"},
}
labels["skiing"] = {
type = "related-to",
description = "=[[snow]] [[skiing]]",
parents = {"winter sports"},
}
labels["skydiving"] = {
type = "related-to",
parents = {"sports"},
}
labels["snowboarding"] = {
type = "related-to",
parents = {"winter sports"},
}
labels["softball"] = {
type = "related-to",
parents = {"ball games"},
}
labels["sports areas"] = {
type = "set",
description = "=designated areas where [[sports]] are played",
parents = {"sports"},
}
labels["sports equipment"] = {
type = "type",
parents = {"sports", "exercise equipment", "tools"},
}
labels["squash"] = {
type = "related-to",
description = "=the sport of [[squash]]",
parents = {"ball games", "racquet sports"},
}
labels["sumo"] = {
type = "related-to",
parents = {"sports"},
}
labels["surfing"] = {
type = "related-to",
parents = {"water sports"},
}
labels["swimming"] = {
type = "related-to",
parents = {"water sports"},
commonscat = true,
wpcat = true,
}
labels["table tennis"] = {
type = "related-to",
description = "default no singularize",
parents = {"ball games", "racquet sports"},
}
labels["tennis"] = {
type = "related-to",
description = "default no singularize",
parents = {"ball games", "racquet sports"},
}
labels["ultimate"] = {
type = "related-to",
description = "=the sport of [[ultimate]]",
parents = {"sports"},
}
labels["underwater diving"] = {
type = "related-to",
description = "default wikify",
parents = {"water sports"},
}
labels["volleyball"] = {
type = "related-to",
parents = {"ball games"},
}
labels["water sports"] = {
type = "related-to",
parents = {"sports"},
}
labels["winter sports"] = {
type = "related-to",
parents = {"sports", "winter activities"},
}
labels["wrestling"] = {
type = "related-to",
parents = {"sports"},
}
return labels
1x34h1gyeik85xzppyguevby6zjw5q5
88809
88808
2026-08-06T20:15:59Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/topic/Sports]]
88808
Scribunto
text/plain
local labels = {}
labels["sports"] = {
type = "related-to",
parents = {"human activity"},
}
labels["archery"] = {
type = "related-to",
parents = {"sports", "weapons"},
}
labels["athletics"] = {
type = "related-to",
description = "default no singularize",
parents = {"sports"},
}
labels["Australian rules football"] = {
type = "related-to",
description = "=[[Australian rules football]], often known as [[Aussie rules]]",
parents = {"football"},
commonscat = true,
wpcat = true,
}
labels["auto racing"] = {
type = "related-to",
parents = {"motor racing", "automotive"},
}
labels["badminton"] = {
type = "related-to",
parents = {"racquet sports"},
}
labels["ball games"] = {
type = "related-to",
parents = {"sports"},
}
labels["baseball"] = {
type = "related-to",
parents = {"ball games"},
}
labels["basketball"] = {
type = "related-to",
parents = {"ball games"},
commonscat = true,
wpcat = true,
}
labels["Basque pelota"] = {
type = "related-to",
parents = {"ball games"},
}
labels["board sports"] = {
type = "related-to",
parents = {"sports"},
}
labels["bodybuilding"] = {
type = "related-to",
parents = {"sports"},
}
labels["boxing"] = {
type = "related-to",
parents = {"sports"},
wpcat = true,
commonscat = true,
}
labels["bullfighting"] = {
type = "related-to",
parents = {"sports"},
}
labels["Canadian football"] = {
type = "related-to",
parents = {"football"},
}
labels["capoeira"] = {
type = "related-to",
parents = {"martial arts"},
}
labels["caving"] = {
type = "related-to",
parents = {"sports"},
}
labels["climbing"] = {
type = "related-to",
parents = {"sports"},
wpcat = true,
commonscat = true,
}
labels["cockfighting"] = {
type = "related-to",
parents = {"sports"},
}
labels["college sports"] = {
type = "related-to",
parents = {"sports", "universities"},
}
labels["cricket"] = {
type = "related-to",
parents = {"ball games"},
}
labels["croquet"] = {
type = "related-to",
parents = {"ball games"},
}
labels["curling"] = {
type = "related-to",
parents = {"winter sports"},
}
labels["cycle racing"] = {
type = "related-to",
parents = {"sports", "cycling"},
}
labels["disc golf"] = {
type = "related-to",
parents = {"sports"},
}
labels["diving"] = {
type = "related-to",
description = "=[[diving]] (the [[sport#Noun|sport]] of [[jump#Verb|jumping]] or [[fall#Verb|falling]] into [[water#Noun|water]] from a [[platform]] or [[springboard#Noun|springboard]])",
additional = "For terms related to [[underwater]] diving, see [[:Category:Underwater diving]] or one of its subcategories.",
parents = {"water sports"},
commonscat = true,
wpcat = true,
}
labels["dressage"] = {
type = "related-to",
parents = {"equestrianism"},
}
labels["equestrianism"] = {
type = "related-to",
parents = {"animal riding", "sports"},
}
labels["exercise"] = {
type = "related-to",
parents = {"sports"},
}
labels["exercise equipment"] = {
type = "set",
description = "=devices designed for pursuing fitness goals by performing sporting activities on them",
parents = {"exercise", "tools"},
}
labels["falconry"] = {
type = "related-to",
parents = {"sports"},
}
labels["fencing"] = {
type = "related-to",
parents = {"sports"},
}
labels["field hockey"] = {
type = "related-to",
parents = {"hockey"},
}
labels["figure skating"] = {
type = "related-to",
parents = {"winter sports"},
}
labels["football"] = {
type = "related-to",
parents = {"ball games"},
}
labels["football (American)"] = {
type = "related-to",
description = "=[[American football]]",
parents = {"football"},
commonscat = "American football",
wpcat = "American football",
}
labels["football (soccer)"] = {
type = "related-to",
description = "=[[soccer]]",
parents = {"football"},
}
labels["Gaelic football"] = {
type = "related-to",
parents = {"football"},
}
labels["gladiatorial combat"] = {
type = "related-to",
parents = {"sports", "entertainment"},
}
labels["golf"] = {
type = "related-to",
parents = {"ball games"},
}
labels["gun sports"] = {
type = "related-to",
parents = {"sports", "firearms"},
}
labels["gymnastics"] = {
type = "related-to",
description = "default no singularize",
parents = {"sports"},
}
labels["handball"] = {
type = "related-to",
parents = {"ball games"},
}
labels["Highland games"] = {
type = "related-to",
parents = {"sports"},
commonscat = true,
wpcat = true,
}
labels["hockey"] = {
type = "related-to",
parents = {"sports"},
}
labels["horse racing"] = {
type = "related-to",
parents = {"equestrianism", "horses"},
commonscat = true,
wpcat = true,
}
labels["hurling"] = {
type = "related-to",
parents = {"ball games"},
}
labels["ice hockey"] = {
type = "related-to",
parents = {"hockey", "winter sports"},
commonscat = true,
wpcat = true,
}
labels["judo"] = {
type = "related-to",
parents = {"martial arts"},
}
labels["karate"] = {
type = "related-to",
parents = {"martial arts"},
}
labels["kendo"] = {
type = "related-to",
parents = {"martial arts"},
}
labels["lacrosse"] = {
type = "related-to",
parents = {"ball games", "racquet sports"},
}
labels["martial arts"] = {
type = "related-to",
parents = {"sports"},
}
labels["motor racing"] = {
type = "related-to",
parents = {"sports", "automotive"},
}
labels["netball"] = {
type = "related-to",
parents = {"ball games"},
}
labels["paintball"] = {
type = "related-to",
parents = {"sports"},
}
labels["pesäpallo"] = {
type = "related-to",
parents = {"ball games", "Finland"},
wp = true,
wpcat = true,
commonscat = true,
}
labels["professional wrestling"] = {
type = "related-to",
parents = {"sports", "entertainment"},
}
labels["racquet sports"] = {
type = "related-to",
parents = {"sports"},
wpcat = true,
commonscat = true,
}
labels["rhythmic gymnastics"] = {
type = "related-to",
parents = {"gymnastics"},
wpcat = true,
commonscat = true,
}
labels["roller derby"] = {
type = "related-to",
parents = {"sports"},
}
labels["rowing"] = {
type = "related-to",
parents = {"water sports"},
wp = "Rowing (sport)",
commonscat = true,
wpcat = true,
}
labels["rugby"] = {
type = "related-to",
parents = {"ball games"},
}
labels["rugby league"] = {
type = "related-to",
parents = {"rugby"},
}
labels["rugby union"] = {
type = "related-to",
parents = {"rugby"},
}
labels["skateboarding"] = {
type = "related-to",
parents = {"board sports"},
}
labels["skiing"] = {
type = "related-to",
description = "=[[snow]] [[skiing]]",
parents = {"winter sports"},
}
labels["skydiving"] = {
type = "related-to",
parents = {"sports"},
}
labels["snowboarding"] = {
type = "related-to",
parents = {"winter sports"},
}
labels["softball"] = {
type = "related-to",
parents = {"ball games"},
}
labels["sports areas"] = {
type = "set",
description = "=designated areas where [[sports]] are played",
parents = {"sports"},
}
labels["sports equipment"] = {
type = "type",
parents = {"sports", "exercise equipment", "tools"},
}
labels["squash"] = {
type = "related-to",
description = "=the sport of [[squash]]",
parents = {"ball games", "racquet sports"},
}
labels["sumo"] = {
type = "related-to",
parents = {"sports"},
}
labels["surfing"] = {
type = "related-to",
parents = {"water sports"},
}
labels["swimming"] = {
type = "related-to",
parents = {"water sports"},
commonscat = true,
wpcat = true,
}
labels["table tennis"] = {
type = "related-to",
description = "default no singularize",
parents = {"ball games", "racquet sports"},
}
labels["tennis"] = {
type = "related-to",
description = "default no singularize",
parents = {"ball games", "racquet sports"},
}
labels["ultimate"] = {
type = "related-to",
description = "=the sport of [[ultimate]]",
parents = {"sports"},
}
labels["underwater diving"] = {
type = "related-to",
description = "default wikify",
parents = {"water sports"},
}
labels["volleyball"] = {
type = "related-to",
parents = {"ball games"},
}
labels["water sports"] = {
type = "related-to",
parents = {"sports"},
}
labels["winter sports"] = {
type = "related-to",
parents = {"sports", "winter activities"},
}
labels["wrestling"] = {
type = "related-to",
parents = {"sports"},
}
return labels
1x34h1gyeik85xzppyguevby6zjw5q5
Mòideal:category tree/topic/Technology
828
17906
88810
2026-07-23T19:36:51Z
en>WingerBot
0
explicit 'description = "default"' unnecessary; remove it (manually assisted)
88810
Scribunto
text/plain
local labels = {}
labels["technology"] = {
type = "related-to",
parents = {"all topics"},
}
labels["aircraft"] = {
type = "related-to",
parents = {"technology", "aviation"},
}
labels["amusement rides"] = {
type = "type",
parents = {"machines"},
}
labels["Apple Inc."] = {
type = "related-to",
description = "={{w|Apple Inc.}} or its products",
parents = {"technology", "computing"},
}
labels["armor"] = {
type = "related-to",
parents = {"technology"},
}
labels["artillery"] = {
type = "related-to",
parents = {"weapons"},
}
labels["automotive parts"] = {
type = "type",
description = "=[[automotive]] [[part]]s",
breadcrumb = "Parts",
parents = {"automotive", "automobiles"},
}
labels["automobiles"] = {
type = "related-to",
parents = {"vehicles", "automotive"},
}
labels["bags"] = {
type = "type",
parents = {"containers"},
}
labels["bicycle parts"] = {
type = "type",
description = "=parts of a [[bicycle]]",
parents = {"cycling"},
}
labels["bicycle types"] = {
type = "type",
description = "=[[bicycle]]s",
parents = {"cycling", "vehicles"},
}
labels["biotechnology"] = {
type = "related-to",
parents = {"technology", "biology"},
}
labels["buttons"] = {
type = "name,type",
description = "=[[button]]s of electronic [[device]]s",
parents = {"mechanisms"},
}
labels["C programming language"] = {
type = "related-to",
description = "=the [[w:C (programming language)|C programming language]] and related [[technology|technologies]]",
parents = {"computer languages"},
}
labels["canals"] = {
type = "name,related-to",
parents = {"technology", "transport"},
}
labels["carpentry tools"] = {
type = "type",
parents = {"tools", "carpentry"},
}
labels["carriages"] = {
type = "type",
parents = {"vehicles"},
}
labels["clocks"] = {
type = "type",
parents = {"machines", "timekeeping"},
}
labels["compilation"] = {
type = "related-to",
description = "=[[compilation]] of source code",
parents = {"computing"},
}
labels["computer graphics"] = {
type = "related-to",
description = "default no singularize",
parents = {"computing"},
wpcat = true,
commonscat = true,
}
labels["computer hardware"] = {
type = "related-to",
parents = {"electronics", "computing"},
}
labels["computer languages"] = {
type = "name",
description = "[[computer language]]s and computer [[programming language]]s",
parents = {"programming"},
}
labels["computer security"] = {
type = "related-to",
parents = {"computing", "security"},
}
labels["computing"] = {
type = "related-to",
parents = {"technology"},
}
labels["consumer electronics"] = {
type = "related-to",
description = "default no singularize",
parents = {"electronics"},
}
labels["connectors"] = {
type = "type",
parents = {"electronics"},
}
labels["construction vehicles"] = {
type = "type",
description = "=vehicles designed to be employed in building settlements",
parents = {"vehicles", "construction"},
}
labels["containers"] = {
type = "type",
parents = {"tools"},
}
labels["food and drink containers"] = {
type = "type",
description = "=[[container]]s for holding [[food]] or [[liquid]]s",
parents = {"containers", "food and drink"},
}
labels["cookware and bakeware"] = {
type = "type",
description = "=[[kitchenware]] used to prepare [[food]]",
parents = {"kitchenware"},
}
labels["cutlery"] = {
type = "type",
parents = {"kitchenware"},
}
labels["data modeling"] = {
type = "related-to",
parents = {"computing"},
}
labels["databases"] = {
type = "related-to",
parents = {"computing"},
}
labels["Dragon Ball"] = {
type = "related-to",
displaytitle = "''Dragon Ball''",
description = "=the Japanese media franchise ''{{w|Dragon Ball}}''",
parents = {"animation", "fantasy", "Japanese fiction"},
}
labels["e-mail"] = {
type = "related-to",
parents = {"Internet", "communication"},
}
labels["electron tubes"] = {
type = "related-to",
description = "=[[electron tube|electron tubes]] such as [[vacuum tube]]s/[[thermionic valve]]s",
parents = {"electronics"},
}
labels["electronics"] = {
type = "related-to",
description = "default no singularize",
parents = {"technology"},
}
labels["fans"] = {
type = "type",
additional = "{{also|Category:{{{langcode}}}:Fans (people)}}",
parents = {"tools"},
}
labels["fasteners"] = {
type = "type",
parents = {"tools"},
}
labels["farming tools"] = {
type = "type",
parents = {"tools", "agriculture"},
}
labels["file formats"] = {
type = "name",
parents = {"computing"},
}
labels["firearms"] = {
type = "related-to",
parents = {"weapons"},
}
labels["glasses"] = {
type = "related-to",
parents = {"eyewear"},
}
labels["graphical user interface"] = {
type = "related-to",
parents = {"computing"},
}
labels["Google"] = {
type = "related-to",
description = "={{w|Google}} or its products",
parents = {"World Wide Web"},
}
labels["gun mechanisms"] = {
type = "type",
description = "=[[firearm]] [[mechanism]]s or their parts",
parents = {"firearms", "mechanisms"},
}
labels["home appliances"] = {
type = "type",
parents = {"machines", "home"},
}
labels["horse tack"] = {
type = "type",
parents = {"tools", "horses", "animal riding"},
}
labels["HTML"] = {
type = "related-to",
description = "=[[HTML]], [[XHTML]], and related [[technology|technologies]]",
parents = {"computer languages", "World Wide Web"},
}
labels["Huawei"] = {
type = "related-to",
description = "={{w|Huawei}} or its products",
parents = {"technology", "mobile phones"},
}
labels["IBM"] = {
type = "related-to",
description = "=the [[American]] [[technology]] and [[consulting]] [[firm]] {{w|IBM}}",
parents = {"technology", "computing"},
}
labels["Internet"] = {
type = "related-to",
description = "default with the",
parents = {"computing", "networking"},
}
labels["Internet memes"] = {
type = "related-to",
parents = {"comedy", "Internet", "memetics"},
}
labels["Java programming language"] = {
type = "related-to",
description = "=the [[w:Java (programming language)|Java programming language]] and related [[technology|technologies]]",
parents = {"computer languages"},
}
labels["JavaScript"] = {
type = "related-to",
description = "={{w|JavaScript}} and related [[technology|technologies]]",
parents = {"computer languages", "World Wide Web"},
}
labels["kitchenware"] = {
type = "type",
parents = {"tools", "cooking", "home appliances"},
}
labels["knives"] = {
type = "type",
parents = {"tools", "weapons"},
}
labels["knots"] = {
type = "type",
parents = {"fasteners"},
}
labels["Linux"] = {
type = "related-to",
description = "=[[Linux]]",
parents = {"Unix"},
}
labels["locks"] = {
type = "type",
parents = {"mechanisms", "fasteners", "security"},
}
labels["machines"] = {
type = "type",
parents = {"technology"},
}
labels["malware"] = {
type = "type",
parents = {"software", "computer security"},
}
labels["masonry tools"] = {
type = "type",
parents = {"tools", "masonry"},
}
labels["measuring instruments"] = {
type = "type",
parents = {"tools"},
}
labels["mechanisms"] = {
type = "type",
parents = {"machines"},
}
labels["medical equipment"] = {
type = "type",
parents = {"tools", "medicine"},
}
labels["metallurgy"] = {
type = "related-to",
parents = {"technology"},
}
labels["metalworking tools"] = {
type = "type",
parents = {"tools", "metalworking"},
}
labels["microelectronics"] = {
type = "related-to",
parents = {"electronics"},
}
labels["Microsoft"] = {
type = "related-to",
description = "={{w|Microsoft Corporation}}",
parents = {"computing"},
}
labels["military vehicles"] = {
type = "type",
parents = {"vehicles", "military"},
}
labels["milling"] = {
type = "related-to",
parents = {"agriculture"},
}
labels["motorcycles"] = {
type = "related-to",
parents = {"vehicles"},
}
labels["nanotechnology"] = {
type = "related-to",
parents = {"technology"},
}
labels["networking"] = {
type = "related-to",
parents = {"computing"},
}
labels["newsgroups"] = {
type = "related-to",
description = "=[[Usenet]] [[newsgroup]]s",
parents = {"Usenet"},
}
labels["Nintendo"] = {
type = "related-to",
description = "{{{langname}}} terms either deriving from or relating to {{w|Nintendo}} or its products",
parents = {"video games"},
}
labels["object-oriented programming"] = {
type = "related-to",
description = "default wikify",
parents = {"programming"},
}
labels["plumbing"] = {
type = "related-to",
parents = {"technology"},
}
labels["polearms"] = {
type = "type",
parents = {"weapons"},
}
labels["Pokémon"] = {
type = "related-to",
displaytitle = "''Pokémon''",
description = "=''{{w|Pokémon}}''",
parents = {"Nintendo", "Japanese fiction"},
}
labels["programming"] = {
type = "related-to",
description = "=[[computer programming]]",
parents = {"computing", "software engineering"},
}
labels["pumps"] = {
type = "type",
parents = {"mechanisms"},
}
labels["regular expressions"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[regular expression]]s.",
parents = {"computing", "programming"},
}
labels["ropes and cords"] = {
type = "type",
parents = {"tools", "fasteners"},
}
labels["saws"] = {
type = "type",
parents = {"tools"},
}
labels["Sega"] = {
type = "related-to",
description = "{{{langname}}} terms either deriving from or relating to {{w|Sega}} or its products",
parents = {"video games"},
}
labels["semiconductors"] = {
type = "related-to",
parents = {"electronics"},
}
labels["simple machines"] = {
type = "type",
parents = {"machines"},
}
labels["software"] = {
type = "related-to",
parents = {"computing", "media"},
}
labels["spacecraft"] = {
type = "related-to",
parents = {"vehicles", "space travel"},
}
labels["spears"] = {
type = "type",
parents = {"weapons"},
}
labels["stationery"] = {
type = "related-to",
parents = {"tools"},
}
labels["swords"] = {
type = "type",
parents = {"weapons"},
}
labels["tools"] = {
type = "type",
parents = {"technology"},
}
labels["trapping"] = {
type = "related-to",
description = "=the business of [[trapper]]s",
parents = {"hunting"},
}
labels["typing keyboards"] = {
type = "related-to",
description = "=[[keyboards]] used as [[writing]] [[instrument]]s",
parents = {"writing instruments"},
}
labels["Unicode"] = {
type = "related-to",
parents = {"computing"},
}
labels["Unix"] = {
type = "related-to",
parents = {"computing"},
}
labels["Usenet"] = {
type = "related-to",
parents = {"Internet"},
}
labels["vehicles"] = {
type = "related-to",
parents = {"machines", "transport"},
}
labels["video compression"] = {
type = "related-to",
parents = {"computing"},
}
labels["visualization"] = {
type = "related-to",
parents = {"computing", "interdisciplinary fields"},
}
labels["warships"] = {
type = "type",
parents = {"watercraft", "military"},
}
labels["watercraft"] = {
type = "type",
description = "=[[watercraft]]: [[ship]]s, [[boat]]s, or any other vehicle that moves on or through the water",
parents = {"vehicles", "nautical"},
}
labels["weapons"] = {
type = "type",
parents = {"tools", "hunting", "military"},
}
labels["websites"] = {
type = "name,type",
parents = {"World Wide Web"},
}
labels["wiki"] = {
type = "related-to",
description = "=[[wiki]]s",
parents = {"World Wide Web"},
}
labels["Wikimedia"] = {
type = "related-to",
description = "={{w|Wikimedia}}-specific [[wiki]]s such as {{w|Wikipedia}} and {{w|Wiktionary}}",
parents = {"wiki"},
}
labels["World Wide Web"] = {
type = "related-to",
description = "default with the",
parents = {"Internet"},
}
labels["writing instruments"] = {
type = "type",
parents = {"tools", "writing", "stationery"},
}
labels["quantum computing"] = {
type = "related-to",
parents = {"computing", "quantum mechanics"},
}
return labels
41ph3apfr48cfr5l7ktui8zymtvvt7a
88811
88810
2026-08-06T20:16:11Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/topic/Technology]]
88810
Scribunto
text/plain
local labels = {}
labels["technology"] = {
type = "related-to",
parents = {"all topics"},
}
labels["aircraft"] = {
type = "related-to",
parents = {"technology", "aviation"},
}
labels["amusement rides"] = {
type = "type",
parents = {"machines"},
}
labels["Apple Inc."] = {
type = "related-to",
description = "={{w|Apple Inc.}} or its products",
parents = {"technology", "computing"},
}
labels["armor"] = {
type = "related-to",
parents = {"technology"},
}
labels["artillery"] = {
type = "related-to",
parents = {"weapons"},
}
labels["automotive parts"] = {
type = "type",
description = "=[[automotive]] [[part]]s",
breadcrumb = "Parts",
parents = {"automotive", "automobiles"},
}
labels["automobiles"] = {
type = "related-to",
parents = {"vehicles", "automotive"},
}
labels["bags"] = {
type = "type",
parents = {"containers"},
}
labels["bicycle parts"] = {
type = "type",
description = "=parts of a [[bicycle]]",
parents = {"cycling"},
}
labels["bicycle types"] = {
type = "type",
description = "=[[bicycle]]s",
parents = {"cycling", "vehicles"},
}
labels["biotechnology"] = {
type = "related-to",
parents = {"technology", "biology"},
}
labels["buttons"] = {
type = "name,type",
description = "=[[button]]s of electronic [[device]]s",
parents = {"mechanisms"},
}
labels["C programming language"] = {
type = "related-to",
description = "=the [[w:C (programming language)|C programming language]] and related [[technology|technologies]]",
parents = {"computer languages"},
}
labels["canals"] = {
type = "name,related-to",
parents = {"technology", "transport"},
}
labels["carpentry tools"] = {
type = "type",
parents = {"tools", "carpentry"},
}
labels["carriages"] = {
type = "type",
parents = {"vehicles"},
}
labels["clocks"] = {
type = "type",
parents = {"machines", "timekeeping"},
}
labels["compilation"] = {
type = "related-to",
description = "=[[compilation]] of source code",
parents = {"computing"},
}
labels["computer graphics"] = {
type = "related-to",
description = "default no singularize",
parents = {"computing"},
wpcat = true,
commonscat = true,
}
labels["computer hardware"] = {
type = "related-to",
parents = {"electronics", "computing"},
}
labels["computer languages"] = {
type = "name",
description = "[[computer language]]s and computer [[programming language]]s",
parents = {"programming"},
}
labels["computer security"] = {
type = "related-to",
parents = {"computing", "security"},
}
labels["computing"] = {
type = "related-to",
parents = {"technology"},
}
labels["consumer electronics"] = {
type = "related-to",
description = "default no singularize",
parents = {"electronics"},
}
labels["connectors"] = {
type = "type",
parents = {"electronics"},
}
labels["construction vehicles"] = {
type = "type",
description = "=vehicles designed to be employed in building settlements",
parents = {"vehicles", "construction"},
}
labels["containers"] = {
type = "type",
parents = {"tools"},
}
labels["food and drink containers"] = {
type = "type",
description = "=[[container]]s for holding [[food]] or [[liquid]]s",
parents = {"containers", "food and drink"},
}
labels["cookware and bakeware"] = {
type = "type",
description = "=[[kitchenware]] used to prepare [[food]]",
parents = {"kitchenware"},
}
labels["cutlery"] = {
type = "type",
parents = {"kitchenware"},
}
labels["data modeling"] = {
type = "related-to",
parents = {"computing"},
}
labels["databases"] = {
type = "related-to",
parents = {"computing"},
}
labels["Dragon Ball"] = {
type = "related-to",
displaytitle = "''Dragon Ball''",
description = "=the Japanese media franchise ''{{w|Dragon Ball}}''",
parents = {"animation", "fantasy", "Japanese fiction"},
}
labels["e-mail"] = {
type = "related-to",
parents = {"Internet", "communication"},
}
labels["electron tubes"] = {
type = "related-to",
description = "=[[electron tube|electron tubes]] such as [[vacuum tube]]s/[[thermionic valve]]s",
parents = {"electronics"},
}
labels["electronics"] = {
type = "related-to",
description = "default no singularize",
parents = {"technology"},
}
labels["fans"] = {
type = "type",
additional = "{{also|Category:{{{langcode}}}:Fans (people)}}",
parents = {"tools"},
}
labels["fasteners"] = {
type = "type",
parents = {"tools"},
}
labels["farming tools"] = {
type = "type",
parents = {"tools", "agriculture"},
}
labels["file formats"] = {
type = "name",
parents = {"computing"},
}
labels["firearms"] = {
type = "related-to",
parents = {"weapons"},
}
labels["glasses"] = {
type = "related-to",
parents = {"eyewear"},
}
labels["graphical user interface"] = {
type = "related-to",
parents = {"computing"},
}
labels["Google"] = {
type = "related-to",
description = "={{w|Google}} or its products",
parents = {"World Wide Web"},
}
labels["gun mechanisms"] = {
type = "type",
description = "=[[firearm]] [[mechanism]]s or their parts",
parents = {"firearms", "mechanisms"},
}
labels["home appliances"] = {
type = "type",
parents = {"machines", "home"},
}
labels["horse tack"] = {
type = "type",
parents = {"tools", "horses", "animal riding"},
}
labels["HTML"] = {
type = "related-to",
description = "=[[HTML]], [[XHTML]], and related [[technology|technologies]]",
parents = {"computer languages", "World Wide Web"},
}
labels["Huawei"] = {
type = "related-to",
description = "={{w|Huawei}} or its products",
parents = {"technology", "mobile phones"},
}
labels["IBM"] = {
type = "related-to",
description = "=the [[American]] [[technology]] and [[consulting]] [[firm]] {{w|IBM}}",
parents = {"technology", "computing"},
}
labels["Internet"] = {
type = "related-to",
description = "default with the",
parents = {"computing", "networking"},
}
labels["Internet memes"] = {
type = "related-to",
parents = {"comedy", "Internet", "memetics"},
}
labels["Java programming language"] = {
type = "related-to",
description = "=the [[w:Java (programming language)|Java programming language]] and related [[technology|technologies]]",
parents = {"computer languages"},
}
labels["JavaScript"] = {
type = "related-to",
description = "={{w|JavaScript}} and related [[technology|technologies]]",
parents = {"computer languages", "World Wide Web"},
}
labels["kitchenware"] = {
type = "type",
parents = {"tools", "cooking", "home appliances"},
}
labels["knives"] = {
type = "type",
parents = {"tools", "weapons"},
}
labels["knots"] = {
type = "type",
parents = {"fasteners"},
}
labels["Linux"] = {
type = "related-to",
description = "=[[Linux]]",
parents = {"Unix"},
}
labels["locks"] = {
type = "type",
parents = {"mechanisms", "fasteners", "security"},
}
labels["machines"] = {
type = "type",
parents = {"technology"},
}
labels["malware"] = {
type = "type",
parents = {"software", "computer security"},
}
labels["masonry tools"] = {
type = "type",
parents = {"tools", "masonry"},
}
labels["measuring instruments"] = {
type = "type",
parents = {"tools"},
}
labels["mechanisms"] = {
type = "type",
parents = {"machines"},
}
labels["medical equipment"] = {
type = "type",
parents = {"tools", "medicine"},
}
labels["metallurgy"] = {
type = "related-to",
parents = {"technology"},
}
labels["metalworking tools"] = {
type = "type",
parents = {"tools", "metalworking"},
}
labels["microelectronics"] = {
type = "related-to",
parents = {"electronics"},
}
labels["Microsoft"] = {
type = "related-to",
description = "={{w|Microsoft Corporation}}",
parents = {"computing"},
}
labels["military vehicles"] = {
type = "type",
parents = {"vehicles", "military"},
}
labels["milling"] = {
type = "related-to",
parents = {"agriculture"},
}
labels["motorcycles"] = {
type = "related-to",
parents = {"vehicles"},
}
labels["nanotechnology"] = {
type = "related-to",
parents = {"technology"},
}
labels["networking"] = {
type = "related-to",
parents = {"computing"},
}
labels["newsgroups"] = {
type = "related-to",
description = "=[[Usenet]] [[newsgroup]]s",
parents = {"Usenet"},
}
labels["Nintendo"] = {
type = "related-to",
description = "{{{langname}}} terms either deriving from or relating to {{w|Nintendo}} or its products",
parents = {"video games"},
}
labels["object-oriented programming"] = {
type = "related-to",
description = "default wikify",
parents = {"programming"},
}
labels["plumbing"] = {
type = "related-to",
parents = {"technology"},
}
labels["polearms"] = {
type = "type",
parents = {"weapons"},
}
labels["Pokémon"] = {
type = "related-to",
displaytitle = "''Pokémon''",
description = "=''{{w|Pokémon}}''",
parents = {"Nintendo", "Japanese fiction"},
}
labels["programming"] = {
type = "related-to",
description = "=[[computer programming]]",
parents = {"computing", "software engineering"},
}
labels["pumps"] = {
type = "type",
parents = {"mechanisms"},
}
labels["regular expressions"] = {
type = "related-to",
description = "{{{langname}}} terms used in [[regular expression]]s.",
parents = {"computing", "programming"},
}
labels["ropes and cords"] = {
type = "type",
parents = {"tools", "fasteners"},
}
labels["saws"] = {
type = "type",
parents = {"tools"},
}
labels["Sega"] = {
type = "related-to",
description = "{{{langname}}} terms either deriving from or relating to {{w|Sega}} or its products",
parents = {"video games"},
}
labels["semiconductors"] = {
type = "related-to",
parents = {"electronics"},
}
labels["simple machines"] = {
type = "type",
parents = {"machines"},
}
labels["software"] = {
type = "related-to",
parents = {"computing", "media"},
}
labels["spacecraft"] = {
type = "related-to",
parents = {"vehicles", "space travel"},
}
labels["spears"] = {
type = "type",
parents = {"weapons"},
}
labels["stationery"] = {
type = "related-to",
parents = {"tools"},
}
labels["swords"] = {
type = "type",
parents = {"weapons"},
}
labels["tools"] = {
type = "type",
parents = {"technology"},
}
labels["trapping"] = {
type = "related-to",
description = "=the business of [[trapper]]s",
parents = {"hunting"},
}
labels["typing keyboards"] = {
type = "related-to",
description = "=[[keyboards]] used as [[writing]] [[instrument]]s",
parents = {"writing instruments"},
}
labels["Unicode"] = {
type = "related-to",
parents = {"computing"},
}
labels["Unix"] = {
type = "related-to",
parents = {"computing"},
}
labels["Usenet"] = {
type = "related-to",
parents = {"Internet"},
}
labels["vehicles"] = {
type = "related-to",
parents = {"machines", "transport"},
}
labels["video compression"] = {
type = "related-to",
parents = {"computing"},
}
labels["visualization"] = {
type = "related-to",
parents = {"computing", "interdisciplinary fields"},
}
labels["warships"] = {
type = "type",
parents = {"watercraft", "military"},
}
labels["watercraft"] = {
type = "type",
description = "=[[watercraft]]: [[ship]]s, [[boat]]s, or any other vehicle that moves on or through the water",
parents = {"vehicles", "nautical"},
}
labels["weapons"] = {
type = "type",
parents = {"tools", "hunting", "military"},
}
labels["websites"] = {
type = "name,type",
parents = {"World Wide Web"},
}
labels["wiki"] = {
type = "related-to",
description = "=[[wiki]]s",
parents = {"World Wide Web"},
}
labels["Wikimedia"] = {
type = "related-to",
description = "={{w|Wikimedia}}-specific [[wiki]]s such as {{w|Wikipedia}} and {{w|Wiktionary}}",
parents = {"wiki"},
}
labels["World Wide Web"] = {
type = "related-to",
description = "default with the",
parents = {"Internet"},
}
labels["writing instruments"] = {
type = "type",
parents = {"tools", "writing", "stationery"},
}
labels["quantum computing"] = {
type = "related-to",
parents = {"computing", "quantum mechanics"},
}
return labels
41ph3apfr48cfr5l7ktui8zymtvvt7a
Mòideal:category tree/topic/Thesaurus
828
17907
88812
2026-07-23T19:36:52Z
en>WingerBot
0
explicit 'description = "default"' unnecessary; remove it (manually assisted)
88812
Scribunto
text/plain
local labels = {}
-- THESE CATEGORIES ARE ONLY INTENDED FOR USE IN THE THESAURUS!
labels["thesaurus-only categories"] = {
type = "grouping",
description = "{{error|do not use this category}}",
parents = {"all topics"},
}
-- closest existing label: ????
labels["aggression"] = {
type = "related-to",
parents = {"human behaviour", "anger"},
thesaurusonly = true,
}
labels["concepts"] = {
type = "grouping",
description = "{{{langname}}} terms grouped into conceptual categories",
parents = {"thesaurus-only categories"},
thesaurusonly = true,
}
-- closest existing label: None (handled by POS categories like "LANG offensive terms")
labels["abusive language"] = {
type = "related-to",
parents = {"communication"},
thesaurusonly = true,
}
-- closest existing label: ?????
labels["change"] = {
type = "related-to",
description = "=[[change]] and [[stasis]]",
parents = {"concepts"},
thesaurusonly = true,
}
-- closest existing label: ?????
labels["connection"] = {
type = "related-to",
parents = {"concepts"},
thesaurusonly = true,
}
-- closest existing label: "metaphysics" or "ontology" - but this is broader in scope
labels["existence"] = {
type = "related-to",
parents = {"concepts"},
thesaurusonly = true,
}
-- closest existing label: ?????
labels["harmony and opposition"] = {
type = "related-to",
parents = {"concepts"},
thesaurusonly = true,
}
-- closest existing label: ????
labels["intellectual faculties"] = {
type = "related-to",
parents = {"human"},
thesaurusonly = true,
}
-- closest existing label: None (handled by the POS category "LANG interjections")
labels["interjections"] = {
type = "related-to",
parents = {"communication"},
thesaurusonly = true,
}
-- closest existing label: "drugs" - but this is broader in scope
labels["intoxication and intoxicants"] = {
type = "related-to",
description = "=[[intoxication]] and [[intoxicants]]",
parents = {"thesaurus-only categories"},
thesaurusonly = true,
}
-- closest existing label: "love" - but the thesaurus cat incorporates friendship etc
labels["love and affection"] = {
type = "related-to",
parents = {"emotions"},
thesaurusonly = true,
}
-- closest existing label: ????
labels["morality"] = {
type = "related-to",
parents = {"personal qualities", "concepts"},
thesaurusonly = true,
}
-- closest existing label: ?????
labels["order"] = {
type = "related-to",
description = "{{{langname}}} terms describing the order and disorder in which things occur or are found; terms to do with the sequential and chaotic nature of things",
parents = {"concepts"},
thesaurusonly = true,
}
-- closest existing label: "personality" - but we use that cat for personality traits, while "personal qualities" is for transient states one can be in (e.g. lucky, drunk)
labels["personal qualities"] = {
type = "related-to",
description = "{{{langname}}} terms describing transient states a person can be in (e.g. lucky, drunk)",
parents = {"thesaurus-only categories"},
thesaurusonly = true,
}
-- closest existing label: ?????
labels["possession"] = {
type = "related-to",
parents = {"concepts"},
thesaurusonly = true,
}
-- closest existing label: ?????
labels["power"] = {
type = "related-to",
parents = {"concepts"},
thesaurusonly = true,
}
-- closest existing label: "quantity" - but we don't want this category to be under Mathematics, these are not mathematical concepts
labels["quantities"] = {
type = "related-to",
parents = {"concepts"},
thesaurusonly = true,
}
-- closest existing label: ?????
labels["relative qualities"] = {
type = "related-to",
parents = {"concepts"},
thesaurusonly = true,
}
-- closest existing label: ????
labels["social acts"] = {
type = "related-to",
parents = {"human behaviour", "communication"},
thesaurusonly = true,
}
-- closest existing label: ?????
labels["status"] = {
type = "related-to",
parents = {"concepts"},
thesaurusonly = true,
}
-- closest existing label: "transport" - but this is much broader, containing verbs of motion and the like
labels["travel and movement"] = {
type = "related-to",
parents = {"concepts", "human behaviour"},
thesaurusonly = true,
}
-- closest existing label: ?????
labels["volition"] = {
type = "related-to",
parents = {"concepts"},
thesaurusonly = true,
}
return labels
8s0s2aee5axq3paz3lsbirgkpqsskmr
88813
88812
2026-08-06T20:16:28Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/topic/Thesaurus]]
88812
Scribunto
text/plain
local labels = {}
-- THESE CATEGORIES ARE ONLY INTENDED FOR USE IN THE THESAURUS!
labels["thesaurus-only categories"] = {
type = "grouping",
description = "{{error|do not use this category}}",
parents = {"all topics"},
}
-- closest existing label: ????
labels["aggression"] = {
type = "related-to",
parents = {"human behaviour", "anger"},
thesaurusonly = true,
}
labels["concepts"] = {
type = "grouping",
description = "{{{langname}}} terms grouped into conceptual categories",
parents = {"thesaurus-only categories"},
thesaurusonly = true,
}
-- closest existing label: None (handled by POS categories like "LANG offensive terms")
labels["abusive language"] = {
type = "related-to",
parents = {"communication"},
thesaurusonly = true,
}
-- closest existing label: ?????
labels["change"] = {
type = "related-to",
description = "=[[change]] and [[stasis]]",
parents = {"concepts"},
thesaurusonly = true,
}
-- closest existing label: ?????
labels["connection"] = {
type = "related-to",
parents = {"concepts"},
thesaurusonly = true,
}
-- closest existing label: "metaphysics" or "ontology" - but this is broader in scope
labels["existence"] = {
type = "related-to",
parents = {"concepts"},
thesaurusonly = true,
}
-- closest existing label: ?????
labels["harmony and opposition"] = {
type = "related-to",
parents = {"concepts"},
thesaurusonly = true,
}
-- closest existing label: ????
labels["intellectual faculties"] = {
type = "related-to",
parents = {"human"},
thesaurusonly = true,
}
-- closest existing label: None (handled by the POS category "LANG interjections")
labels["interjections"] = {
type = "related-to",
parents = {"communication"},
thesaurusonly = true,
}
-- closest existing label: "drugs" - but this is broader in scope
labels["intoxication and intoxicants"] = {
type = "related-to",
description = "=[[intoxication]] and [[intoxicants]]",
parents = {"thesaurus-only categories"},
thesaurusonly = true,
}
-- closest existing label: "love" - but the thesaurus cat incorporates friendship etc
labels["love and affection"] = {
type = "related-to",
parents = {"emotions"},
thesaurusonly = true,
}
-- closest existing label: ????
labels["morality"] = {
type = "related-to",
parents = {"personal qualities", "concepts"},
thesaurusonly = true,
}
-- closest existing label: ?????
labels["order"] = {
type = "related-to",
description = "{{{langname}}} terms describing the order and disorder in which things occur or are found; terms to do with the sequential and chaotic nature of things",
parents = {"concepts"},
thesaurusonly = true,
}
-- closest existing label: "personality" - but we use that cat for personality traits, while "personal qualities" is for transient states one can be in (e.g. lucky, drunk)
labels["personal qualities"] = {
type = "related-to",
description = "{{{langname}}} terms describing transient states a person can be in (e.g. lucky, drunk)",
parents = {"thesaurus-only categories"},
thesaurusonly = true,
}
-- closest existing label: ?????
labels["possession"] = {
type = "related-to",
parents = {"concepts"},
thesaurusonly = true,
}
-- closest existing label: ?????
labels["power"] = {
type = "related-to",
parents = {"concepts"},
thesaurusonly = true,
}
-- closest existing label: "quantity" - but we don't want this category to be under Mathematics, these are not mathematical concepts
labels["quantities"] = {
type = "related-to",
parents = {"concepts"},
thesaurusonly = true,
}
-- closest existing label: ?????
labels["relative qualities"] = {
type = "related-to",
parents = {"concepts"},
thesaurusonly = true,
}
-- closest existing label: ????
labels["social acts"] = {
type = "related-to",
parents = {"human behaviour", "communication"},
thesaurusonly = true,
}
-- closest existing label: ?????
labels["status"] = {
type = "related-to",
parents = {"concepts"},
thesaurusonly = true,
}
-- closest existing label: "transport" - but this is much broader, containing verbs of motion and the like
labels["travel and movement"] = {
type = "related-to",
parents = {"concepts", "human behaviour"},
thesaurusonly = true,
}
-- closest existing label: ?????
labels["volition"] = {
type = "related-to",
parents = {"concepts"},
thesaurusonly = true,
}
return labels
8s0s2aee5axq3paz3lsbirgkpqsskmr
Mòideal:category tree/topic/Time
828
17908
88814
2026-07-23T19:36:54Z
en>WingerBot
0
explicit 'description = "default"' unnecessary; remove it (manually assisted)
88814
Scribunto
text/plain
local labels = {}
labels["time"] = {
type = "related-to",
parents = {"all topics"},
}
labels["Anglo-Saxon calendar months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Anglo-Saxon calendar}}.",
parents = {"months", "lunar months"},
}
labels["Armenian calendar months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Armenian calendar}}.",
parents = {"months"},
}
labels["calendar"] = {
type = "related-to",
description = "default with the",
parents = {"timekeeping"},
}
labels["centuries"] = {
type = "name",
parents = {"calendar"},
}
labels["Bengali calendar months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Bengali calendar}}.",
parents = {"months"},
}
labels["Burmese zodiac signs"] = {
type = "name",
description = "{{{langname}}} names for the signs of the {{w|Burmese zodiac}}.",
parents = {"astrology", "calendar"},
}
labels["Chinese earthly branches"] = {
type = "name",
description = "=[[Chinese]] [[earthly branch]]es",
parents = {"Chinese sexagenary cycle terms"},
}
labels["Chinese heavenly stems"] = {
type = "name",
description = "=[[Chinese]] [[heavenly stem]]s",
parents = {"Chinese sexagenary cycle terms"},
}
labels["Chinese months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Chinese calendar}}.",
parents = {"months"},
}
labels["Chinese sexagenary cycle terms"] = {
type = "name",
description = "=[[Chinese]] {{w|sexagenary cycle}} [[term]]s",
parents = {"calendar"},
}
labels["Chinese zodiac signs"] = {
type = "name",
description = "default wikify",
parents = {"astrology", "calendar", "Chinese mythology"},
}
labels["Christmas"] = {
type = "related-to",
description = "default no singularize",
parents = {"holidays", "Christianity"},
}
labels["days of the Hindu calendar"] = {
type = "name",
description = "{{{langname}}} names for the [[day]]s of the {{w|Hindu calendar}}.",
parents = {"periodic occurrences", "Hinduism"},
}
labels["days of the week"] = {
type = "name",
description = "=the [[Appendix:Days of the week|days of the week]]",
parents = {"periodic occurrences"},
}
labels["decades"] = {
type = "name",
parents = {"calendar"},
}
labels["Easter"] = {
type = "related-to",
parents = {"holidays", "Christianity"},
}
labels["Egyptian calendar months"] = {
type = "name",
description = "{{{langname}}} names for the months of the ancient {{w|Egyptian calendar}}.",
parents = {"Ancient Egypt", "months"},
}
labels["festivals"] = {
type = "name,type",
parents = {"observances"},
}
labels["Gregorian calendar months"] = {
type = "name",
parents = {"months"},
}
labels["Halloween"] = {
type = "related-to",
parents = {"holidays"},
}
labels["Hanukkah"] = {
type = "related-to",
description = "default no singularize",
parents = {"holidays", "Judaism"},
}
labels["Hebrew calendar months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Hebrew calendar}}.",
parents = {"months"},
}
labels["Hindu Jovian years"] = {
type = "name",
description = "{{{langname}}} names for years in the cycle of [[Jovian year]]s in traditional calendars of [[Hinduism]].",
additional = "These are based on the movement of the planet Jupiter rather than that of the sun.",
parents = {"calendar", "Hinduism"},
}
labels["Hindu lunar calendar months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Hindu calendar|Hindu lunar calendar}}.",
parents = {"months", "Hinduism"},
}
labels["Hindu solar calendar months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Hindu calendar|Hindu solar calendar}}.",
parents = {"months", "Hinduism"},
}
labels["holidays"] = {
type = "name,type",
parents = {"observances"},
}
labels["Icelandic calendar months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Icelandic calendar}}.",
parents = {"months"},
}
labels["Islamic calendar months"] = {
type = "name",
parents = {"months", "Islam"},
}
labels["Japanese calendar months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Japanese calendar}}.",
parents = {"months"},
}
labels["Javanese calendar months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Javanese calendar}}.",
parents = {"months"},
}
labels["Kojoda months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Yoruba calendar}}.",
parents = {"months"},
}
labels["lunar months"] = {
type = "set",
parents = {"months"},
}
labels["Mesoamerican day signs"] = {
type = "name",
parents = {"symbols", "calendar"},
wp = "Aztec calendar",
}
labels["months"] = {
type = "set",
preceding = "{{also|Appendix:Months of the year}}",
description = "{{{langname}}} names of the [[month]]s of the [[year]].",
parents = {"periodic occurrences"},
wpcat = true,
commonscat = true,
}
labels["Nanakshahi calendar months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Nanakshahi calendar}}.",
parents = {"months"},
}
labels["Norse calendar months"] = {
type = "name",
parents = {"months"},
}
labels["observances"] = {
type = "set",
parents = {"calendar"},
}
labels["periodic occurrences"] = {
type = "set",
description = "=occurrences that repeat at certain intervals of time",
parents = {"time"},
}
labels["Persian months"] = {
type = "name",
parents = {"months"},
}
labels["Ramadan"] = {
type = "related-to",
parents = {"observances", "Islam", "Islamic calendar months"},
}
labels["seasons"] = {
type = "set",
parents = {"periodic occurrences", "nature"},
}
labels["Sinterklaas"] = {
type = "related-to",
description = "default no singularize",
parents = {"holidays", "mythological figures"},
}
labels["Chinese solar terms"] = {
type = "set",
parents = {"calendar", "Sun"},
}
labels["New Year"] = {
type = "related-to",
description = "=the [[Gregorian]] [[New Year]]",
parents = {"holidays"},
}
labels["Syrian Christian months"] = {
type = "name",
parents = {"months"},
}
labels["Tamil calendar months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Tamil calendar}}.",
parents = {"months"},
}
labels["time zones"] = {
type = "name",
parents = {"timekeeping"},
}
labels["Thanksgiving"] = {
type = "related-to",
parents = {"holidays"},
}
labels["timekeeping"] = {
type = "related-to",
parents = {"time"},
}
labels["times of day"] = {
type = "set",
description = "=[[time]]s of the [[day]]",
parents = {"periodic occurrences", "timekeeping"},
}
labels["day"] = {
type = "related-to",
description = "=the [[day]] ([[etymologically]] or [[semantically]])",
additional = "'''NOTE''': Do not include days of the week within this category. These should be in [[:Category:{{{langcode}}}:Days of the week]].",
parents = {"time"},
}
labels["night"] = {
type = "related-to",
description = "=the [[night]] ([[etymologically]] or [[semantically]])",
parents = {"time", "darkness"},
}
labels["years"] = {
type = "type",
parents = {"periodic occurrences"},
}
labels["past"] = {
type = "related-to",
description = "{{{langname}}} terms for the [[past]], or for events in the [[past]].",
additional = "NOTE: Do NOT include words that are exclusively verbs, or words related to the field of history or historiography, within this category.",
parents = {"time"},
}
labels["present"] = {
type = "related-to",
description = "{{{langname}}} terms for the [[present]], or for events in the [[present]].",
additional = "NOTE: Do NOT include words that are exclusively verbs, or words related to the field of history or historiography, within this category.",
parents = {"time"},
}
labels["future"] = {
type = "related-to",
description = "{{{langname}}} terms for the [[future]], or for events in the [[future]].",
additional = "NOTE: Do NOT include words that are exclusively verbs, or words related to the field of history or historiography, within this category.",
parents = {"time"},
}
labels["time travel"] = {
type = "related-to",
parents = {"time", "science fiction", "travel", "relativity"},
}
return labels
qf1mz3v3w3ifoh4tpwmr1hvq5z5ftlc
88815
88814
2026-08-06T20:16:37Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/topic/Time]]
88814
Scribunto
text/plain
local labels = {}
labels["time"] = {
type = "related-to",
parents = {"all topics"},
}
labels["Anglo-Saxon calendar months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Anglo-Saxon calendar}}.",
parents = {"months", "lunar months"},
}
labels["Armenian calendar months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Armenian calendar}}.",
parents = {"months"},
}
labels["calendar"] = {
type = "related-to",
description = "default with the",
parents = {"timekeeping"},
}
labels["centuries"] = {
type = "name",
parents = {"calendar"},
}
labels["Bengali calendar months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Bengali calendar}}.",
parents = {"months"},
}
labels["Burmese zodiac signs"] = {
type = "name",
description = "{{{langname}}} names for the signs of the {{w|Burmese zodiac}}.",
parents = {"astrology", "calendar"},
}
labels["Chinese earthly branches"] = {
type = "name",
description = "=[[Chinese]] [[earthly branch]]es",
parents = {"Chinese sexagenary cycle terms"},
}
labels["Chinese heavenly stems"] = {
type = "name",
description = "=[[Chinese]] [[heavenly stem]]s",
parents = {"Chinese sexagenary cycle terms"},
}
labels["Chinese months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Chinese calendar}}.",
parents = {"months"},
}
labels["Chinese sexagenary cycle terms"] = {
type = "name",
description = "=[[Chinese]] {{w|sexagenary cycle}} [[term]]s",
parents = {"calendar"},
}
labels["Chinese zodiac signs"] = {
type = "name",
description = "default wikify",
parents = {"astrology", "calendar", "Chinese mythology"},
}
labels["Christmas"] = {
type = "related-to",
description = "default no singularize",
parents = {"holidays", "Christianity"},
}
labels["days of the Hindu calendar"] = {
type = "name",
description = "{{{langname}}} names for the [[day]]s of the {{w|Hindu calendar}}.",
parents = {"periodic occurrences", "Hinduism"},
}
labels["days of the week"] = {
type = "name",
description = "=the [[Appendix:Days of the week|days of the week]]",
parents = {"periodic occurrences"},
}
labels["decades"] = {
type = "name",
parents = {"calendar"},
}
labels["Easter"] = {
type = "related-to",
parents = {"holidays", "Christianity"},
}
labels["Egyptian calendar months"] = {
type = "name",
description = "{{{langname}}} names for the months of the ancient {{w|Egyptian calendar}}.",
parents = {"Ancient Egypt", "months"},
}
labels["festivals"] = {
type = "name,type",
parents = {"observances"},
}
labels["Gregorian calendar months"] = {
type = "name",
parents = {"months"},
}
labels["Halloween"] = {
type = "related-to",
parents = {"holidays"},
}
labels["Hanukkah"] = {
type = "related-to",
description = "default no singularize",
parents = {"holidays", "Judaism"},
}
labels["Hebrew calendar months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Hebrew calendar}}.",
parents = {"months"},
}
labels["Hindu Jovian years"] = {
type = "name",
description = "{{{langname}}} names for years in the cycle of [[Jovian year]]s in traditional calendars of [[Hinduism]].",
additional = "These are based on the movement of the planet Jupiter rather than that of the sun.",
parents = {"calendar", "Hinduism"},
}
labels["Hindu lunar calendar months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Hindu calendar|Hindu lunar calendar}}.",
parents = {"months", "Hinduism"},
}
labels["Hindu solar calendar months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Hindu calendar|Hindu solar calendar}}.",
parents = {"months", "Hinduism"},
}
labels["holidays"] = {
type = "name,type",
parents = {"observances"},
}
labels["Icelandic calendar months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Icelandic calendar}}.",
parents = {"months"},
}
labels["Islamic calendar months"] = {
type = "name",
parents = {"months", "Islam"},
}
labels["Japanese calendar months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Japanese calendar}}.",
parents = {"months"},
}
labels["Javanese calendar months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Javanese calendar}}.",
parents = {"months"},
}
labels["Kojoda months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Yoruba calendar}}.",
parents = {"months"},
}
labels["lunar months"] = {
type = "set",
parents = {"months"},
}
labels["Mesoamerican day signs"] = {
type = "name",
parents = {"symbols", "calendar"},
wp = "Aztec calendar",
}
labels["months"] = {
type = "set",
preceding = "{{also|Appendix:Months of the year}}",
description = "{{{langname}}} names of the [[month]]s of the [[year]].",
parents = {"periodic occurrences"},
wpcat = true,
commonscat = true,
}
labels["Nanakshahi calendar months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Nanakshahi calendar}}.",
parents = {"months"},
}
labels["Norse calendar months"] = {
type = "name",
parents = {"months"},
}
labels["observances"] = {
type = "set",
parents = {"calendar"},
}
labels["periodic occurrences"] = {
type = "set",
description = "=occurrences that repeat at certain intervals of time",
parents = {"time"},
}
labels["Persian months"] = {
type = "name",
parents = {"months"},
}
labels["Ramadan"] = {
type = "related-to",
parents = {"observances", "Islam", "Islamic calendar months"},
}
labels["seasons"] = {
type = "set",
parents = {"periodic occurrences", "nature"},
}
labels["Sinterklaas"] = {
type = "related-to",
description = "default no singularize",
parents = {"holidays", "mythological figures"},
}
labels["Chinese solar terms"] = {
type = "set",
parents = {"calendar", "Sun"},
}
labels["New Year"] = {
type = "related-to",
description = "=the [[Gregorian]] [[New Year]]",
parents = {"holidays"},
}
labels["Syrian Christian months"] = {
type = "name",
parents = {"months"},
}
labels["Tamil calendar months"] = {
type = "name",
description = "{{{langname}}} names for the months of the {{w|Tamil calendar}}.",
parents = {"months"},
}
labels["time zones"] = {
type = "name",
parents = {"timekeeping"},
}
labels["Thanksgiving"] = {
type = "related-to",
parents = {"holidays"},
}
labels["timekeeping"] = {
type = "related-to",
parents = {"time"},
}
labels["times of day"] = {
type = "set",
description = "=[[time]]s of the [[day]]",
parents = {"periodic occurrences", "timekeeping"},
}
labels["day"] = {
type = "related-to",
description = "=the [[day]] ([[etymologically]] or [[semantically]])",
additional = "'''NOTE''': Do not include days of the week within this category. These should be in [[:Category:{{{langcode}}}:Days of the week]].",
parents = {"time"},
}
labels["night"] = {
type = "related-to",
description = "=the [[night]] ([[etymologically]] or [[semantically]])",
parents = {"time", "darkness"},
}
labels["years"] = {
type = "type",
parents = {"periodic occurrences"},
}
labels["past"] = {
type = "related-to",
description = "{{{langname}}} terms for the [[past]], or for events in the [[past]].",
additional = "NOTE: Do NOT include words that are exclusively verbs, or words related to the field of history or historiography, within this category.",
parents = {"time"},
}
labels["present"] = {
type = "related-to",
description = "{{{langname}}} terms for the [[present]], or for events in the [[present]].",
additional = "NOTE: Do NOT include words that are exclusively verbs, or words related to the field of history or historiography, within this category.",
parents = {"time"},
}
labels["future"] = {
type = "related-to",
description = "{{{langname}}} terms for the [[future]], or for events in the [[future]].",
additional = "NOTE: Do NOT include words that are exclusively verbs, or words related to the field of history or historiography, within this category.",
parents = {"time"},
}
labels["time travel"] = {
type = "related-to",
parents = {"time", "science fiction", "travel", "relativity"},
}
return labels
qf1mz3v3w3ifoh4tpwmr1hvq5z5ftlc
Mòideal:category tree/topic/Transport
828
17909
88816
2026-07-23T19:36:55Z
en>WingerBot
0
explicit 'description = "default"' unnecessary; remove it (manually assisted)
88816
Scribunto
text/plain
local labels = {}
labels["transport"] = {
type = "related-to",
parents = {"all topics"},
}
labels["animal riding"] = {
type = "related-to",
description = "=riding [[animal]]s",
parents = {"transport", "horses"},
}
labels["automotive"] = {
type = "related-to",
parents = {"transport"},
}
labels["cycling"] = {
type = "related-to",
parents = {"transport"},
}
labels["nautical"] = {
type = "related-to",
parents = {"transport"},
}
labels["navigation"] = {
type = "related-to",
parents = {"transport"},
}
labels["rail transportation"] = {
type = "related-to",
parents = {"transport"},
}
labels["road transport"] = {
type = "related-to",
parents = {"transport"},
}
labels["roads"] = {
type = "related-to",
parents = {"road transport"},
}
labels["sailing"] = {
type = "related-to",
parents = {"nautical"},
}
labels["ship parts"] = {
type = "type",
parents = {"nautical"},
}
labels["ship prefixes"] = {
type = "name",
parents = {"nautical"},
}
labels["shipping"] = {
type = "related-to",
description = "=the transportation of goods by ship",
additional = "For the [[fandom]] sense of {{l|en|ship|shipping|id=fandomverb}}, see [[:Category:Shipping (fandom)]].",
parents = {"transport", "nautical"},
}
labels["space travel"] = {
type = "related-to",
parents = {"transport", "astronautics"},
}
labels["walking"] = {
type = "related-to",
parents = {"transport"},
}
return labels
0x4n5ofohmfmwnlquqmv56vl094lg7z
88817
88816
2026-08-06T20:16:47Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/topic/Transport]]
88816
Scribunto
text/plain
local labels = {}
labels["transport"] = {
type = "related-to",
parents = {"all topics"},
}
labels["animal riding"] = {
type = "related-to",
description = "=riding [[animal]]s",
parents = {"transport", "horses"},
}
labels["automotive"] = {
type = "related-to",
parents = {"transport"},
}
labels["cycling"] = {
type = "related-to",
parents = {"transport"},
}
labels["nautical"] = {
type = "related-to",
parents = {"transport"},
}
labels["navigation"] = {
type = "related-to",
parents = {"transport"},
}
labels["rail transportation"] = {
type = "related-to",
parents = {"transport"},
}
labels["road transport"] = {
type = "related-to",
parents = {"transport"},
}
labels["roads"] = {
type = "related-to",
parents = {"road transport"},
}
labels["sailing"] = {
type = "related-to",
parents = {"nautical"},
}
labels["ship parts"] = {
type = "type",
parents = {"nautical"},
}
labels["ship prefixes"] = {
type = "name",
parents = {"nautical"},
}
labels["shipping"] = {
type = "related-to",
description = "=the transportation of goods by ship",
additional = "For the [[fandom]] sense of {{l|en|ship|shipping|id=fandomverb}}, see [[:Category:Shipping (fandom)]].",
parents = {"transport", "nautical"},
}
labels["space travel"] = {
type = "related-to",
parents = {"transport", "astronautics"},
}
labels["walking"] = {
type = "related-to",
parents = {"transport"},
}
return labels
0x4n5ofohmfmwnlquqmv56vl094lg7z
Mòideal:category tree/topic/Physical actions
828
17910
88818
2026-07-23T19:36:42Z
en>WingerBot
0
explicit 'description = "default"' unnecessary; remove it (manually assisted)
88818
Scribunto
text/plain
local labels = {}
labels["physical actions"] = {
type = "set",
parents = {"all topics"},
}
labels["physical impact"] = {
type = "set",
description = "{{{langname}}} terms for [[physical]] [[impact]] on something",
parents = {"physical actions"},
}
labels["hit"] = {
type = "set",
description = "{{{langname}}} terms for [[hitting]] something",
parents = {"physical impact"},
}
labels["rub"] = {
type = "set",
description = "{{{langname}}} terms for [[rubbing]] something, moving something smooth against something else, in order to make it smooth or clean",
parents = {"physical impact"},
}
return labels
7muuojzvu8rrqziv9eazpgengxy5qjm
88819
88818
2026-08-06T20:16:57Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/topic/Physical_actions]]
88818
Scribunto
text/plain
local labels = {}
labels["physical actions"] = {
type = "set",
parents = {"all topics"},
}
labels["physical impact"] = {
type = "set",
description = "{{{langname}}} terms for [[physical]] [[impact]] on something",
parents = {"physical actions"},
}
labels["hit"] = {
type = "set",
description = "{{{langname}}} terms for [[hitting]] something",
parents = {"physical impact"},
}
labels["rub"] = {
type = "set",
description = "{{{langname}}} terms for [[rubbing]] something, moving something smooth against something else, in order to make it smooth or clean",
parents = {"physical impact"},
}
return labels
7muuojzvu8rrqziv9eazpgengxy5qjm
Mòideal:category tree/fam/alg
828
17911
88820
2026-07-26T06:02:54Z
en>Benwing
0
Changed protection settings for "[[Module:category tree/fam/alg]]": per existence of [[WT:CLTR]]; changes to these modules should go through a vetting process ([Edit=Allow only autopatrollers] (indefinite) [Move=Allow only autopatrollers] (indefinite))
88820
Scribunto
text/plain
local labels = {}
labels["transitive animate verbs"] = {
description = "{{{langname}}} transitive verbs with an animate object, commonly abbreviated VTA.",
breadcrumb = "transitive animate",
parents = {"verbs by inflection type", {name = "transitive verbs", sort = "animate"}},
}
labels["transitive inanimate verbs"] = {
description = "{{{langname}}} transitive verbs with an inanimate object, commonly abbreviated VTI.",
breadcrumb = "transitive inanimate",
parents = {"verbs by inflection type", {name = "transitive verbs", sort = "inanimate"}},
}
labels["animate intransitive verbs"] = {
description = "{{{langname}}} intransitive verbs with an animate subject, commonly abbreviated VAI.",
breadcrumb = "animate intransitive",
parents = {"verbs by inflection type", {name = "intransitive verbs", sort = "animate"}},
}
labels["inanimate intransitive verbs"] = {
description = "{{{langname}}} intransitive verbs with an inanimate subject, commonly abbreviated VII.",
breadcrumb = "inanimate intransitive",
parents = {"verbs by inflection type", {name = "intransitive verbs", sort = "inanimate"}},
}
-- Add 'umbrella_parents' key if not already present.
for key, data in pairs(labels) do
if not data.umbrella_parents then
data.umbrella_parents = "Terms by grammatical category subcategories by language"
end
end
return {LABELS = labels}
85qdgi9z8fm0fttnf1ltxgr2wyrtvw0
88821
88820
2026-08-06T20:17:32Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/fam/alg]]
88820
Scribunto
text/plain
local labels = {}
labels["transitive animate verbs"] = {
description = "{{{langname}}} transitive verbs with an animate object, commonly abbreviated VTA.",
breadcrumb = "transitive animate",
parents = {"verbs by inflection type", {name = "transitive verbs", sort = "animate"}},
}
labels["transitive inanimate verbs"] = {
description = "{{{langname}}} transitive verbs with an inanimate object, commonly abbreviated VTI.",
breadcrumb = "transitive inanimate",
parents = {"verbs by inflection type", {name = "transitive verbs", sort = "inanimate"}},
}
labels["animate intransitive verbs"] = {
description = "{{{langname}}} intransitive verbs with an animate subject, commonly abbreviated VAI.",
breadcrumb = "animate intransitive",
parents = {"verbs by inflection type", {name = "intransitive verbs", sort = "animate"}},
}
labels["inanimate intransitive verbs"] = {
description = "{{{langname}}} intransitive verbs with an inanimate subject, commonly abbreviated VII.",
breadcrumb = "inanimate intransitive",
parents = {"verbs by inflection type", {name = "intransitive verbs", sort = "inanimate"}},
}
-- Add 'umbrella_parents' key if not already present.
for key, data in pairs(labels) do
if not data.umbrella_parents then
data.umbrella_parents = "Terms by grammatical category subcategories by language"
end
end
return {LABELS = labels}
85qdgi9z8fm0fttnf1ltxgr2wyrtvw0
Mòideal:category tree/fam/gem
828
17912
88822
2026-07-26T06:03:10Z
en>Benwing
0
Changed protection settings for "[[Module:category tree/fam/gem]]": per existence of [[WT:CLTR]]; changes to these modules should go through a vetting process ([Edit=Allow only autopatrollers] (indefinite) [Move=Allow only autopatrollers] (indefinite))
88822
Scribunto
text/plain
local labels = {}
------- GERMANIC VERB CLASSES -------
labels["strong verbs"] = {
description = "{{{langname}}} verbs that do not use a dental suffix to mark the past tense and past participle, instead using vowel change ([[ablaut]]) and often a suffix ''-(e)n'' in the past participle.",
breadcrumb = "strong",
parents = {"verbs by inflection type"},
}
labels["weak verbs"] = {
description = "{{{langname}}} verbs that display dental suffixes in their past tense conjugated forms.",
breadcrumb = "weak",
parents = {"verbs by inflection type"},
}
labels["preterite-present verbs"] = {
description = "{{{langname}}} verbs that inflect in the present tense like the past tense of strong verbs.",
breadcrumb = "preterite-present",
parents = {"verbs by inflection type"},
}
labels["class 1 strong verbs"] = {
description = "{{{langname}}} class 1 strong verbs, where the [[ablaut]] vowel was followed by ''-y-'' in Proto-Indo-European.",
breadcrumb = "class 1",
parents = {{name = "strong verbs", sort = "1"}},
}
labels["class 1 weak verbs"] = {
description = "{{{langname}}} class 1 weak verbs, where the stem was followed by {{ic|/i/~/j/}} in Proto-Germanic (or {{ic|/ij/}} after a heavy stem, due to {{w|Sievers' Law}}).",
additional = "This triggered [[umlaut]] in most daughter languages, as well as gemination of the final consonant in light stems in West Germanic.",
breadcrumb = "class 1",
parents = {{name = "weak verbs", sort = "1"}},
}
labels["class 1 weak j-present verbs"] = {
description = "{{{langname}}} class 1 weak verbs with {{ic|/i/~/j/~/ij/}} in Proto-Germanic only in the present tense, but not elsewhere.",
additional = "Most class 1 weak verbs had {{ic|/i/}} in the past tense and past participle, leading to " ..
"[[umlaut]] throughout the verb in daughter languages with umlaut. A few archaic verbs, however, lacked " ..
"this [[interfix]], with the [[dental]] consonant of the ending attached directly to the stem. Original " ..
"instances of this are the reflexes of English [[seek]], [[think]], [[buy]] and [[work]], with apparently " ..
"irregular pasts ''sought'', ''thought'', ''bought'' and archaic ''wrought'', and it was often extended " ..
"to other verbs in various daughter languages (e.g. the [[Old English]] reflexes of [[sell]], [[tell]], " ..
"[[teach]] and formerly [[reach]], with apparently irregular pasts ''sold'', ''told'', ''taught'' and " ..
"now-obsolete ''raught''). The apparent reversal of umlaut in the past tense is sometimes called " ..
"{{m|de|Rückumlaut|lit=backwards umlaut}} in Germanic studies.",
breadcrumb = "''j''-present",
parents = {{name = "class 1 weak verbs", sort = "j-present"}},
}
labels["class 1 weak heavy-stem verbs"] = {
description = "{{{langname}}} class 1 weak verbs with a heavy stem in Proto-Germanic, i.e. a stem containing a long vowel or ending in two consonants.",
additional = "Such verbs had the {{w|Sievers' Law}} variant interfix {{ic|/ij/}} between the stem and endings " ..
"in the present tense, which evolved differently from light-stem verbs in most daughter languages, which " ..
"had an interfix {{ic|/i/~/j/}} in the present tense. Note that some verbs with multisyllabic stems were " ..
"treated as heavy-stem and some as light-stem, depending on the analysis of the metrical feet of the stem.",
breadcrumb_and_first_sort_key = "heavy-stem",
parents = "class 1 weak verbs",
}
labels["class 1 weak light-stem verbs"] = {
description = "{{{langname}}} class 1 weak verbs with a light stem in Proto-Germanic, i.e. a stem containing a short vowel and ending in only one consonant.",
additional = "Such verbs had the {{w|Sievers' Law}} variant interfix {{ic|/i/~/j/}} between the stem and " ..
"endings in the present tense, which evolved differently from heavy-stem verbs in most daughter languages, " ..
"which had an interfix {{ic|/ij/}} in the present tense. Note that some verbs with multisyllabic stems were " ..
"treated as heavy-stem and some as light-stem, depending on the analysis of the metrical feet of the stem.",
breadcrumb_and_first_sort_key = "light-stem",
parents = "class 1 weak verbs",
}
labels["class 2 strong verbs"] = {
description = "{{{langname}}} class 2 strong verbs, where the [[ablaut]] vowel was followed by ''-w-'' in Proto-Indo-European.",
breadcrumb = "class 2",
parents = {{name = "strong verbs", sort = "2"}},
}
labels["class 2a strong verbs"] = {
description = "{{{langname}}} class 2 strong verbs where the [[ablaut]] vowel was ''*eu'' in Proto-Germanic.",
breadcrumb = "class 2a",
parents = {{name = "class 2 strong verbs", sort = "1"}},
}
labels["class 2b strong verbs"] = {
description = "{{{langname}}} class 2 strong verbs where the [[ablaut]] vowel was ''*ū'' in Proto-Germanic.",
breadcrumb = "class 2b",
parents = {{name = "class 2 strong verbs", sort = "2"}},
}
labels["class 2 weak verbs"] = {
description = "{{{langname}}} class 2 weak verbs, where the stem was followed by ''*ō'' in Proto-Germanic.",
breadcrumb = "class 2",
parents = {{name = "weak verbs", sort = "2"}},
}
labels["class 3 weak verbs"] = {
description = "{{{langname}}} class 3 weak verbs, where the stem was followed by ''*ai''~''*ā'' in Proto-Germanic, which was generalized to ''*ē'' in West Germanic.",
breadcrumb = "class 3",
parents = {{name = "weak verbs", sort = "3"}},
}
labels["class 3 strong verbs"] = {
description = "{{{langname}}} class 3 strong verbs, where the [[ablaut]] vowel was followed by a [[consonant cluster]] in Proto-Indo-European.",
breadcrumb = "class 3",
parents = {{name = "strong verbs", sort = "3"}},
}
labels["class 3a strong verbs"] = {
description = "{{{langname}}} class 3 strong verbs where the [[consonant cluster]] following the [[ablaut]] vowel begins with a nasal consonant.",
breadcrumb = "class 3a",
parents = {{name = "class 3 strong verbs", sort = "1"}},
}
labels["class 3b strong verbs"] = {
description = "{{{langname}}} class 3 strong verbs where the [[consonant cluster]] following the [[ablaut]] vowel begins with a lateral consonant or velar fricative.",
breadcrumb = "class 3b",
parents = {{name = "class 3 strong verbs", sort = "2"}},
}
labels["class 3c strong verbs"] = {
description = "{{{langname}}} class 3 strong verbs where the [[consonant cluster]] following the [[ablaut]] vowel begins with a rhotic consonant.",
breadcrumb = "class 3c",
parents = {{name = "class 3 strong verbs", sort = "3"}},
}
labels["class 4 strong verbs"] = {
description = "{{{langname}}} class 4 strong verbs, where the [[ablaut]] vowel was followed by a [[sonorant]] (''m'', ''n'', ''l'', ''r'') but no other consonant in Proto-Indo-European.",
breadcrumb = "class 4",
parents = {{name = "strong verbs", sort = "4"}},
}
labels["class 4 weak verbs"] = {
description = "{{{langname}}} class 4 weak verbs, where the stem was followed by ''*n'' in Proto-Germanic.",
breadcrumb = "class 4",
parents = {{name = "weak verbs", sort = "4"}},
}
labels["class 5 strong verbs"] = {
description = "{{{langname}}} class 5 strong verbs, where the [[ablaut]] vowel was followed by [[consonant]] other than a [[sonorant]] in Proto-Indo-European.",
breadcrumb = "class 5",
parents = {{name = "strong verbs", sort = "5"}},
}
labels["class 5 strong j-present verbs"] = {
description = "{{{langname}}} class 5 strong verbs with a {{IPAchar|/j/}} suffix in the present tense in Proto-Germanic.",
additional = "This [[umlaut]]ed the root vowel to {{ic|/i/}}, and caused gemination of the stem-final consonant in the West Germanic languages. The {{ic|/j/}} was maintained in Gothic, Old Norse (and modern Icelandic) and Old Saxon, but otherwise dropped.",
breadcrumb = "''j''-present",
parents = {{name = "class 5 strong verbs", sort = "j-present"}},
}
labels["class 6 strong verbs"] = {
description = "{{{langname}}} class 6 strong verbs, with the stem vowel ''-a-'' (and usually a single stem-final consonant), except those where it is followed by a sonorant and another consonant (this combination was considered a diphthong in PIE and therefore belonged to class 7).",
additional = "The Proto-Indo-European origin of this class is not securely known.",
breadcrumb = "class 6",
parents = {{name = "strong verbs", sort = "6"}},
}
labels["class 6 strong j-present verbs"] = {
description = "{{{langname}}} class 6 strong verbs with a {{IPAchar|/j/}} suffix in the present tense in Proto-Germanic.",
additional = "This caused gemination of the stem-final consonant in the West Germanic languages, and [[umlaut]] of the root vowel in most languages. The {{ic|/j/}} was maintained in Gothic, Old Norse (and modern Icelandic) and Old Saxon, but otherwise dropped.",
breadcrumb = "''j''-present",
parents = {{name = "class 6 strong verbs", sort = "j-present"}},
}
labels["class 7 strong verbs"] = {
description = "{{{langname}}} class 7 strong verbs, which retained their reduplication in the past tense in Proto-Germanic.",
breadcrumb = "class 7",
parents = {{name = "strong verbs", sort = "7"}},
}
labels["class 7a strong verbs"] = {
description = "{{{langname}}} class 7 strong verbs where the root vowel was ''*ai'' in Proto-Germanic, analogous to class 1.",
breadcrumb = "class 7a",
parents = {{name = "class 7 strong verbs", sort = "a"}},
}
labels["class 7b strong verbs"] = {
description = "{{{langname}}} class 7 strong verbs where the root vowel was ''*au'' in Proto-Germanic, analogous to class 2.",
breadcrumb = "class 7b",
parents = {{name = "class 7 strong verbs", sort = "b"}},
}
labels["class 7c strong verbs"] = {
description = "{{{langname}}} class 7 strong verbs where the root vowel was ''*a'' followed by a [[consonant cluster]] in Proto-Germanic, analogous to class 3.",
breadcrumb = "class 7c",
parents = {{name = "class 7 strong verbs", sort = "c"}},
}
labels["class 7d strong verbs"] = {
description = "{{{langname}}} class 7 strong verbs where the root vowel was ''*ē'' in Proto-Germanic.",
breadcrumb = "class 7d",
parents = {{name = "class 7 strong verbs", sort = "d"}},
}
labels["class 7e strong verbs"] = {
description = "{{{langname}}} class 7 strong verbs where the root vowel was ''*ō'' in Proto-Germanic.",
breadcrumb = "class 7e",
parents = {{name = "class 7 strong verbs", sort = "e"}},
}
labels["class 7 strong j-present verbs"] = {
description = "{{{langname}}} class 7 strong verbs with a {{IPAchar|/j/}} suffix in the present tense in Proto-Germanic.",
additional = "This caused [[umlaut]] of the root vowel in most languages. The {{ic|/j/}} was maintained in Gothic, Old Norse (and modern Icelandic) and Old Saxon, but otherwise dropped.",
breadcrumb = "''j''-present",
parents = {{name = "class 7 strong verbs", sort = "j-present"}},
}
-- Add 'umbrella_parents' key if not already present.
for key, data in pairs(labels) do
if not data.umbrella_parents then
data.umbrella_parents = "Terms by grammatical category subcategories by language"
end
end
return {LABELS = labels}
88k7sky1bf589e9xdsynl8pwglofaen
88823
88822
2026-08-06T20:17:35Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/fam/gem]]
88822
Scribunto
text/plain
local labels = {}
------- GERMANIC VERB CLASSES -------
labels["strong verbs"] = {
description = "{{{langname}}} verbs that do not use a dental suffix to mark the past tense and past participle, instead using vowel change ([[ablaut]]) and often a suffix ''-(e)n'' in the past participle.",
breadcrumb = "strong",
parents = {"verbs by inflection type"},
}
labels["weak verbs"] = {
description = "{{{langname}}} verbs that display dental suffixes in their past tense conjugated forms.",
breadcrumb = "weak",
parents = {"verbs by inflection type"},
}
labels["preterite-present verbs"] = {
description = "{{{langname}}} verbs that inflect in the present tense like the past tense of strong verbs.",
breadcrumb = "preterite-present",
parents = {"verbs by inflection type"},
}
labels["class 1 strong verbs"] = {
description = "{{{langname}}} class 1 strong verbs, where the [[ablaut]] vowel was followed by ''-y-'' in Proto-Indo-European.",
breadcrumb = "class 1",
parents = {{name = "strong verbs", sort = "1"}},
}
labels["class 1 weak verbs"] = {
description = "{{{langname}}} class 1 weak verbs, where the stem was followed by {{ic|/i/~/j/}} in Proto-Germanic (or {{ic|/ij/}} after a heavy stem, due to {{w|Sievers' Law}}).",
additional = "This triggered [[umlaut]] in most daughter languages, as well as gemination of the final consonant in light stems in West Germanic.",
breadcrumb = "class 1",
parents = {{name = "weak verbs", sort = "1"}},
}
labels["class 1 weak j-present verbs"] = {
description = "{{{langname}}} class 1 weak verbs with {{ic|/i/~/j/~/ij/}} in Proto-Germanic only in the present tense, but not elsewhere.",
additional = "Most class 1 weak verbs had {{ic|/i/}} in the past tense and past participle, leading to " ..
"[[umlaut]] throughout the verb in daughter languages with umlaut. A few archaic verbs, however, lacked " ..
"this [[interfix]], with the [[dental]] consonant of the ending attached directly to the stem. Original " ..
"instances of this are the reflexes of English [[seek]], [[think]], [[buy]] and [[work]], with apparently " ..
"irregular pasts ''sought'', ''thought'', ''bought'' and archaic ''wrought'', and it was often extended " ..
"to other verbs in various daughter languages (e.g. the [[Old English]] reflexes of [[sell]], [[tell]], " ..
"[[teach]] and formerly [[reach]], with apparently irregular pasts ''sold'', ''told'', ''taught'' and " ..
"now-obsolete ''raught''). The apparent reversal of umlaut in the past tense is sometimes called " ..
"{{m|de|Rückumlaut|lit=backwards umlaut}} in Germanic studies.",
breadcrumb = "''j''-present",
parents = {{name = "class 1 weak verbs", sort = "j-present"}},
}
labels["class 1 weak heavy-stem verbs"] = {
description = "{{{langname}}} class 1 weak verbs with a heavy stem in Proto-Germanic, i.e. a stem containing a long vowel or ending in two consonants.",
additional = "Such verbs had the {{w|Sievers' Law}} variant interfix {{ic|/ij/}} between the stem and endings " ..
"in the present tense, which evolved differently from light-stem verbs in most daughter languages, which " ..
"had an interfix {{ic|/i/~/j/}} in the present tense. Note that some verbs with multisyllabic stems were " ..
"treated as heavy-stem and some as light-stem, depending on the analysis of the metrical feet of the stem.",
breadcrumb_and_first_sort_key = "heavy-stem",
parents = "class 1 weak verbs",
}
labels["class 1 weak light-stem verbs"] = {
description = "{{{langname}}} class 1 weak verbs with a light stem in Proto-Germanic, i.e. a stem containing a short vowel and ending in only one consonant.",
additional = "Such verbs had the {{w|Sievers' Law}} variant interfix {{ic|/i/~/j/}} between the stem and " ..
"endings in the present tense, which evolved differently from heavy-stem verbs in most daughter languages, " ..
"which had an interfix {{ic|/ij/}} in the present tense. Note that some verbs with multisyllabic stems were " ..
"treated as heavy-stem and some as light-stem, depending on the analysis of the metrical feet of the stem.",
breadcrumb_and_first_sort_key = "light-stem",
parents = "class 1 weak verbs",
}
labels["class 2 strong verbs"] = {
description = "{{{langname}}} class 2 strong verbs, where the [[ablaut]] vowel was followed by ''-w-'' in Proto-Indo-European.",
breadcrumb = "class 2",
parents = {{name = "strong verbs", sort = "2"}},
}
labels["class 2a strong verbs"] = {
description = "{{{langname}}} class 2 strong verbs where the [[ablaut]] vowel was ''*eu'' in Proto-Germanic.",
breadcrumb = "class 2a",
parents = {{name = "class 2 strong verbs", sort = "1"}},
}
labels["class 2b strong verbs"] = {
description = "{{{langname}}} class 2 strong verbs where the [[ablaut]] vowel was ''*ū'' in Proto-Germanic.",
breadcrumb = "class 2b",
parents = {{name = "class 2 strong verbs", sort = "2"}},
}
labels["class 2 weak verbs"] = {
description = "{{{langname}}} class 2 weak verbs, where the stem was followed by ''*ō'' in Proto-Germanic.",
breadcrumb = "class 2",
parents = {{name = "weak verbs", sort = "2"}},
}
labels["class 3 weak verbs"] = {
description = "{{{langname}}} class 3 weak verbs, where the stem was followed by ''*ai''~''*ā'' in Proto-Germanic, which was generalized to ''*ē'' in West Germanic.",
breadcrumb = "class 3",
parents = {{name = "weak verbs", sort = "3"}},
}
labels["class 3 strong verbs"] = {
description = "{{{langname}}} class 3 strong verbs, where the [[ablaut]] vowel was followed by a [[consonant cluster]] in Proto-Indo-European.",
breadcrumb = "class 3",
parents = {{name = "strong verbs", sort = "3"}},
}
labels["class 3a strong verbs"] = {
description = "{{{langname}}} class 3 strong verbs where the [[consonant cluster]] following the [[ablaut]] vowel begins with a nasal consonant.",
breadcrumb = "class 3a",
parents = {{name = "class 3 strong verbs", sort = "1"}},
}
labels["class 3b strong verbs"] = {
description = "{{{langname}}} class 3 strong verbs where the [[consonant cluster]] following the [[ablaut]] vowel begins with a lateral consonant or velar fricative.",
breadcrumb = "class 3b",
parents = {{name = "class 3 strong verbs", sort = "2"}},
}
labels["class 3c strong verbs"] = {
description = "{{{langname}}} class 3 strong verbs where the [[consonant cluster]] following the [[ablaut]] vowel begins with a rhotic consonant.",
breadcrumb = "class 3c",
parents = {{name = "class 3 strong verbs", sort = "3"}},
}
labels["class 4 strong verbs"] = {
description = "{{{langname}}} class 4 strong verbs, where the [[ablaut]] vowel was followed by a [[sonorant]] (''m'', ''n'', ''l'', ''r'') but no other consonant in Proto-Indo-European.",
breadcrumb = "class 4",
parents = {{name = "strong verbs", sort = "4"}},
}
labels["class 4 weak verbs"] = {
description = "{{{langname}}} class 4 weak verbs, where the stem was followed by ''*n'' in Proto-Germanic.",
breadcrumb = "class 4",
parents = {{name = "weak verbs", sort = "4"}},
}
labels["class 5 strong verbs"] = {
description = "{{{langname}}} class 5 strong verbs, where the [[ablaut]] vowel was followed by [[consonant]] other than a [[sonorant]] in Proto-Indo-European.",
breadcrumb = "class 5",
parents = {{name = "strong verbs", sort = "5"}},
}
labels["class 5 strong j-present verbs"] = {
description = "{{{langname}}} class 5 strong verbs with a {{IPAchar|/j/}} suffix in the present tense in Proto-Germanic.",
additional = "This [[umlaut]]ed the root vowel to {{ic|/i/}}, and caused gemination of the stem-final consonant in the West Germanic languages. The {{ic|/j/}} was maintained in Gothic, Old Norse (and modern Icelandic) and Old Saxon, but otherwise dropped.",
breadcrumb = "''j''-present",
parents = {{name = "class 5 strong verbs", sort = "j-present"}},
}
labels["class 6 strong verbs"] = {
description = "{{{langname}}} class 6 strong verbs, with the stem vowel ''-a-'' (and usually a single stem-final consonant), except those where it is followed by a sonorant and another consonant (this combination was considered a diphthong in PIE and therefore belonged to class 7).",
additional = "The Proto-Indo-European origin of this class is not securely known.",
breadcrumb = "class 6",
parents = {{name = "strong verbs", sort = "6"}},
}
labels["class 6 strong j-present verbs"] = {
description = "{{{langname}}} class 6 strong verbs with a {{IPAchar|/j/}} suffix in the present tense in Proto-Germanic.",
additional = "This caused gemination of the stem-final consonant in the West Germanic languages, and [[umlaut]] of the root vowel in most languages. The {{ic|/j/}} was maintained in Gothic, Old Norse (and modern Icelandic) and Old Saxon, but otherwise dropped.",
breadcrumb = "''j''-present",
parents = {{name = "class 6 strong verbs", sort = "j-present"}},
}
labels["class 7 strong verbs"] = {
description = "{{{langname}}} class 7 strong verbs, which retained their reduplication in the past tense in Proto-Germanic.",
breadcrumb = "class 7",
parents = {{name = "strong verbs", sort = "7"}},
}
labels["class 7a strong verbs"] = {
description = "{{{langname}}} class 7 strong verbs where the root vowel was ''*ai'' in Proto-Germanic, analogous to class 1.",
breadcrumb = "class 7a",
parents = {{name = "class 7 strong verbs", sort = "a"}},
}
labels["class 7b strong verbs"] = {
description = "{{{langname}}} class 7 strong verbs where the root vowel was ''*au'' in Proto-Germanic, analogous to class 2.",
breadcrumb = "class 7b",
parents = {{name = "class 7 strong verbs", sort = "b"}},
}
labels["class 7c strong verbs"] = {
description = "{{{langname}}} class 7 strong verbs where the root vowel was ''*a'' followed by a [[consonant cluster]] in Proto-Germanic, analogous to class 3.",
breadcrumb = "class 7c",
parents = {{name = "class 7 strong verbs", sort = "c"}},
}
labels["class 7d strong verbs"] = {
description = "{{{langname}}} class 7 strong verbs where the root vowel was ''*ē'' in Proto-Germanic.",
breadcrumb = "class 7d",
parents = {{name = "class 7 strong verbs", sort = "d"}},
}
labels["class 7e strong verbs"] = {
description = "{{{langname}}} class 7 strong verbs where the root vowel was ''*ō'' in Proto-Germanic.",
breadcrumb = "class 7e",
parents = {{name = "class 7 strong verbs", sort = "e"}},
}
labels["class 7 strong j-present verbs"] = {
description = "{{{langname}}} class 7 strong verbs with a {{IPAchar|/j/}} suffix in the present tense in Proto-Germanic.",
additional = "This caused [[umlaut]] of the root vowel in most languages. The {{ic|/j/}} was maintained in Gothic, Old Norse (and modern Icelandic) and Old Saxon, but otherwise dropped.",
breadcrumb = "''j''-present",
parents = {{name = "class 7 strong verbs", sort = "j-present"}},
}
-- Add 'umbrella_parents' key if not already present.
for key, data in pairs(labels) do
if not data.umbrella_parents then
data.umbrella_parents = "Terms by grammatical category subcategories by language"
end
end
return {LABELS = labels}
88k7sky1bf589e9xdsynl8pwglofaen
Mòideal:category tree/fam/jpx
828
17913
88824
2026-07-26T06:03:25Z
en>Benwing
0
Changed protection settings for "[[Module:category tree/fam/jpx]]": per existence of [[WT:CLTR]]; changes to these modules should go through a vetting process ([Edit=Allow only autopatrollers] (indefinite) [Move=Allow only autopatrollers] (indefinite))
88824
Scribunto
text/plain
local labels = {}
local handlers = {}
local m_str_utils = require("Module:string utilities")
local concat = table.concat
local full_link = require("Module:links").full_link
local insert = table.insert
local Hani_sort = require("Module:Hani-sortkey").makeSortKey
local match = m_str_utils.match
local sort = table.sort
local tag_text = require("Module:script_utilities").tag_text
local ucfirst = m_str_utils.ucfirst
local Hira = require("Module:scripts").getByCode("Hira")
local Jpan = require("Module:scripts").getByCode("Jpan")
local kana_to_romaji = require("Module:Hrkt-translit").tr
local m_numeric = require("Module:ConvertNumeric")
local kana_capture = "([-" .. require("Module:ja/data/range").kana .. "・]+)"
local yomi_data = require("Module:kanjitab/data")
labels["adnominals"] = {
description = "{{{langname}}} adnominals, or {{ja-r|連%体%詞|れん%たい%し}}, which modify nouns, and do not conjugate or [[predicate#Verb|predicate]].",
parents = {{name = "{{{langcat}}}", raw = true}},
}
labels["hiragana"] = {
description = "{{{langname}}} terms with hiragana {{mdash}} {{ja-r|平%仮%名|ひら%が%な}} {{mdash}} forms, sorted by conventional hiragana sequence. The hiragana form is a [[phonetic]] representation of that word. " ..
"Wiktionary represents {{{langname}}}-language segments in three ways: in normal form (with [[kanji]], if appropriate), in [[hiragana]] " ..
"form (this differs from kanji form only when the segment contains kanji), and in [[romaji]] form.",
additional = "''See also'' [[:Category:{{{langname}}} katakana]]",
toc_template = "Hira-categoryTOC",
toc_template_full = "Hira-categoryTOC/full",
parents = {
{name = "{{{langcat}}}", raw = true},
"Category:Hiragana script characters",
}
}
labels["historical hiragana"] = {
description = "{{{langname}}} historical [[hiragana]].",
additional = "''See also'' [[:Category:{{{langname}}} historical katakana]].",
toc_template = "Hira-categoryTOC",
toc_template_full = "Hira-categoryTOC/full",
parents = {
"hiragana",
{name = "{{{langcat}}}", raw = true},
"Category:Hiragana script characters",
}
}
labels["katakana"] = {
description = "{{{langname}}} terms with katakana {{mdash}} {{ja-r|片%仮%名|かた%か%な}} {{mdash}} forms, sorted by conventional katakana sequence. Katakana is used primarily for transliterations of foreign words, including old Chinese hanzi not used in [[shinjitai]].",
additional = "''See also'' [[:Category:{{{langname}}} hiragana]]",
toc_template = "Kana-categoryTOC",
toc_template_full = "Kana-categoryTOC/full",
parents = {
{name = "{{{langcat}}}", raw = true},
"Category:Katakana script characters",
}
}
labels["historical katakana"] = {
description = "{{{langname}}} historical [[katakana]].",
additional = "''See also'' [[:Category:{{{langname}}} historical hiragana]].",
toc_template = "Kana-categoryTOC",
toc_template_full = "Kana-categoryTOC/full",
parents = {
"katakana",
{name = "{{{langcat}}}", raw = true},
"Category:Katakana script characters",
}
}
labels["terms spelled with mixed kana"] = {
description = "{{{langname}}} terms which combine [[hiragana]] and [[katakana]] characters, potentially with [[kanji]] too.",
parents = {
{name = "{{{langcat}}}", raw = true},
"hiragana",
"katakana",
},
}
labels["kanji"] = {
topright = "{{wp|Kanji}}",
description = "{{{langname}}} symbols of the Han logographic script, which can represent sounds or convey meanings directly.",
toc_template = "Hani-categoryTOC",
umbrella = "Han characters",
parents = "logograms",
}
labels["kanji by reading"] = {
description = "{{{langname}}} kanji categorized by reading.",
parents = {{name = "kanji", sort = "reading"}},
}
labels["makurakotoba"] = {
topright = "{{wp|Makurakotoba}}",
description = "{{{langname}}} idioms used in poetry to introduce specific words.",
parents = {"idioms"},
}
labels["terms by kanji readings"] = {
description = "{{{langname}}} categories grouped with regard to the readings of the kanji with which they are spelled.",
parents = {{name = "{{{langcat}}}", raw = true}},
}
labels["terms by reading pattern"] = {
description = "{{{langname}}} categories with terms grouped by their reading patterns.",
parents = {{name = "{{{langcat}}}", raw = true}},
}
labels["terms by number of kanji"] = {
description = "{{{langname}}} terms categorized by number of kanji.",
parents = {"terms by orthographic property"},
}
local function handle_onyomi_list(category, category_type, cat_yomi_type)
local onyomi, seen = {}, {}
for _, yomi in pairs(yomi_data) do
if not seen[yomi] and yomi.onyomi then
local yomi_catname = yomi[category_type]
if yomi_catname ~= false then
local yomi_type = yomi.type
if yomi_type ~= "on'yomi" and yomi_type ~= cat_yomi_type then
insert(onyomi, "[[:Category:{{{langname}}} " .. category:gsub("{{{yomi_catname}}}", yomi_catname) .. "]]")
end
end
end
seen[yomi] = true
end
sort(onyomi)
return onyomi
end
local function add_yomi_category(category, category_type, parent, description)
for _, yomi in pairs(yomi_data) do
local yomi_catname = yomi[category_type]
if yomi_catname ~= false then
local yomi_type = yomi.type
local yomi_desc = yomi.link or yomi_catname
if yomi.description then
yomi_desc = yomi_desc .. "; " .. yomi.description
end
local label = {
description = description .. " " .. yomi_desc .. ".",
breadcrumb = yomi_type,
parents = {{name = parent, sort = yomi_catname}},
}
if yomi.onyomi then
local onyomi = handle_onyomi_list(category, category_type, yomi_type)
label.additional = "Categories of terms with " ..
(yomi_type == "on'yomi" and "more" or "other") ..
" specific types of on'yomi readings can be found in the following categories:\n* " .. concat(onyomi, "\n* ")
if yomi_type ~= "on'yomi" then
insert(label.parents, 1, {
name = (category:gsub("{{{yomi_catname}}}", yomi_data.on[category_type])),
sort = yomi_catname
})
end
end
labels[category:gsub("{{{yomi_catname}}}", yomi_catname)] = label
end
end
end
add_yomi_category(
"terms read with {{{yomi_catname}}}",
"reading_category",
"terms by reading pattern",
"{{{langname}}} terms read with"
)
add_yomi_category(
"terms spelled with kanji with {{{yomi_catname}}} readings",
"kanji_category",
"terms by kanji reading type",
"{{{langname}}} categories with terms that are spelled with one or more kanji read with"
)
labels["terms with missing yomi"] = {
description = "{{{langname}}} terms where at least one [[Appendix:Japanese glossary#yomi|yomi]] is missing from {{tl|{{{langcode}}}-kanjitab}}.",
hidden = true,
can_be_empty = true,
parents = {"entry maintenance"},
}
labels["terms with IPA pronunciation with pitch accent"] = {
description = "{{{langname}}} terms with pronunciations that have {{w|Japanese pitch accent|pitch accent}} specified.",
additional = "Pitch accent can be specified in {{tl|{{{langcode}}}-pron}} with the {{code|=acc=}} parameter.",
can_be_empty = true,
parents = {"entry maintenance", "pitch accent"},
}
labels["terms with IPA pronunciation missing pitch accent"] = {
description = "{{{langname}}} terms with pronunciations that do not have a {{w|Japanese pitch accent|pitch accent}} specified.",
additional = "Pitch accent can be specified in {{tl|{{{langcode}}}-pron}} with the {{code|=acc=}} parameter.",
hidden = true,
can_be_empty = true,
parents = {"entry maintenance"},
}
labels["pitch accent"] = {
description = "{{{langname}}} terms regarding {{w|Japanese pitch accent|pitch accent}} pronunciation.",
can_be_empty = true,
parents = {{name = "{{{langcat}}}", raw = true}},
}
labels["terms with Heiban pitch accent (Tōkyō)"] = {
description = "{{{langname}}} terms with pronunciations that are (Tōkyō) [[平板型|Heiban]] {{w|Japanese pitch accent|pitch accent}}.",
can_be_empty = true,
parents = {"pitch accent"}
}
labels["terms with Atamadaka pitch accent (Tōkyō)"] = {
description = "{{{langname}}} terms with pronunciations that are (Tōkyō) [[頭高型|Atamadaka]] {{w|Japanese pitch accent|pitch accent}}.",
can_be_empty = true,
parents = {"pitch accent"}
}
labels["terms with Nakadaka pitch accent (Tōkyō)"] = {
description = "{{{langname}}} terms with pronunciations that are (Tōkyō) [[中高型|Nakadaka]] {{w|Japanese pitch accent|pitch accent}}.",
can_be_empty = true,
parents = {"pitch accent"}
}
labels["terms with Odaka pitch accent (Tōkyō)"] = {
description = "{{{langname}}} terms with pronunciations that are (Tōkyō) [[尾高型|Odaka]] {{w|Japanese pitch accent|pitch accent}}.",
can_be_empty = true,
parents = {"pitch accent"}
}
labels["terms with complex pitch accent (Tōkyō)"] = {
description = "{{{langname}}} terms with pronunciations that are (Tōkyō) complex {{w|Japanese pitch accent|pitch accent}}, as in having more than one {{m|ja|アクセント句}}.",
can_be_empty = true,
parents = {"pitch accent"}
}
labels["pitch accent deaccenting before の"] = {
description = "{{{langname}}} terms with {{w|Japanese pitch accent|pitch accent}} pronunciations that have exceptional deaccenting or lack thereof before の ({{ja-deaccenting-before-no}}).",
can_be_empty = true,
parents = {"pitch accent"}
}
labels["terms with Odaka pitch accent not deaccented before の (Tōkyō)"] = {
description = "{{{langname}}} terms with pronunciations that are (Tōkyō) [[尾高型|Odaka]] {{w|Japanese pitch accent|pitch accent}} and do not become deaccented before の ({{ja-deaccenting-before-no}}).\n\nTerms are placed in this category using {{tl|ja-odaka-deaccent-exception}}.",
can_be_empty = true,
parents = {"pitch accent deaccenting before の"}
}
labels["terms with Nakadaka pitch accent deaccented before の (Tōkyō)"] = {
description = "{{{langname}}} terms with pronunciations that are (Tōkyō) [[中高型|Nakadaka]] {{w|Japanese pitch accent|pitch accent}} and become deaccented before の ({{ja-deaccenting-before-no}}).\n\nTerms are placed in this category using {{tl|ja-nakadaka-deaccent}}.",
can_be_empty = true,
parents = {"pitch accent deaccenting before の"}
}
labels["terms by kanji reading type"] = {
description = "{{{langname}}} categories with terms grouped with regard to the types of readings of the kanji with which " ..
"they are spelled; broadly, those of Chinese origin, {{ja-r|音|おん}} readings, and those of non-Chinese origin, {{ja-r|訓|くん}} readings.",
parents = {{name = "{{{langcat}}}", raw = true}},
}
labels["terms spelled with ateji"] = {
topright = "{{wp|Ateji}}",
description = "{{{langname}}} terms containing one or more [[Appendix:Japanese glossary#ateji|ateji]] {{mdash}} {{ja-r|当て字|あてじ}} {{mdash}} which are [[kanji]] used to represent sounds rather than meanings (though meaning may have some influence on which kanji are chosen).",
parents = {{name = "{{{langcat}}}", raw = true}},
}
labels["terms spelled with daiyōji"] = {
description = "Japanese terms spelled using [[Appendix:Japanese glossary#daiyouji|daiyōji]], categorized using {{temp|ja-daiyouji}}.",
parents = {"terms by etymology"},
}
labels["terms spelled with jukujikun"] = {
description = "{{{langname}}} terms containing one or more [[Appendix:Japanese glossary#jukujikun|jukujikun]] {{mdash}} {{ja-r|熟%字%訓|じゅく%じ%くん}} {{mdash}} which are [[kanji]] used to represent meanings rather than sounds.",
parents = {{name = "{{{langcat}}}", raw = true}},
}
local function add_grade_categories(grade, desc, wp, only_one, parent, sort)
local grade_kanji = grade .. " kanji"
local topright = wp and ("{{wp|%s}}"):format(ucfirst(grade_kanji)) or nil
labels[grade_kanji] = {
topright = topright,
description = "{{{langname}}} kanji " .. desc,
toc_template = "Hani-categoryTOC",
parents = {{
name = parent and (parent .. " kanji") or "kanji",
sort = sort or grade
}},
}
labels["terms spelled with " .. grade_kanji] = {
topright = topright,
description = "{{{langname}}} terms spelled with " .. (only_one and "at least one " or "") .. "kanji " .. desc,
parents = {{
name = parent and ("terms spelled with " .. parent .. " kanji") or "terms by orthographic property",
sort = sort or grade
}},
}
end
for i = 1, 6 do
local ord = m_numeric.ones_position_ord[i]
add_grade_categories(
ord .. " grade",
"taught in the " .. ord .. " grade of elementary school, as designated by the the official list of {{ja-r|教%育 漢%字|きょう%いく かん%じ|education kanji}}.",
false,
false,
"kyōiku",
i
)
end
add_grade_categories(
"kyōiku",
"on the official list of {{ja-r|教%育 漢%字|きょう%いく かん%じ|education kanji}}.",
true,
false,
"jōyō"
)
add_grade_categories(
"secondary school",
"on the official list of {{ja-r|常%用 漢%字|じょう%よう かん%じ|regular-use characters}} that are generally taught in secondary school.",
false,
false,
"jōyō"
)
add_grade_categories(
"jōyō",
"on the official list of {{ja-r|常%用 漢%字|じょう%よう かん%じ|regular-use characters}}.",
true,
false
)
add_grade_categories(
"tōyō",
"on the official list of {{ja-r|当%用 漢%字|とう%よう かん%じ|general-use characters}}, which was used from 1946{{ndash}}1981 until the publication of the list of {{ja-r|常%用 漢%字|じょう%よう かん%じ|regular-use characters}}.",
true,
false
)
add_grade_categories(
"jinmeiyō",
"on the official list of {{ja-r|人%名%用 漢%字|じん%めい%-よう かん%じ|kanji for use in personal names}}.",
true,
true
)
add_grade_categories(
"hyōgai",
"not included on the official list of {{ja-r|常%用 漢%字|じょう%よう かん%じ|regular-use characters}} or {{ja-r|人%名%用 漢%字|じん%めい%-よう かん%じ|kanji for use in personal names}}, known as {{ja-r|表%外 漢%字|ひょう%がい かん%じ}} or {{ja-r|表%外%字|ひょう%がい%じ|unlisted characters}}.",
true,
true
)
labels["terms with multiple readings"] = {
description = "{{{langname}}} terms with multiple pronunciations (hence multiple [[kana]] spellings).",
parents = {{name = "{{{langcat}}}", raw = true}},
}
labels["kanji readings by number of morae"] = {
description = "{{{langname}}} categories grouped with regard to the number of morae in their kanji readings.",
parents = {{name = "{{{langcat}}}", raw = true}},
}
labels["single-kanji terms"] = {
description = "{{{langname}}} terms written as a single kanji.",
parents = {
"terms by orthographic property",
{name = "terms with 1 kanji", sort = " "},
},
}
labels["kanji with kun readings missing okurigana designation"] = {
breadcrumb = "Kanji missing okurigana designation",
description = "{{{langname}}} kanji entries in which one or more kun readings entered into {{tl|{{{langcode}}}-readings}} is missing a hyphen denoting okurigana.",
toc_template = "Hani-categoryTOC",
hidden = true,
can_be_empty = true,
parents = {"entry maintenance"},
}
labels["terms by the individual characters in their historical spellings"] = {
breadcrumb = "Historical",
description = "{{{langname}}} terms categorized by whether their spellings in the {{w|historical kana orthography}} included certain individual characters.",
parents = {{name = "terms by their individual characters", sort = " "}},
}
labels["verbs without transitivity"] = {
description = "{{{langname}}} verbs missing the {{code|=tr=}} parameter from their headword templates.",
hidden = true,
can_be_empty = true,
parents = {"entry maintenance"},
}
labels["yojijukugo"] = {
topright = "{{wp|Yojijukugo}}",
description = "{{{langname}}} four-[[kanji]] compound terms, {{ja-r|四%字 熟%語|よ%じ じゅく%ご}}, with idiomatic meanings; typically derived from Classical Chinese, Buddhist scripture or traditional Japanese proverbs.",
additional = "Compare Chinese {{w|chengyu}} and Korean {{w|sajaseong-eo}}.",
umbrella = "four-character idioms",
parents = {"idioms"},
}
-- FIXME: Only works for 0 through 19.
local word_to_number = {}
for k, v in pairs(m_numeric.ones_position) do
word_to_number[v] = k
end
local periods = {
historical = true,
ancient = true,
}
local function get_period_text_and_reading_type_link(period, reading_type)
if period and not periods[period] then
return nil
end
local period_text = period and period .. " " or nil
-- Allow periods (historical or ancient) by themselves; they will parse as reading types.
if not period and periods[reading_type] then
return nil, reading_type
end
local reading_type_link = "[[Appendix:Japanese glossary#" .. reading_type .. "|" .. reading_type .. "]]"
return period_text, reading_type_link
end
local function get_sc(str)
return match(str:gsub("[%s%p]+", ""), "[^" .. Hira:getCharacters() .. "]") and Jpan or Hira
end
local function get_tagged_reading(reading, lang)
return tag_text(reading, lang, get_sc(reading))
end
local function get_reading_link(reading, lang, period, link)
local hist = periods[period]
reading = reading:gsub("[%.%-%s]+", "")
return full_link({
lang = lang,
sc = get_sc(reading),
term = link or reading:gsub("・", ""),
-- If we have okurigana, demarcate furigana.
alt = reading:gsub("^(.-)・", "<span style=\"border-top:1px solid;position:relative;padding:1px;\">%1<span style=\"position:absolute;top:0;bottom:67%%;right:0%%;border-right:1px solid;\"></span></span>"),
tr = kana_to_romaji((reading:gsub("・", ".")), lang:getCode(), nil, {keep_dot = true, hist = hist})
:gsub("^(.-)%.", "<u>%1</u>"),
pos = reading:find("・", 1, true) and get_tagged_reading((reading:gsub("^.-・", "~")), lang) or nil
}, "term")
end
local function is_on_subtype(reading_type)
return reading_type:find(".on$")
end
insert(handlers, function(data)
local n =data.label:match("^terms with ([1-9]%d*) kanji$")
if not n then
return
end
local sortkey = require("Module:category tree").numeral_sortkey(n, 2097152)
return {
breadcrumb = n,
description = ("{{{langname}}} terms containing exactly %d kanji."):format(n),
-- TODO: implement this using the same mechanism used to implement parents (i.e. avoiding the need for raw categories).
-- umbrella = {
-- breadcrumb = ("%d kanji"):format(n),
-- parents = {{name = "terms by number of kanji subcategories by language", sort = sortkey}},
-- },
parents = {{name = ("terms by number of kanji"), sort = sortkey}}
}
end)
insert(handlers, function(data)
local label_pref, kana = data.label:match("^(terms historically spelled with )" .. kana_capture .. "$")
if not kana then
return
end
local lang = data.lang
return {
description = "{{{langname}}} terms spelled with " .. get_reading_link(kana, lang, "historical") .. " in the {{w|historical kana orthography}}.",
displaytitle = "{{{langname}}} " .. label_pref .. get_tagged_reading(kana, lang),
breadcrumb = "historical",
parents = {
{name = "terms spelled with " .. kana, sort = " "},
{name = "terms by the individual characters in their historical spellings", sort = lang:makeSortKey(kana)}
},
umbrella = false,
}
end)
insert(handlers, function(data)
local count, plural = data.label:match("^kanji readings with (.+) mora(e?)$")
-- Make sure 'one' goes with singular and other numbers with plural.
if not count or (count == "one") ~= (plural == "") then
return
end
local num = word_to_number[count]
if not num then
return nil
end
return {
description = "{{{langname}}} kanji readings containing " .. count .. " mora" .. plural .. ".",
breadcrumb = num,
parents = {{name = "kanji readings by number of morae", sort = num}},
umbrella = false,
}
end)
insert(handlers, function(data)
local label_pref, period, reading_type, reading = match(data.label, "^(kanji with ([a-z]-) ?([%a']+) reading )" .. kana_capture .. "$")
if not period then
return
end
period = period ~= "" and period or nil
local period_text, reading_type_link = get_period_text_and_reading_type_link(period, reading_type)
if not reading_type_link then
return
end
local lang = data.lang
-- Compute parents.
local parents, breadcrumb = {}
if reading:find("・", 1, true) then
local okurigana = reading:match("・(.*)")
insert(parents, {
name = "kanji with " .. (period_text or "") .. reading_type .. " reading " .. reading:match("(.-)・"),
-- Sort by okurigana, since all coordinate categories will have the same furigana.
sort = (lang:makeSortKey(okurigana))
})
breadcrumb = "~" .. okurigana
else
insert(parents, {
name = "kanji by " .. (period_text or "") .. reading_type .. " reading",
sort = (lang:makeSortKey(reading))
})
breadcrumb = reading
end
if is_on_subtype(reading_type) then
insert(parents, {name = "kanji with " .. (period_text or "") .. "on reading " .. reading, sort = reading_type})
elseif period_text then
insert(parents, {name = "kanji with " .. period_text .. "reading " .. reading, sort = reading_type})
end
if not period_text then
insert(parents, {name = "kanji read as " .. reading, sort = reading_type})
end
return {
description = "{{{langname}}} [[kanji]] with the " .. (period_text or "") .. reading_type_link .. " reading " ..
get_reading_link(reading, lang, period or reading_type) .. ".",
displaytitle = "{{{langname}}} " .. label_pref .. get_tagged_reading(reading, lang),
breadcrumb = get_tagged_reading(breadcrumb, lang),
parents = parents,
umbrella = false,
}
end)
insert(handlers, function(data)
local period, reading_type = match(data.label, "^kanji by ([a-z]-) ?([%a']+) reading$")
if not period then
return
end
period = period ~= "" and period or nil
local period_text, reading_type_link = get_period_text_and_reading_type_link(period, reading_type)
if not reading_type_link then
return nil
end
-- Compute parents.
local parents = {
is_on_subtype(reading_type) and {name = "kanji by " .. (period_text or "") .. "on reading", sort = reading_type} or
period_text and {name = "kanji by " .. reading_type .. " reading", sort = period} or
{name = "kanji by reading", sort = reading_type}
}
if period_text then
insert(parents, {name = "kanji by " .. period_text .. "reading", sort = reading_type})
end
-- Compute description.
local description = "{{{langname}}} [[kanji]] categorized by " .. (period_text or "") .. reading_type_link .. " reading."
return {
description = description,
breadcrumb = (period_text or "") .. reading_type,
parents = parents,
umbrella = false,
}
end)
insert(handlers, function(data)
local label_pref, reading = match(data.label, "^(kanji read as )" .. kana_capture .. "$")
if not reading then
return
end
local args = require("Module:parameters").process(data.args, {
["histconsol"] = true,
})
local lang = data.lang
local parents, breadcrumb = {}
if reading:find("・", 1, true) then
local okurigana = reading:match("・(.*)")
insert(parents, {
name = "kanji read as " .. reading:match("(.-)・"),
-- Sort by okurigana, since all coordinate categories will have the same furigana.
sort = (lang:makeSortKey(okurigana))
})
breadcrumb = "~" .. okurigana
else
insert(parents, {
name = "kanji by reading",
sort = (lang:makeSortKey(reading))
})
breadcrumb = reading
end
local addl
local period_text
if args.histconsol then
period_text = "historical"
addl = ("This is a [[Wikipedia:Historical kana orthography|historical]] [[Wikipedia:Kanazukai|reading]], now " ..
"consolidated with the [[Wikipedia:Modern kana usage|modern reading]] of " ..
get_reading_link(args.histconsol, lang, nil, ("Category:Japanese kanji read as %s"):format(args.histconsol)) .. ".")
end
return {
description = "{{{langname}}} [[kanji]] read as " .. get_reading_link(reading, lang, period_text) .. ".",
additional = addl,
displaytitle = "{{{langname}}} " .. label_pref .. get_tagged_reading(reading, lang),
breadcrumb = get_tagged_reading(breadcrumb, lang),
parents = parents,
umbrella = false,
}, true
end)
insert(handlers, function(data)
local label_pref, reading = match(data.label, "^(terms spelled with kanji read as )" .. kana_capture .. "$")
if not reading then
return
end
-- Compute parents.
local lang = data.lang
local sort_key = (lang:makeSortKey(reading))
local mora_count = require("Module:ja").count_morae(reading)
local mora_count_words = m_numeric.spell_number(tostring(mora_count))
local parents = {
{name = "terms by kanji readings", sort = sort_key},
{name = "kanji readings with " .. mora_count_words .. " mora" .. (mora_count > 1 and "e" or ""), sort = sort_key},
{name = "kanji read as " .. reading, sort = " "},
}
local tagged_reading = get_tagged_reading(reading, lang)
return {
description = "{{{langname}}} terms that contain kanji that exhibit a reading of " .. get_reading_link(reading, lang) ..
" in those terms prior to any sound changes.",
displaytitle = "{{{langname}}} " .. label_pref .. tagged_reading,
breadcrumb = tagged_reading,
parents = parents,
umbrella = false,
}
end)
insert(handlers, function(data)
local kanji, reading = match(data.label, "^terms spelled with (.) read as " .. kana_capture .. "$")
if not kanji then
return nil
end
local args = require("Module:parameters").process(data.args, {
[1] = {list = true},
})
local lang = data.lang
if #args[1] == 0 then
error("For categories of the form \"" .. lang:getCanonicalName() ..
" terms spelled with KANJI read as READING\", at least one reading type (e.g. <code>kun</code> or <code>on</code>) must be specified using <code>1=</code>, <code>2=</code>, <code>3=</code>, etc.")
end
local yomi_types, parents = {}, {}
for _, yomi, category in ipairs(args[1]) do
local yomi_data = yomi_data[yomi]
if not yomi_data then
error("The yomi type \"" .. yomi .. "\" is not recognized.")
end
category = yomi_data.kanji_category
if not category then
error("The yomi type \"" .. yomi .. "\" is not valid for this type of category.")
end
insert(yomi_types, yomi_data.link)
insert(parents, {
name = "terms spelled with kanji with " .. category .. " readings",
sort = (lang:makeSortKey(reading))
})
end
insert(parents, 1, {name = "terms spelled with " .. kanji, sort = (lang:makeSortKey(reading))})
insert(parents, 2, {name = "terms spelled with kanji read as " .. reading, sort = Hani_sort(kanji)})
yomi_types = (#yomi_types > 1 and "one of " or "") .. "its " ..
require("Module:table").serialCommaJoin(yomi_types, {conj = "or"}) ..
" reading" .. (#yomi_types > 1 and "s" or "")
local tagged_kanji = get_tagged_reading(kanji, lang)
local tagged_reading = get_tagged_reading(reading, lang)
return {
description = "{{{langname}}} terms spelled with {{l|{{{langcode}}}|" .. kanji .. "}} with " ..
yomi_types .. " of " .. get_reading_link(reading, lang) .. ".",
displaytitle = "{{{langname}}} terms spelled with " .. tagged_kanji .. " read as " .. tagged_reading,
breadcrumb = "read as " .. tagged_reading,
parents = parents,
umbrella = false,
}, true
end)
insert(handlers, function(data)
local affix, kanji, reading = data.label:match("^terms ([a-z]+fix)ed with (.+) read as " .. kana_capture .. "$")
if not affix or not kanji or not reading then
return nil
end
local args = require("Module:parameters").process(data.args, {
[1] = {list = true},
})
local lang = data.lang
if #args[1] == 0 then
error("For categories of the form \"" .. lang:getCanonicalName() ..
" terms AFFIXed with KANJI read as READING\", at least one reading type (e.g. <code>kun</code> or <code>on</code>) must be specified using <code>1=</code>, <code>2=</code>, <code>3=</code>, etc.")
end
local yomi_types = {}
for _, yomi, category in ipairs(args[1]) do
local yomi_data = yomi_data[yomi]
if not yomi_data then
error("The yomi type \"" .. yomi .. "\" is not recognized.")
end
category = yomi_data.kanji_category
if not category then
error("The yomi type \"" .. yomi .. "\" is not valid for this type of category.")
end
insert(yomi_types, yomi_data.link)
end
yomi_types = (#yomi_types > 1 and "one of " or "") .. "its " ..
require("Module:table").serialCommaJoin(yomi_types, {conj = "or"}) ..
" reading" .. (#yomi_types > 1 and "s" or "")
local description = "{{{langname}}} terms " .. affix .. "ed with {{l|{{{langcode}}}|" .. kanji .. "}} with " ..
yomi_types .. " of " .. get_reading_link(reading, lang) .. "."
local what_categorizes_msg =
require("Module:category tree/fam/jpx/what-categorizes-msg")
.get_what_categorizes_msg(kanji, reading, affix)
if what_categorizes_msg then
description = description .. "\n\n" .. what_categorizes_msg
end
local parents = {}
table.insert(parents, {name = "terms " .. affix .. "ed with " .. kanji, sort = (lang:makeSortKey(reading))})
if mw.title.new("Category:" .. lang:getCanonicalName() .. " terms spelled with " .. kanji .. " read as " .. reading).exists then
table.insert(parents, {name = "terms spelled with " .. kanji .. " read as " .. reading, sort = (lang:makeSortKey(reading)), args=data.args})
end
local tagged_kanji = get_tagged_reading(kanji, lang)
local tagged_reading = get_tagged_reading(reading, lang)
return {
description = description,
displaytitle = "{{{langname}}} terms " .. affix .. "ed with " .. tagged_kanji .. " read as " .. tagged_reading,
breadcrumb = "read as " .. reading,
parents = parents,
umbrella = false,
}, true
end)
insert(handlers, function(data)
local kanji, daiyoji = match(data.label, "^terms with (.) replaced by daiyōji (.)$")
if not kanji then
return nil
end
local args = require("Module:parameters").process(data.args, {
["sort"] = true,
})
local lang = data.lang
if not args.sort then
error("For categories of the form \"" .. lang:getCanonicalName() ..
" terms with KANJI replaced by daiyōji DAIYOJI\", the sort key must be specified using sort=")
end
local tagged_kanji = get_tagged_reading(kanji, lang)
local tagged_daiyoji = get_tagged_reading(daiyoji, lang)
return {
description = "{{{langname}}} terms with {{l|{{{langcode}}}|" .. kanji .. "}} replaced by [[Appendix:Japanese glossary#daiyouji|daiyōji]] {{l|{{{langcode}}}|" .. daiyoji .. "}}.",
displaytitle = "{{{langname}}} terms with " .. tagged_kanji .. " replaced by daiyōji " .. tagged_daiyoji,
breadcrumb = tagged_kanji .. " replaced by daiyōji " .. tagged_daiyoji,
parents = {{name = "terms spelled with daiyōji", sort = args.sort}},
umbrella = false,
}, true
end)
return {LABELS = labels, HANDLERS = handlers}
ea2zmcmhguqw3ivdjan9pb199sxgqx3
88825
88824
2026-08-06T20:17:39Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/fam/jpx]]
88824
Scribunto
text/plain
local labels = {}
local handlers = {}
local m_str_utils = require("Module:string utilities")
local concat = table.concat
local full_link = require("Module:links").full_link
local insert = table.insert
local Hani_sort = require("Module:Hani-sortkey").makeSortKey
local match = m_str_utils.match
local sort = table.sort
local tag_text = require("Module:script_utilities").tag_text
local ucfirst = m_str_utils.ucfirst
local Hira = require("Module:scripts").getByCode("Hira")
local Jpan = require("Module:scripts").getByCode("Jpan")
local kana_to_romaji = require("Module:Hrkt-translit").tr
local m_numeric = require("Module:ConvertNumeric")
local kana_capture = "([-" .. require("Module:ja/data/range").kana .. "・]+)"
local yomi_data = require("Module:kanjitab/data")
labels["adnominals"] = {
description = "{{{langname}}} adnominals, or {{ja-r|連%体%詞|れん%たい%し}}, which modify nouns, and do not conjugate or [[predicate#Verb|predicate]].",
parents = {{name = "{{{langcat}}}", raw = true}},
}
labels["hiragana"] = {
description = "{{{langname}}} terms with hiragana {{mdash}} {{ja-r|平%仮%名|ひら%が%な}} {{mdash}} forms, sorted by conventional hiragana sequence. The hiragana form is a [[phonetic]] representation of that word. " ..
"Wiktionary represents {{{langname}}}-language segments in three ways: in normal form (with [[kanji]], if appropriate), in [[hiragana]] " ..
"form (this differs from kanji form only when the segment contains kanji), and in [[romaji]] form.",
additional = "''See also'' [[:Category:{{{langname}}} katakana]]",
toc_template = "Hira-categoryTOC",
toc_template_full = "Hira-categoryTOC/full",
parents = {
{name = "{{{langcat}}}", raw = true},
"Category:Hiragana script characters",
}
}
labels["historical hiragana"] = {
description = "{{{langname}}} historical [[hiragana]].",
additional = "''See also'' [[:Category:{{{langname}}} historical katakana]].",
toc_template = "Hira-categoryTOC",
toc_template_full = "Hira-categoryTOC/full",
parents = {
"hiragana",
{name = "{{{langcat}}}", raw = true},
"Category:Hiragana script characters",
}
}
labels["katakana"] = {
description = "{{{langname}}} terms with katakana {{mdash}} {{ja-r|片%仮%名|かた%か%な}} {{mdash}} forms, sorted by conventional katakana sequence. Katakana is used primarily for transliterations of foreign words, including old Chinese hanzi not used in [[shinjitai]].",
additional = "''See also'' [[:Category:{{{langname}}} hiragana]]",
toc_template = "Kana-categoryTOC",
toc_template_full = "Kana-categoryTOC/full",
parents = {
{name = "{{{langcat}}}", raw = true},
"Category:Katakana script characters",
}
}
labels["historical katakana"] = {
description = "{{{langname}}} historical [[katakana]].",
additional = "''See also'' [[:Category:{{{langname}}} historical hiragana]].",
toc_template = "Kana-categoryTOC",
toc_template_full = "Kana-categoryTOC/full",
parents = {
"katakana",
{name = "{{{langcat}}}", raw = true},
"Category:Katakana script characters",
}
}
labels["terms spelled with mixed kana"] = {
description = "{{{langname}}} terms which combine [[hiragana]] and [[katakana]] characters, potentially with [[kanji]] too.",
parents = {
{name = "{{{langcat}}}", raw = true},
"hiragana",
"katakana",
},
}
labels["kanji"] = {
topright = "{{wp|Kanji}}",
description = "{{{langname}}} symbols of the Han logographic script, which can represent sounds or convey meanings directly.",
toc_template = "Hani-categoryTOC",
umbrella = "Han characters",
parents = "logograms",
}
labels["kanji by reading"] = {
description = "{{{langname}}} kanji categorized by reading.",
parents = {{name = "kanji", sort = "reading"}},
}
labels["makurakotoba"] = {
topright = "{{wp|Makurakotoba}}",
description = "{{{langname}}} idioms used in poetry to introduce specific words.",
parents = {"idioms"},
}
labels["terms by kanji readings"] = {
description = "{{{langname}}} categories grouped with regard to the readings of the kanji with which they are spelled.",
parents = {{name = "{{{langcat}}}", raw = true}},
}
labels["terms by reading pattern"] = {
description = "{{{langname}}} categories with terms grouped by their reading patterns.",
parents = {{name = "{{{langcat}}}", raw = true}},
}
labels["terms by number of kanji"] = {
description = "{{{langname}}} terms categorized by number of kanji.",
parents = {"terms by orthographic property"},
}
local function handle_onyomi_list(category, category_type, cat_yomi_type)
local onyomi, seen = {}, {}
for _, yomi in pairs(yomi_data) do
if not seen[yomi] and yomi.onyomi then
local yomi_catname = yomi[category_type]
if yomi_catname ~= false then
local yomi_type = yomi.type
if yomi_type ~= "on'yomi" and yomi_type ~= cat_yomi_type then
insert(onyomi, "[[:Category:{{{langname}}} " .. category:gsub("{{{yomi_catname}}}", yomi_catname) .. "]]")
end
end
end
seen[yomi] = true
end
sort(onyomi)
return onyomi
end
local function add_yomi_category(category, category_type, parent, description)
for _, yomi in pairs(yomi_data) do
local yomi_catname = yomi[category_type]
if yomi_catname ~= false then
local yomi_type = yomi.type
local yomi_desc = yomi.link or yomi_catname
if yomi.description then
yomi_desc = yomi_desc .. "; " .. yomi.description
end
local label = {
description = description .. " " .. yomi_desc .. ".",
breadcrumb = yomi_type,
parents = {{name = parent, sort = yomi_catname}},
}
if yomi.onyomi then
local onyomi = handle_onyomi_list(category, category_type, yomi_type)
label.additional = "Categories of terms with " ..
(yomi_type == "on'yomi" and "more" or "other") ..
" specific types of on'yomi readings can be found in the following categories:\n* " .. concat(onyomi, "\n* ")
if yomi_type ~= "on'yomi" then
insert(label.parents, 1, {
name = (category:gsub("{{{yomi_catname}}}", yomi_data.on[category_type])),
sort = yomi_catname
})
end
end
labels[category:gsub("{{{yomi_catname}}}", yomi_catname)] = label
end
end
end
add_yomi_category(
"terms read with {{{yomi_catname}}}",
"reading_category",
"terms by reading pattern",
"{{{langname}}} terms read with"
)
add_yomi_category(
"terms spelled with kanji with {{{yomi_catname}}} readings",
"kanji_category",
"terms by kanji reading type",
"{{{langname}}} categories with terms that are spelled with one or more kanji read with"
)
labels["terms with missing yomi"] = {
description = "{{{langname}}} terms where at least one [[Appendix:Japanese glossary#yomi|yomi]] is missing from {{tl|{{{langcode}}}-kanjitab}}.",
hidden = true,
can_be_empty = true,
parents = {"entry maintenance"},
}
labels["terms with IPA pronunciation with pitch accent"] = {
description = "{{{langname}}} terms with pronunciations that have {{w|Japanese pitch accent|pitch accent}} specified.",
additional = "Pitch accent can be specified in {{tl|{{{langcode}}}-pron}} with the {{code|=acc=}} parameter.",
can_be_empty = true,
parents = {"entry maintenance", "pitch accent"},
}
labels["terms with IPA pronunciation missing pitch accent"] = {
description = "{{{langname}}} terms with pronunciations that do not have a {{w|Japanese pitch accent|pitch accent}} specified.",
additional = "Pitch accent can be specified in {{tl|{{{langcode}}}-pron}} with the {{code|=acc=}} parameter.",
hidden = true,
can_be_empty = true,
parents = {"entry maintenance"},
}
labels["pitch accent"] = {
description = "{{{langname}}} terms regarding {{w|Japanese pitch accent|pitch accent}} pronunciation.",
can_be_empty = true,
parents = {{name = "{{{langcat}}}", raw = true}},
}
labels["terms with Heiban pitch accent (Tōkyō)"] = {
description = "{{{langname}}} terms with pronunciations that are (Tōkyō) [[平板型|Heiban]] {{w|Japanese pitch accent|pitch accent}}.",
can_be_empty = true,
parents = {"pitch accent"}
}
labels["terms with Atamadaka pitch accent (Tōkyō)"] = {
description = "{{{langname}}} terms with pronunciations that are (Tōkyō) [[頭高型|Atamadaka]] {{w|Japanese pitch accent|pitch accent}}.",
can_be_empty = true,
parents = {"pitch accent"}
}
labels["terms with Nakadaka pitch accent (Tōkyō)"] = {
description = "{{{langname}}} terms with pronunciations that are (Tōkyō) [[中高型|Nakadaka]] {{w|Japanese pitch accent|pitch accent}}.",
can_be_empty = true,
parents = {"pitch accent"}
}
labels["terms with Odaka pitch accent (Tōkyō)"] = {
description = "{{{langname}}} terms with pronunciations that are (Tōkyō) [[尾高型|Odaka]] {{w|Japanese pitch accent|pitch accent}}.",
can_be_empty = true,
parents = {"pitch accent"}
}
labels["terms with complex pitch accent (Tōkyō)"] = {
description = "{{{langname}}} terms with pronunciations that are (Tōkyō) complex {{w|Japanese pitch accent|pitch accent}}, as in having more than one {{m|ja|アクセント句}}.",
can_be_empty = true,
parents = {"pitch accent"}
}
labels["pitch accent deaccenting before の"] = {
description = "{{{langname}}} terms with {{w|Japanese pitch accent|pitch accent}} pronunciations that have exceptional deaccenting or lack thereof before の ({{ja-deaccenting-before-no}}).",
can_be_empty = true,
parents = {"pitch accent"}
}
labels["terms with Odaka pitch accent not deaccented before の (Tōkyō)"] = {
description = "{{{langname}}} terms with pronunciations that are (Tōkyō) [[尾高型|Odaka]] {{w|Japanese pitch accent|pitch accent}} and do not become deaccented before の ({{ja-deaccenting-before-no}}).\n\nTerms are placed in this category using {{tl|ja-odaka-deaccent-exception}}.",
can_be_empty = true,
parents = {"pitch accent deaccenting before の"}
}
labels["terms with Nakadaka pitch accent deaccented before の (Tōkyō)"] = {
description = "{{{langname}}} terms with pronunciations that are (Tōkyō) [[中高型|Nakadaka]] {{w|Japanese pitch accent|pitch accent}} and become deaccented before の ({{ja-deaccenting-before-no}}).\n\nTerms are placed in this category using {{tl|ja-nakadaka-deaccent}}.",
can_be_empty = true,
parents = {"pitch accent deaccenting before の"}
}
labels["terms by kanji reading type"] = {
description = "{{{langname}}} categories with terms grouped with regard to the types of readings of the kanji with which " ..
"they are spelled; broadly, those of Chinese origin, {{ja-r|音|おん}} readings, and those of non-Chinese origin, {{ja-r|訓|くん}} readings.",
parents = {{name = "{{{langcat}}}", raw = true}},
}
labels["terms spelled with ateji"] = {
topright = "{{wp|Ateji}}",
description = "{{{langname}}} terms containing one or more [[Appendix:Japanese glossary#ateji|ateji]] {{mdash}} {{ja-r|当て字|あてじ}} {{mdash}} which are [[kanji]] used to represent sounds rather than meanings (though meaning may have some influence on which kanji are chosen).",
parents = {{name = "{{{langcat}}}", raw = true}},
}
labels["terms spelled with daiyōji"] = {
description = "Japanese terms spelled using [[Appendix:Japanese glossary#daiyouji|daiyōji]], categorized using {{temp|ja-daiyouji}}.",
parents = {"terms by etymology"},
}
labels["terms spelled with jukujikun"] = {
description = "{{{langname}}} terms containing one or more [[Appendix:Japanese glossary#jukujikun|jukujikun]] {{mdash}} {{ja-r|熟%字%訓|じゅく%じ%くん}} {{mdash}} which are [[kanji]] used to represent meanings rather than sounds.",
parents = {{name = "{{{langcat}}}", raw = true}},
}
local function add_grade_categories(grade, desc, wp, only_one, parent, sort)
local grade_kanji = grade .. " kanji"
local topright = wp and ("{{wp|%s}}"):format(ucfirst(grade_kanji)) or nil
labels[grade_kanji] = {
topright = topright,
description = "{{{langname}}} kanji " .. desc,
toc_template = "Hani-categoryTOC",
parents = {{
name = parent and (parent .. " kanji") or "kanji",
sort = sort or grade
}},
}
labels["terms spelled with " .. grade_kanji] = {
topright = topright,
description = "{{{langname}}} terms spelled with " .. (only_one and "at least one " or "") .. "kanji " .. desc,
parents = {{
name = parent and ("terms spelled with " .. parent .. " kanji") or "terms by orthographic property",
sort = sort or grade
}},
}
end
for i = 1, 6 do
local ord = m_numeric.ones_position_ord[i]
add_grade_categories(
ord .. " grade",
"taught in the " .. ord .. " grade of elementary school, as designated by the the official list of {{ja-r|教%育 漢%字|きょう%いく かん%じ|education kanji}}.",
false,
false,
"kyōiku",
i
)
end
add_grade_categories(
"kyōiku",
"on the official list of {{ja-r|教%育 漢%字|きょう%いく かん%じ|education kanji}}.",
true,
false,
"jōyō"
)
add_grade_categories(
"secondary school",
"on the official list of {{ja-r|常%用 漢%字|じょう%よう かん%じ|regular-use characters}} that are generally taught in secondary school.",
false,
false,
"jōyō"
)
add_grade_categories(
"jōyō",
"on the official list of {{ja-r|常%用 漢%字|じょう%よう かん%じ|regular-use characters}}.",
true,
false
)
add_grade_categories(
"tōyō",
"on the official list of {{ja-r|当%用 漢%字|とう%よう かん%じ|general-use characters}}, which was used from 1946{{ndash}}1981 until the publication of the list of {{ja-r|常%用 漢%字|じょう%よう かん%じ|regular-use characters}}.",
true,
false
)
add_grade_categories(
"jinmeiyō",
"on the official list of {{ja-r|人%名%用 漢%字|じん%めい%-よう かん%じ|kanji for use in personal names}}.",
true,
true
)
add_grade_categories(
"hyōgai",
"not included on the official list of {{ja-r|常%用 漢%字|じょう%よう かん%じ|regular-use characters}} or {{ja-r|人%名%用 漢%字|じん%めい%-よう かん%じ|kanji for use in personal names}}, known as {{ja-r|表%外 漢%字|ひょう%がい かん%じ}} or {{ja-r|表%外%字|ひょう%がい%じ|unlisted characters}}.",
true,
true
)
labels["terms with multiple readings"] = {
description = "{{{langname}}} terms with multiple pronunciations (hence multiple [[kana]] spellings).",
parents = {{name = "{{{langcat}}}", raw = true}},
}
labels["kanji readings by number of morae"] = {
description = "{{{langname}}} categories grouped with regard to the number of morae in their kanji readings.",
parents = {{name = "{{{langcat}}}", raw = true}},
}
labels["single-kanji terms"] = {
description = "{{{langname}}} terms written as a single kanji.",
parents = {
"terms by orthographic property",
{name = "terms with 1 kanji", sort = " "},
},
}
labels["kanji with kun readings missing okurigana designation"] = {
breadcrumb = "Kanji missing okurigana designation",
description = "{{{langname}}} kanji entries in which one or more kun readings entered into {{tl|{{{langcode}}}-readings}} is missing a hyphen denoting okurigana.",
toc_template = "Hani-categoryTOC",
hidden = true,
can_be_empty = true,
parents = {"entry maintenance"},
}
labels["terms by the individual characters in their historical spellings"] = {
breadcrumb = "Historical",
description = "{{{langname}}} terms categorized by whether their spellings in the {{w|historical kana orthography}} included certain individual characters.",
parents = {{name = "terms by their individual characters", sort = " "}},
}
labels["verbs without transitivity"] = {
description = "{{{langname}}} verbs missing the {{code|=tr=}} parameter from their headword templates.",
hidden = true,
can_be_empty = true,
parents = {"entry maintenance"},
}
labels["yojijukugo"] = {
topright = "{{wp|Yojijukugo}}",
description = "{{{langname}}} four-[[kanji]] compound terms, {{ja-r|四%字 熟%語|よ%じ じゅく%ご}}, with idiomatic meanings; typically derived from Classical Chinese, Buddhist scripture or traditional Japanese proverbs.",
additional = "Compare Chinese {{w|chengyu}} and Korean {{w|sajaseong-eo}}.",
umbrella = "four-character idioms",
parents = {"idioms"},
}
-- FIXME: Only works for 0 through 19.
local word_to_number = {}
for k, v in pairs(m_numeric.ones_position) do
word_to_number[v] = k
end
local periods = {
historical = true,
ancient = true,
}
local function get_period_text_and_reading_type_link(period, reading_type)
if period and not periods[period] then
return nil
end
local period_text = period and period .. " " or nil
-- Allow periods (historical or ancient) by themselves; they will parse as reading types.
if not period and periods[reading_type] then
return nil, reading_type
end
local reading_type_link = "[[Appendix:Japanese glossary#" .. reading_type .. "|" .. reading_type .. "]]"
return period_text, reading_type_link
end
local function get_sc(str)
return match(str:gsub("[%s%p]+", ""), "[^" .. Hira:getCharacters() .. "]") and Jpan or Hira
end
local function get_tagged_reading(reading, lang)
return tag_text(reading, lang, get_sc(reading))
end
local function get_reading_link(reading, lang, period, link)
local hist = periods[period]
reading = reading:gsub("[%.%-%s]+", "")
return full_link({
lang = lang,
sc = get_sc(reading),
term = link or reading:gsub("・", ""),
-- If we have okurigana, demarcate furigana.
alt = reading:gsub("^(.-)・", "<span style=\"border-top:1px solid;position:relative;padding:1px;\">%1<span style=\"position:absolute;top:0;bottom:67%%;right:0%%;border-right:1px solid;\"></span></span>"),
tr = kana_to_romaji((reading:gsub("・", ".")), lang:getCode(), nil, {keep_dot = true, hist = hist})
:gsub("^(.-)%.", "<u>%1</u>"),
pos = reading:find("・", 1, true) and get_tagged_reading((reading:gsub("^.-・", "~")), lang) or nil
}, "term")
end
local function is_on_subtype(reading_type)
return reading_type:find(".on$")
end
insert(handlers, function(data)
local n =data.label:match("^terms with ([1-9]%d*) kanji$")
if not n then
return
end
local sortkey = require("Module:category tree").numeral_sortkey(n, 2097152)
return {
breadcrumb = n,
description = ("{{{langname}}} terms containing exactly %d kanji."):format(n),
-- TODO: implement this using the same mechanism used to implement parents (i.e. avoiding the need for raw categories).
-- umbrella = {
-- breadcrumb = ("%d kanji"):format(n),
-- parents = {{name = "terms by number of kanji subcategories by language", sort = sortkey}},
-- },
parents = {{name = ("terms by number of kanji"), sort = sortkey}}
}
end)
insert(handlers, function(data)
local label_pref, kana = data.label:match("^(terms historically spelled with )" .. kana_capture .. "$")
if not kana then
return
end
local lang = data.lang
return {
description = "{{{langname}}} terms spelled with " .. get_reading_link(kana, lang, "historical") .. " in the {{w|historical kana orthography}}.",
displaytitle = "{{{langname}}} " .. label_pref .. get_tagged_reading(kana, lang),
breadcrumb = "historical",
parents = {
{name = "terms spelled with " .. kana, sort = " "},
{name = "terms by the individual characters in their historical spellings", sort = lang:makeSortKey(kana)}
},
umbrella = false,
}
end)
insert(handlers, function(data)
local count, plural = data.label:match("^kanji readings with (.+) mora(e?)$")
-- Make sure 'one' goes with singular and other numbers with plural.
if not count or (count == "one") ~= (plural == "") then
return
end
local num = word_to_number[count]
if not num then
return nil
end
return {
description = "{{{langname}}} kanji readings containing " .. count .. " mora" .. plural .. ".",
breadcrumb = num,
parents = {{name = "kanji readings by number of morae", sort = num}},
umbrella = false,
}
end)
insert(handlers, function(data)
local label_pref, period, reading_type, reading = match(data.label, "^(kanji with ([a-z]-) ?([%a']+) reading )" .. kana_capture .. "$")
if not period then
return
end
period = period ~= "" and period or nil
local period_text, reading_type_link = get_period_text_and_reading_type_link(period, reading_type)
if not reading_type_link then
return
end
local lang = data.lang
-- Compute parents.
local parents, breadcrumb = {}
if reading:find("・", 1, true) then
local okurigana = reading:match("・(.*)")
insert(parents, {
name = "kanji with " .. (period_text or "") .. reading_type .. " reading " .. reading:match("(.-)・"),
-- Sort by okurigana, since all coordinate categories will have the same furigana.
sort = (lang:makeSortKey(okurigana))
})
breadcrumb = "~" .. okurigana
else
insert(parents, {
name = "kanji by " .. (period_text or "") .. reading_type .. " reading",
sort = (lang:makeSortKey(reading))
})
breadcrumb = reading
end
if is_on_subtype(reading_type) then
insert(parents, {name = "kanji with " .. (period_text or "") .. "on reading " .. reading, sort = reading_type})
elseif period_text then
insert(parents, {name = "kanji with " .. period_text .. "reading " .. reading, sort = reading_type})
end
if not period_text then
insert(parents, {name = "kanji read as " .. reading, sort = reading_type})
end
return {
description = "{{{langname}}} [[kanji]] with the " .. (period_text or "") .. reading_type_link .. " reading " ..
get_reading_link(reading, lang, period or reading_type) .. ".",
displaytitle = "{{{langname}}} " .. label_pref .. get_tagged_reading(reading, lang),
breadcrumb = get_tagged_reading(breadcrumb, lang),
parents = parents,
umbrella = false,
}
end)
insert(handlers, function(data)
local period, reading_type = match(data.label, "^kanji by ([a-z]-) ?([%a']+) reading$")
if not period then
return
end
period = period ~= "" and period or nil
local period_text, reading_type_link = get_period_text_and_reading_type_link(period, reading_type)
if not reading_type_link then
return nil
end
-- Compute parents.
local parents = {
is_on_subtype(reading_type) and {name = "kanji by " .. (period_text or "") .. "on reading", sort = reading_type} or
period_text and {name = "kanji by " .. reading_type .. " reading", sort = period} or
{name = "kanji by reading", sort = reading_type}
}
if period_text then
insert(parents, {name = "kanji by " .. period_text .. "reading", sort = reading_type})
end
-- Compute description.
local description = "{{{langname}}} [[kanji]] categorized by " .. (period_text or "") .. reading_type_link .. " reading."
return {
description = description,
breadcrumb = (period_text or "") .. reading_type,
parents = parents,
umbrella = false,
}
end)
insert(handlers, function(data)
local label_pref, reading = match(data.label, "^(kanji read as )" .. kana_capture .. "$")
if not reading then
return
end
local args = require("Module:parameters").process(data.args, {
["histconsol"] = true,
})
local lang = data.lang
local parents, breadcrumb = {}
if reading:find("・", 1, true) then
local okurigana = reading:match("・(.*)")
insert(parents, {
name = "kanji read as " .. reading:match("(.-)・"),
-- Sort by okurigana, since all coordinate categories will have the same furigana.
sort = (lang:makeSortKey(okurigana))
})
breadcrumb = "~" .. okurigana
else
insert(parents, {
name = "kanji by reading",
sort = (lang:makeSortKey(reading))
})
breadcrumb = reading
end
local addl
local period_text
if args.histconsol then
period_text = "historical"
addl = ("This is a [[Wikipedia:Historical kana orthography|historical]] [[Wikipedia:Kanazukai|reading]], now " ..
"consolidated with the [[Wikipedia:Modern kana usage|modern reading]] of " ..
get_reading_link(args.histconsol, lang, nil, ("Category:Japanese kanji read as %s"):format(args.histconsol)) .. ".")
end
return {
description = "{{{langname}}} [[kanji]] read as " .. get_reading_link(reading, lang, period_text) .. ".",
additional = addl,
displaytitle = "{{{langname}}} " .. label_pref .. get_tagged_reading(reading, lang),
breadcrumb = get_tagged_reading(breadcrumb, lang),
parents = parents,
umbrella = false,
}, true
end)
insert(handlers, function(data)
local label_pref, reading = match(data.label, "^(terms spelled with kanji read as )" .. kana_capture .. "$")
if not reading then
return
end
-- Compute parents.
local lang = data.lang
local sort_key = (lang:makeSortKey(reading))
local mora_count = require("Module:ja").count_morae(reading)
local mora_count_words = m_numeric.spell_number(tostring(mora_count))
local parents = {
{name = "terms by kanji readings", sort = sort_key},
{name = "kanji readings with " .. mora_count_words .. " mora" .. (mora_count > 1 and "e" or ""), sort = sort_key},
{name = "kanji read as " .. reading, sort = " "},
}
local tagged_reading = get_tagged_reading(reading, lang)
return {
description = "{{{langname}}} terms that contain kanji that exhibit a reading of " .. get_reading_link(reading, lang) ..
" in those terms prior to any sound changes.",
displaytitle = "{{{langname}}} " .. label_pref .. tagged_reading,
breadcrumb = tagged_reading,
parents = parents,
umbrella = false,
}
end)
insert(handlers, function(data)
local kanji, reading = match(data.label, "^terms spelled with (.) read as " .. kana_capture .. "$")
if not kanji then
return nil
end
local args = require("Module:parameters").process(data.args, {
[1] = {list = true},
})
local lang = data.lang
if #args[1] == 0 then
error("For categories of the form \"" .. lang:getCanonicalName() ..
" terms spelled with KANJI read as READING\", at least one reading type (e.g. <code>kun</code> or <code>on</code>) must be specified using <code>1=</code>, <code>2=</code>, <code>3=</code>, etc.")
end
local yomi_types, parents = {}, {}
for _, yomi, category in ipairs(args[1]) do
local yomi_data = yomi_data[yomi]
if not yomi_data then
error("The yomi type \"" .. yomi .. "\" is not recognized.")
end
category = yomi_data.kanji_category
if not category then
error("The yomi type \"" .. yomi .. "\" is not valid for this type of category.")
end
insert(yomi_types, yomi_data.link)
insert(parents, {
name = "terms spelled with kanji with " .. category .. " readings",
sort = (lang:makeSortKey(reading))
})
end
insert(parents, 1, {name = "terms spelled with " .. kanji, sort = (lang:makeSortKey(reading))})
insert(parents, 2, {name = "terms spelled with kanji read as " .. reading, sort = Hani_sort(kanji)})
yomi_types = (#yomi_types > 1 and "one of " or "") .. "its " ..
require("Module:table").serialCommaJoin(yomi_types, {conj = "or"}) ..
" reading" .. (#yomi_types > 1 and "s" or "")
local tagged_kanji = get_tagged_reading(kanji, lang)
local tagged_reading = get_tagged_reading(reading, lang)
return {
description = "{{{langname}}} terms spelled with {{l|{{{langcode}}}|" .. kanji .. "}} with " ..
yomi_types .. " of " .. get_reading_link(reading, lang) .. ".",
displaytitle = "{{{langname}}} terms spelled with " .. tagged_kanji .. " read as " .. tagged_reading,
breadcrumb = "read as " .. tagged_reading,
parents = parents,
umbrella = false,
}, true
end)
insert(handlers, function(data)
local affix, kanji, reading = data.label:match("^terms ([a-z]+fix)ed with (.+) read as " .. kana_capture .. "$")
if not affix or not kanji or not reading then
return nil
end
local args = require("Module:parameters").process(data.args, {
[1] = {list = true},
})
local lang = data.lang
if #args[1] == 0 then
error("For categories of the form \"" .. lang:getCanonicalName() ..
" terms AFFIXed with KANJI read as READING\", at least one reading type (e.g. <code>kun</code> or <code>on</code>) must be specified using <code>1=</code>, <code>2=</code>, <code>3=</code>, etc.")
end
local yomi_types = {}
for _, yomi, category in ipairs(args[1]) do
local yomi_data = yomi_data[yomi]
if not yomi_data then
error("The yomi type \"" .. yomi .. "\" is not recognized.")
end
category = yomi_data.kanji_category
if not category then
error("The yomi type \"" .. yomi .. "\" is not valid for this type of category.")
end
insert(yomi_types, yomi_data.link)
end
yomi_types = (#yomi_types > 1 and "one of " or "") .. "its " ..
require("Module:table").serialCommaJoin(yomi_types, {conj = "or"}) ..
" reading" .. (#yomi_types > 1 and "s" or "")
local description = "{{{langname}}} terms " .. affix .. "ed with {{l|{{{langcode}}}|" .. kanji .. "}} with " ..
yomi_types .. " of " .. get_reading_link(reading, lang) .. "."
local what_categorizes_msg =
require("Module:category tree/fam/jpx/what-categorizes-msg")
.get_what_categorizes_msg(kanji, reading, affix)
if what_categorizes_msg then
description = description .. "\n\n" .. what_categorizes_msg
end
local parents = {}
table.insert(parents, {name = "terms " .. affix .. "ed with " .. kanji, sort = (lang:makeSortKey(reading))})
if mw.title.new("Category:" .. lang:getCanonicalName() .. " terms spelled with " .. kanji .. " read as " .. reading).exists then
table.insert(parents, {name = "terms spelled with " .. kanji .. " read as " .. reading, sort = (lang:makeSortKey(reading)), args=data.args})
end
local tagged_kanji = get_tagged_reading(kanji, lang)
local tagged_reading = get_tagged_reading(reading, lang)
return {
description = description,
displaytitle = "{{{langname}}} terms " .. affix .. "ed with " .. tagged_kanji .. " read as " .. tagged_reading,
breadcrumb = "read as " .. reading,
parents = parents,
umbrella = false,
}, true
end)
insert(handlers, function(data)
local kanji, daiyoji = match(data.label, "^terms with (.) replaced by daiyōji (.)$")
if not kanji then
return nil
end
local args = require("Module:parameters").process(data.args, {
["sort"] = true,
})
local lang = data.lang
if not args.sort then
error("For categories of the form \"" .. lang:getCanonicalName() ..
" terms with KANJI replaced by daiyōji DAIYOJI\", the sort key must be specified using sort=")
end
local tagged_kanji = get_tagged_reading(kanji, lang)
local tagged_daiyoji = get_tagged_reading(daiyoji, lang)
return {
description = "{{{langname}}} terms with {{l|{{{langcode}}}|" .. kanji .. "}} replaced by [[Appendix:Japanese glossary#daiyouji|daiyōji]] {{l|{{{langcode}}}|" .. daiyoji .. "}}.",
displaytitle = "{{{langname}}} terms with " .. tagged_kanji .. " replaced by daiyōji " .. tagged_daiyoji,
breadcrumb = tagged_kanji .. " replaced by daiyōji " .. tagged_daiyoji,
parents = {{name = "terms spelled with daiyōji", sort = args.sort}},
umbrella = false,
}, true
end)
return {LABELS = labels, HANDLERS = handlers}
ea2zmcmhguqw3ivdjan9pb199sxgqx3
Mòideal:category tree/fam/jpx/what-categorizes-msg
828
17914
88826
2026-07-26T06:04:37Z
en>Benwing
0
Protected "[[Module:category tree/fam/jpx/what-categorizes-msg]]": per existence of [[WT:CLTR]]; changes to these modules should go through a vetting process ([Edit=Allow only autopatrollers] (indefinite) [Move=Allow only autopatrollers] (indefinite))
88826
Scribunto
text/plain
local export = {}
local m_str_utils = require("Module:string utilities")
local d_range = mw.loadData("Module:ja/data/range")
-- Handles the "Terms are placed in this category..." text in Japanese affix categories
-- Used in
-- [[Module:category tree/affixes and compounds]]
-- [[Module:category tree/fam/jpx]]
function export.get_what_categorizes_msg(affix, affix_reading, affix_type)
if not affix_reading then
if m_str_utils.find(affix, "[" .. d_range.kanji .. "々〻]") then
--Category has kanji but no reading "read as"
return nil
end
affix_reading = affix
end
local tl
if affix_type == "suffix" then
tl = "{{tl|ja-compound|<var>|...|</var>|" .. affix .. "|-" .. affix_reading .. "}}"
elseif affix_type == "prefix" then
tl = "{{tl|ja-compound|" .. affix .. "|" .. affix_reading .. "-|<var>|...|</var>}}"
elseif affix_type == "infix" then
tl = "{{tl|ja-compound|<var>|...|</var>|" .. affix .. "|-" .. affix_reading .. "-|<var>|...|</var>}}"
end
if tl then
tl = "Terms are placed in this category using " .. tl .. "."
end
return tl
end
return export
aseik7d8zcfi37ce42nxrihk8lz825b
88827
88826
2026-08-06T20:17:49Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/fam/jpx/what-categorizes-msg]]
88826
Scribunto
text/plain
local export = {}
local m_str_utils = require("Module:string utilities")
local d_range = mw.loadData("Module:ja/data/range")
-- Handles the "Terms are placed in this category..." text in Japanese affix categories
-- Used in
-- [[Module:category tree/affixes and compounds]]
-- [[Module:category tree/fam/jpx]]
function export.get_what_categorizes_msg(affix, affix_reading, affix_type)
if not affix_reading then
if m_str_utils.find(affix, "[" .. d_range.kanji .. "々〻]") then
--Category has kanji but no reading "read as"
return nil
end
affix_reading = affix
end
local tl
if affix_type == "suffix" then
tl = "{{tl|ja-compound|<var>|...|</var>|" .. affix .. "|-" .. affix_reading .. "}}"
elseif affix_type == "prefix" then
tl = "{{tl|ja-compound|" .. affix .. "|" .. affix_reading .. "-|<var>|...|</var>}}"
elseif affix_type == "infix" then
tl = "{{tl|ja-compound|<var>|...|</var>|" .. affix .. "|-" .. affix_reading .. "-|<var>|...|</var>}}"
end
if tl then
tl = "Terms are placed in this category using " .. tl .. "."
end
return tl
end
return export
aseik7d8zcfi37ce42nxrihk8lz825b
Mòideal:category tree/fam/qfa-kor
828
17915
88828
2026-07-26T06:05:01Z
en>Benwing
0
Changed protection settings for "[[Module:category tree/fam/qfa-kor]]": per existence of [[WT:CLTR]]; changes to these modules should go through a vetting process ([Edit=Allow only autopatrollers] (indefinite) [Move=Allow only autopatrollers] (indefinite))
88828
Scribunto
text/plain
local labels = {}
local handlers = {}
labels["hanja"] = {
topright = "{{wp|Hanja}}",
description = "{{{langname}}} symbols of the Han logographic script, which can represent sounds or convey meanings directly.",
toc_template = "Hani-categoryTOC",
umbrella = "Han characters",
parents = "logograms",
}
labels["hanja forms"] = {
topright = "{{wp|Hanja}}",
description = "{{{langname}}} terms written in [[hanja]].",
parents = "terms by script",
}
labels["idu forms"] = {
topright = "{{wp|Idu script}}",
description = "{{{langname}}} terms written in [[idu]].",
parents = "terms by script",
}
labels["four-character idioms"] = {
topright = "{{wp|Sajaseong-eo}}",
description = "{{{langname}}} traditional idiomatic expressions, also called sajaseong-eo, usually consisting of four syllables and traditionally given in [[hanja]]; typically derived from [[Classical Chinese]].",
additional = "Compare Chinese {{w|chengyu}} and Japanese {{w|yojijukugo}}.",
umbrella = "four-character idioms",
parents = "idioms",
}
labels["terms written in Hanja-Hangul mixed script"] = {
topright = "{{wp|Korean mixed script}}",
description = "{{{langname}}} mixed script is a form of writing that uses both [[hangeul]] (hangul) (an alphabetical script) and [[hanja]] (logo-syllabic characters).",
parents = "terms written in multiple scripts",
}
return {LABELS = labels, HANDLERS = handlers}
sg8wfygn9ns7dv1gh1uvf8z468u1th2
88829
88828
2026-08-06T20:17:59Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/fam/qfa-kor]]
88828
Scribunto
text/plain
local labels = {}
local handlers = {}
labels["hanja"] = {
topright = "{{wp|Hanja}}",
description = "{{{langname}}} symbols of the Han logographic script, which can represent sounds or convey meanings directly.",
toc_template = "Hani-categoryTOC",
umbrella = "Han characters",
parents = "logograms",
}
labels["hanja forms"] = {
topright = "{{wp|Hanja}}",
description = "{{{langname}}} terms written in [[hanja]].",
parents = "terms by script",
}
labels["idu forms"] = {
topright = "{{wp|Idu script}}",
description = "{{{langname}}} terms written in [[idu]].",
parents = "terms by script",
}
labels["four-character idioms"] = {
topright = "{{wp|Sajaseong-eo}}",
description = "{{{langname}}} traditional idiomatic expressions, also called sajaseong-eo, usually consisting of four syllables and traditionally given in [[hanja]]; typically derived from [[Classical Chinese]].",
additional = "Compare Chinese {{w|chengyu}} and Japanese {{w|yojijukugo}}.",
umbrella = "four-character idioms",
parents = "idioms",
}
labels["terms written in Hanja-Hangul mixed script"] = {
topright = "{{wp|Korean mixed script}}",
description = "{{{langname}}} mixed script is a form of writing that uses both [[hangeul]] (hangul) (an alphabetical script) and [[hanja]] (logo-syllabic characters).",
parents = "terms written in multiple scripts",
}
return {LABELS = labels, HANDLERS = handlers}
sg8wfygn9ns7dv1gh1uvf8z468u1th2
Mòideal:category tree/fam/roa-ibe
828
17916
88830
2026-07-26T06:05:21Z
en>Benwing
0
Changed protection settings for "[[Module:category tree/fam/roa-ibe]]": per existence of [[WT:CLTR]]; changes to these modules should go through a vetting process ([Edit=Allow only autopatrollers] (indefinite) [Move=Allow only autopatrollers] (indefinite))
88830
Scribunto
text/plain
local labels = {}
local conjugations = {
["ar"] = "{{{langname}}} first conjugation verbs, derived from Latin [[:Category:Latin first conjugation verbs|first conjugation (-āre) verbs]].",
["er"] = "{{{langname}}} second conjugation verbs, derived from Latin [[:Category:Latin second conjugation verbs|second conjugation (-ēre)]] or [[:Category:Latin third conjugation verbs|third conjugation (-ere)]] verbs.",
["ir"] = "{{{langname}}} third conjugation verbs, derived from Latin [[:Category:Latin third conjugation verbs|third conjugation (-ere)]] or [[:Category:Latin fourth conjugation verbs|fourth conjugation (-īre)]] verbs.",
}
labels["verbs by conjugation"] = {
description = "{{{langname}}} verbs categorized by conjugation.",
parents = {"verbs by inflection type"},
}
for conj, conjdesc in pairs(conjugations) do
labels["verbs ending in -" .. conj] = {
description = conjdesc,
displaytitle = "{{{langname}}} verbs ending in {{m|{{{langcode}}}||-" .. conj .. "}}",
parents = {
{name = "verbs by conjugation", sort = conj},
},
breadcrumb = "{{m|{{{langcode}}}||-" .. conj .. "}}",
}
end
labels["verbs by vowel alternation"] = {
description = "{{{langname}}} verbs categorized by type of vowel alternation.",
parents = {"verbs by inflection type"},
}
labels["verbs by consonant alternation"] = {
description = "{{{langname}}} verbs categorized by type of consonant alternation.",
parents = {"verbs by inflection type"},
}
labels["third-person-only verbs"] = {
description = "{{{langname}}} verbs with forms that exist only in the third person, and have no imperatives.",
parents = {{name = "defective verbs"}},
breadcrumb = "third-person-only",
}
-- Add 'umbrella_parents' key if not already present.
for key, data in pairs(labels) do
if not data.umbrella_parents then
data.umbrella_parents = "Terms by grammatical category subcategories by language"
end
end
return {LABELS = labels}
4hqzujtavdjpjcevxpm30i0mf4rf6vg
88831
88830
2026-08-06T20:18:05Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/fam/roa-ibe]]
88830
Scribunto
text/plain
local labels = {}
local conjugations = {
["ar"] = "{{{langname}}} first conjugation verbs, derived from Latin [[:Category:Latin first conjugation verbs|first conjugation (-āre) verbs]].",
["er"] = "{{{langname}}} second conjugation verbs, derived from Latin [[:Category:Latin second conjugation verbs|second conjugation (-ēre)]] or [[:Category:Latin third conjugation verbs|third conjugation (-ere)]] verbs.",
["ir"] = "{{{langname}}} third conjugation verbs, derived from Latin [[:Category:Latin third conjugation verbs|third conjugation (-ere)]] or [[:Category:Latin fourth conjugation verbs|fourth conjugation (-īre)]] verbs.",
}
labels["verbs by conjugation"] = {
description = "{{{langname}}} verbs categorized by conjugation.",
parents = {"verbs by inflection type"},
}
for conj, conjdesc in pairs(conjugations) do
labels["verbs ending in -" .. conj] = {
description = conjdesc,
displaytitle = "{{{langname}}} verbs ending in {{m|{{{langcode}}}||-" .. conj .. "}}",
parents = {
{name = "verbs by conjugation", sort = conj},
},
breadcrumb = "{{m|{{{langcode}}}||-" .. conj .. "}}",
}
end
labels["verbs by vowel alternation"] = {
description = "{{{langname}}} verbs categorized by type of vowel alternation.",
parents = {"verbs by inflection type"},
}
labels["verbs by consonant alternation"] = {
description = "{{{langname}}} verbs categorized by type of consonant alternation.",
parents = {"verbs by inflection type"},
}
labels["third-person-only verbs"] = {
description = "{{{langname}}} verbs with forms that exist only in the third person, and have no imperatives.",
parents = {{name = "defective verbs"}},
breadcrumb = "third-person-only",
}
-- Add 'umbrella_parents' key if not already present.
for key, data in pairs(labels) do
if not data.umbrella_parents then
data.umbrella_parents = "Terms by grammatical category subcategories by language"
end
end
return {LABELS = labels}
4hqzujtavdjpjcevxpm30i0mf4rf6vg
Mòideal:category tree/fam/sem-ara
828
17917
88832
2026-07-26T06:05:40Z
en>Benwing
0
Changed protection settings for "[[Module:category tree/fam/sem-ara]]": per existence of [[WT:CLTR]]; changes to these modules should go through a vetting process ([Edit=Allow only autopatrollers] (indefinite) [Move=Allow only autopatrollers] (indefinite))
88832
Scribunto
text/plain
local labels = {}
--[=[
This module handles language-specific categories for all Aramaic varieties.
]=]
-----------------------------------------------------------------------------
-- --
-- NOUNS --
-- --
-----------------------------------------------------------------------------
---------------------------------- Noun labels ---------------------------------
local function add_noun_labels(labels)
-- Currently there is no [[Appendix:Aramaic nominals]]. Formerly the code conditionalized on the language
-- but only referenced [[Appendix:Assyrian Neo-Aramaic nominals]] (which also doesn't exist) for lang code 'aii',
-- and was broken for all other languages. If we want to conditionalize on the language now, we have to make the
-- description a function (in which case it will be passed an object whose `.lang` field is the language), or use
-- a handler.
local nominal_appendix = "Appendix:Aramaic nominals"
local appendix_exists = mw.title.new(nominal_appendix).exists
local function make_appendix_link(text, anchor)
if appendix_exists then
anchor = anchor or mw.getContentLanguage():ucfirst(text)
return ("[[%s#%s|%s]]"):format(nominal_appendix, anchor, text)
else
return text
end
end
labels["nouns by derivation type"] = {
description = "{{{langname}}} nouns categorized by type of derivation.",
breadcrumb = "by derivation type",
parents = {{name = "nouns", sort = "derivation type"}},
}
labels["agents"] = {
description = "{{{langname}}} " .. make_appendix_link("agents") .. ", i.e. nouns and adjectives having the meaning \"one who does X\" for some verb.",
breadcrumb_and_first_sort_key = "agents",
parents = {"nouns by derivation type"},
}
labels["instance nouns"] = {
description = "{{{langname}}} " .. make_appendix_link("instance nouns") .. ", i.e. nouns having the meaning \"an instance of doing X\" for some verb.",
breadcrumb_and_first_sort_key = "instance nouns",
parents = {"nouns by derivation type"},
}
labels["nouns of place"] = {
description = "{{{langname}}} " .. make_appendix_link("nouns of place") .. ", i.e. nouns having the approximate meaning \"the place for doing X\" for some verb.",
breadcrumb_and_first_sort_key = "nouns of place",
parents = {"nouns by derivation type"},
}
labels["occupational nouns"] = {
description = "{{{langname}}} " .. make_appendix_link("occupational nouns") .. ", i.e. nouns referring to people employed in doing something.",
breadcrumb_and_first_sort_key = "occupational nouns",
parents = {"nouns by derivation type"},
}
labels["tool nouns"] = {
description = "{{{langname}}} " .. make_appendix_link("tool nouns") .. ", i.e. nouns having the approximate meaning \"tool for doing X\" for some verb.",
breadcrumb_and_first_sort_key = "tool nouns",
parents = {"nouns by derivation type"},
}
-- Add 'umbrella_parents' key if not already present.
for key, data in pairs(labels) do
if not data.umbrella_parents then
data.umbrella_parents = "Lemmas subcategories by language"
end
end
end
-----------------------------------------------------------------------------
-- --
-- WRAPPERS --
-- --
-----------------------------------------------------------------------------
add_noun_labels(labels)
return {LABELS = labels}
2wnbtxzybpb1981cnjrrsfk1xh5g10d
88833
88832
2026-08-06T20:18:10Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/fam/sem-ara]]
88832
Scribunto
text/plain
local labels = {}
--[=[
This module handles language-specific categories for all Aramaic varieties.
]=]
-----------------------------------------------------------------------------
-- --
-- NOUNS --
-- --
-----------------------------------------------------------------------------
---------------------------------- Noun labels ---------------------------------
local function add_noun_labels(labels)
-- Currently there is no [[Appendix:Aramaic nominals]]. Formerly the code conditionalized on the language
-- but only referenced [[Appendix:Assyrian Neo-Aramaic nominals]] (which also doesn't exist) for lang code 'aii',
-- and was broken for all other languages. If we want to conditionalize on the language now, we have to make the
-- description a function (in which case it will be passed an object whose `.lang` field is the language), or use
-- a handler.
local nominal_appendix = "Appendix:Aramaic nominals"
local appendix_exists = mw.title.new(nominal_appendix).exists
local function make_appendix_link(text, anchor)
if appendix_exists then
anchor = anchor or mw.getContentLanguage():ucfirst(text)
return ("[[%s#%s|%s]]"):format(nominal_appendix, anchor, text)
else
return text
end
end
labels["nouns by derivation type"] = {
description = "{{{langname}}} nouns categorized by type of derivation.",
breadcrumb = "by derivation type",
parents = {{name = "nouns", sort = "derivation type"}},
}
labels["agents"] = {
description = "{{{langname}}} " .. make_appendix_link("agents") .. ", i.e. nouns and adjectives having the meaning \"one who does X\" for some verb.",
breadcrumb_and_first_sort_key = "agents",
parents = {"nouns by derivation type"},
}
labels["instance nouns"] = {
description = "{{{langname}}} " .. make_appendix_link("instance nouns") .. ", i.e. nouns having the meaning \"an instance of doing X\" for some verb.",
breadcrumb_and_first_sort_key = "instance nouns",
parents = {"nouns by derivation type"},
}
labels["nouns of place"] = {
description = "{{{langname}}} " .. make_appendix_link("nouns of place") .. ", i.e. nouns having the approximate meaning \"the place for doing X\" for some verb.",
breadcrumb_and_first_sort_key = "nouns of place",
parents = {"nouns by derivation type"},
}
labels["occupational nouns"] = {
description = "{{{langname}}} " .. make_appendix_link("occupational nouns") .. ", i.e. nouns referring to people employed in doing something.",
breadcrumb_and_first_sort_key = "occupational nouns",
parents = {"nouns by derivation type"},
}
labels["tool nouns"] = {
description = "{{{langname}}} " .. make_appendix_link("tool nouns") .. ", i.e. nouns having the approximate meaning \"tool for doing X\" for some verb.",
breadcrumb_and_first_sort_key = "tool nouns",
parents = {"nouns by derivation type"},
}
-- Add 'umbrella_parents' key if not already present.
for key, data in pairs(labels) do
if not data.umbrella_parents then
data.umbrella_parents = "Lemmas subcategories by language"
end
end
end
-----------------------------------------------------------------------------
-- --
-- WRAPPERS --
-- --
-----------------------------------------------------------------------------
add_noun_labels(labels)
return {LABELS = labels}
2wnbtxzybpb1981cnjrrsfk1xh5g10d
Mòideal:category tree/fam/sla
828
17918
88834
2026-07-26T06:05:52Z
en>Benwing
0
Changed protection settings for "[[Module:category tree/fam/sla]]": per existence of [[WT:CLTR]]; changes to these modules should go through a vetting process ([Edit=Allow only template editors and administrators] (indefinite) [Move=Allow only template editors and administrators] (indefinite))
88834
Scribunto
text/plain
local labels = {}
labels["multidirectional verbs"] = {
description = "{{{langname}}} verbs of motion whose motion is multidirectional (as opposed to unidirectional) or indirect, or whose action is repeated or in a series, instead of being a single, completed action.",
additional = "Multidirectional verbs are always imperfective in aspect, even with prefixes that are normally associated with the perfective aspect. See also {{lg|unidirectional verb}}.",
parents = {"verbs"},
umbrella_parents = "Lemmas subcategories by language",
}
labels["unidirectional verbs"] = {
description = "{{{langname}}} verbs of motion whose motion is unidirectional (as opposed to multidirectional), a definitely directed motion, or a single, completed action (instead of a repeated action or series of actions).",
additional = "Unidirectional verbs may be either imperfective or perfective. See also {{lg|multidirectional verb}}.",
parents = {"verbs"},
umbrella_parents = "Lemmas subcategories by language",
}
------- Slavic terms with prothetic consonants -------
for _, back_sound in ipairs {"v-", "w-", "в-"} do
labels["terms with prothetic " .. back_sound] = {
description = "{{{langname}}} terms with a prothetic {{m|{{{langcode}}}||" .. back_sound .. "}}, which was not present etymologically in {{w|Proto-Slavic}}.",
displaytitle = "{{{langname}}} terms with prothetic {{m|{{{langcode}}}||" .. back_sound .. "}}",
additional = "This sound was originally added before terms beginning with a back vowel (''o'' or ''u'') to prevent {{lg|hiatus}} when the preceding word ended in a vowel, and in time was incorporated into the word itself.",
breadcrumb = "with prothetic {{m|{{{langcode}}}||" .. back_sound .. "}}",
parents = {{name = "terms by lexical property", sort = "prothetic"}},
umbrella = {
description = "Categories with terms with a prothetic {{m|und||" .. back_sound .. "}}, which was not present etymologically in {{w|Proto-Slavic}}.",
breadcrumb = "Terms with prothetic {{m|und||" .. back_sound .. "}}",
parents = {"Terms by lexical property subcategories by language"},
}
}
end
labels["terms with prothetic h-"] = {
description = "{{{langname}}} terms with a prothetic {{m|{{{langcode}}}||h}}, which was not present etymologically in {{w|Proto-Slavic}}.",
displaytitle = "{{{langname}}} terms with prothetic {{m|{{{langcode}}}||h}}",
additional = "This sound was originally added before terms beginning with a vowel to prevent {{lg|hiatus}} when the preceding word ended in a vowel, and in time was incorporated into the word itself.",
breadcrumb = "with prothetic {{m|{{{langcode}}}||h}}",
parents = {{name = "terms by lexical property", sort = "prothetic"}},
umbrella = {
description = "Categories with terms with a prothetic {{m|und||h}}, which was not present etymologically in {{w|Proto-Slavic}}.",
breadcrumb = "Terms with prothetic {{m|und||h}}",
parents = {"Terms by lexical property subcategories by language"},
}
}
return {LABELS = labels}
s4khv5hpn2b3ncc7mqr1hlxeaftnq8z
88835
88834
2026-08-06T20:18:31Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/fam/sla]]
88834
Scribunto
text/plain
local labels = {}
labels["multidirectional verbs"] = {
description = "{{{langname}}} verbs of motion whose motion is multidirectional (as opposed to unidirectional) or indirect, or whose action is repeated or in a series, instead of being a single, completed action.",
additional = "Multidirectional verbs are always imperfective in aspect, even with prefixes that are normally associated with the perfective aspect. See also {{lg|unidirectional verb}}.",
parents = {"verbs"},
umbrella_parents = "Lemmas subcategories by language",
}
labels["unidirectional verbs"] = {
description = "{{{langname}}} verbs of motion whose motion is unidirectional (as opposed to multidirectional), a definitely directed motion, or a single, completed action (instead of a repeated action or series of actions).",
additional = "Unidirectional verbs may be either imperfective or perfective. See also {{lg|multidirectional verb}}.",
parents = {"verbs"},
umbrella_parents = "Lemmas subcategories by language",
}
------- Slavic terms with prothetic consonants -------
for _, back_sound in ipairs {"v-", "w-", "в-"} do
labels["terms with prothetic " .. back_sound] = {
description = "{{{langname}}} terms with a prothetic {{m|{{{langcode}}}||" .. back_sound .. "}}, which was not present etymologically in {{w|Proto-Slavic}}.",
displaytitle = "{{{langname}}} terms with prothetic {{m|{{{langcode}}}||" .. back_sound .. "}}",
additional = "This sound was originally added before terms beginning with a back vowel (''o'' or ''u'') to prevent {{lg|hiatus}} when the preceding word ended in a vowel, and in time was incorporated into the word itself.",
breadcrumb = "with prothetic {{m|{{{langcode}}}||" .. back_sound .. "}}",
parents = {{name = "terms by lexical property", sort = "prothetic"}},
umbrella = {
description = "Categories with terms with a prothetic {{m|und||" .. back_sound .. "}}, which was not present etymologically in {{w|Proto-Slavic}}.",
breadcrumb = "Terms with prothetic {{m|und||" .. back_sound .. "}}",
parents = {"Terms by lexical property subcategories by language"},
}
}
end
labels["terms with prothetic h-"] = {
description = "{{{langname}}} terms with a prothetic {{m|{{{langcode}}}||h}}, which was not present etymologically in {{w|Proto-Slavic}}.",
displaytitle = "{{{langname}}} terms with prothetic {{m|{{{langcode}}}||h}}",
additional = "This sound was originally added before terms beginning with a vowel to prevent {{lg|hiatus}} when the preceding word ended in a vowel, and in time was incorporated into the word itself.",
breadcrumb = "with prothetic {{m|{{{langcode}}}||h}}",
parents = {{name = "terms by lexical property", sort = "prothetic"}},
umbrella = {
description = "Categories with terms with a prothetic {{m|und||h}}, which was not present etymologically in {{w|Proto-Slavic}}.",
breadcrumb = "Terms with prothetic {{m|und||h}}",
parents = {"Terms by lexical property subcategories by language"},
}
}
return {LABELS = labels}
s4khv5hpn2b3ncc7mqr1hlxeaftnq8z
Mòideal:category tree/fam/trk
828
17919
88836
2026-07-26T06:02:25Z
en>Benwing
0
Changed protection settings for "[[Module:category tree/fam/trk]]": per existence of [[WT:CLTR]]; changes to these modules should go through a vetting process ([Edit=Allow only autopatrollers] (indefinite) [Move=Allow only autopatrollers] (indefinite))
88836
Scribunto
text/plain
local labels = {}
------- Turkic izafet I/II/III compounds -------
-- FIXME: Possibly should be limited to a subfamily of Turkic.
labels["izafet I compounds"] = {
description = "{{{langname}}} izafet I compounds, i.e. nominal compounds consisting of two nouns both lacking 3rd-person possessive marking.",
additional = "These compounds are right-headed (the second noun is modified by the first), unlike Persian {{lg|ezafe}} compounds, which are typically left-headed.",
breadcrumb_and_first_sort_key = "izafet I",
parents = {"compound terms"},
}
labels["izafet II compounds"] = {
description = "{{{langname}}} izafet II compounds, i.e. nominal compounds with the first noun having zero-marking, and the second noun receiving a possessive suffix.",
additional = "These compounds are right-headed (the second noun is modified by the first), unlike Persian {{lg|ezafe}} compounds, which are typically left-headed.",
breadcrumb_and_first_sort_key = "izafet II",
parents = {"compound terms"},
}
labels["izafet III compounds"] = {
description = "{{{langname}}} izafet III compounds, i.e. nominal compounds with the first noun in the genitive case and the second noun receiving a possessive suffix.",
additional = "These compounds are right-headed (the second noun is modified by the first), unlike Persian {{lg|ezafe}} compounds, which are typically left-headed.",
breadcrumb_and_first_sort_key = "izafet III",
parents = {"compound terms"},
}
labels["Persian-style izafet compounds"] = {
description = "{{{langname}}} Persian-style izafet compounds, i.e. left-headed nominal compounds with the first noun receiving a Persian-style {{lg|ezafe}} suffix and the second noun having zero-marking.",
additional = "These compounds are left-headed (the first noun is modified by second), unlike native Turkic izafet compounds, which are always right-headed.",
breadcrumb_and_first_sort_key = "Persian-style",
parents = {"izafet II compounds"},
}
-- Add 'umbrella_parents' key if not already present.
for key, data in pairs(labels) do
if not data.umbrella_parents then
data.umbrella_parents = "Types of compound terms by language"
end
end
return {LABELS = labels}
ps5lfysh0wgnpjatzdh4pzsy8y1w7y6
88837
88836
2026-08-06T20:18:37Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/fam/trk]]
88836
Scribunto
text/plain
local labels = {}
------- Turkic izafet I/II/III compounds -------
-- FIXME: Possibly should be limited to a subfamily of Turkic.
labels["izafet I compounds"] = {
description = "{{{langname}}} izafet I compounds, i.e. nominal compounds consisting of two nouns both lacking 3rd-person possessive marking.",
additional = "These compounds are right-headed (the second noun is modified by the first), unlike Persian {{lg|ezafe}} compounds, which are typically left-headed.",
breadcrumb_and_first_sort_key = "izafet I",
parents = {"compound terms"},
}
labels["izafet II compounds"] = {
description = "{{{langname}}} izafet II compounds, i.e. nominal compounds with the first noun having zero-marking, and the second noun receiving a possessive suffix.",
additional = "These compounds are right-headed (the second noun is modified by the first), unlike Persian {{lg|ezafe}} compounds, which are typically left-headed.",
breadcrumb_and_first_sort_key = "izafet II",
parents = {"compound terms"},
}
labels["izafet III compounds"] = {
description = "{{{langname}}} izafet III compounds, i.e. nominal compounds with the first noun in the genitive case and the second noun receiving a possessive suffix.",
additional = "These compounds are right-headed (the second noun is modified by the first), unlike Persian {{lg|ezafe}} compounds, which are typically left-headed.",
breadcrumb_and_first_sort_key = "izafet III",
parents = {"compound terms"},
}
labels["Persian-style izafet compounds"] = {
description = "{{{langname}}} Persian-style izafet compounds, i.e. left-headed nominal compounds with the first noun receiving a Persian-style {{lg|ezafe}} suffix and the second noun having zero-marking.",
additional = "These compounds are left-headed (the first noun is modified by second), unlike native Turkic izafet compounds, which are always right-headed.",
breadcrumb_and_first_sort_key = "Persian-style",
parents = {"izafet II compounds"},
}
-- Add 'umbrella_parents' key if not already present.
for key, data in pairs(labels) do
if not data.umbrella_parents then
data.umbrella_parents = "Types of compound terms by language"
end
end
return {LABELS = labels}
ps5lfysh0wgnpjatzdh4pzsy8y1w7y6
Mòideal:category tree/fam/tup
828
17920
88838
2026-07-26T06:06:16Z
en>Benwing
0
Changed protection settings for "[[Module:category tree/fam/tup]]": per existence of [[WT:CLTR]]; changes to these modules should go through a vetting process ([Edit=Allow only autopatrollers] (indefinite) [Move=Allow only autopatrollers] (indefinite))
88838
Scribunto
text/plain
local labels = {}
------- TUPIAN LEMMA CLASSES -------
labels["pluriform adjectives"] = {
description = "{{{langname}}} adjectives that have a relational prefix added to their stem.",
breadcrumb = "pluriform",
parents = {"adjectives by inflection type"},
}
labels["pluriform nouns"] = {
description = "{{{langname}}} nouns that have a relational prefix added to their stem.",
breadcrumb = "pluriform",
parents = {"nouns by inflection type"},
}
labels["pluriform postpositions"] = {
description = "{{{langname}}} postpositions that have a relational prefix added to their stem.",
breadcrumb = "pluriform",
parents = {"postpositions by inflection type"},
}
labels["pluriform verbs"] = {
description = "{{{langname}}} verbs that have a relational prefix added to their stem.",
breadcrumb = "pluriform",
parents = {"verbs by inflection type"},
}
-- Add 'umbrella_parents' key if not already present.
for key, data in pairs(labels) do
if not data.umbrella_parents then
data.umbrella_parents = "Terms by grammatical category subcategories by language"
end
end
return {LABELS = labels}
9d7dzb67cu3aowe6olrn7k161dlypdp
88839
88838
2026-08-06T20:18:40Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/fam/tup]]
88838
Scribunto
text/plain
local labels = {}
------- TUPIAN LEMMA CLASSES -------
labels["pluriform adjectives"] = {
description = "{{{langname}}} adjectives that have a relational prefix added to their stem.",
breadcrumb = "pluriform",
parents = {"adjectives by inflection type"},
}
labels["pluriform nouns"] = {
description = "{{{langname}}} nouns that have a relational prefix added to their stem.",
breadcrumb = "pluriform",
parents = {"nouns by inflection type"},
}
labels["pluriform postpositions"] = {
description = "{{{langname}}} postpositions that have a relational prefix added to their stem.",
breadcrumb = "pluriform",
parents = {"postpositions by inflection type"},
}
labels["pluriform verbs"] = {
description = "{{{langname}}} verbs that have a relational prefix added to their stem.",
breadcrumb = "pluriform",
parents = {"verbs by inflection type"},
}
-- Add 'umbrella_parents' key if not already present.
for key, data in pairs(labels) do
if not data.umbrella_parents then
data.umbrella_parents = "Terms by grammatical category subcategories by language"
end
end
return {LABELS = labels}
9d7dzb67cu3aowe6olrn7k161dlypdp
Mòideal:category tree/fam/zhx
828
17921
88840
2026-07-26T06:06:29Z
en>Benwing
0
Changed protection settings for "[[Module:category tree/fam/zhx]]": per existence of [[WT:CLTR]]; changes to these modules should go through a vetting process ([Edit=Allow only autopatrollers] (indefinite) [Move=Allow only autopatrollers] (indefinite))
88840
Scribunto
text/plain
local labels = {}
local handlers = {}
labels["hanzi"] = {
topright = "{{wp|Chinese characters}}",
description = "{{{langname}}} symbols of the Han logographic script, which can represent sounds or convey meanings directly.",
umbrella = "Han characters",
parents = "logograms",
}
labels["chengyu"] = {
topright = "{{wp|Chengyu}}",
description = "{{{langname}}} traditional idiomatic expressions, usually consisting of four [[hanzi]]; typically derived from [[Classical Chinese]].",
additional = "Compare Japanese {{w|yojijukugo}} and Korean {{w|sajaseong-eo}}.",
parents = "idioms",
}
labels["terms with uncreated forms"] = {
description = "{{{langname}}} terms that use a hanzi box template (such as {{temp|zh-forms}}) with a form not having a page of its own, or a {{temp|zh-see}} template linking to a page without a Chinese section or a nonexistent page",
additional = "If the redlink in the hanzi box is a variant or simplified form, the page may be created with {{temp|subst:zh-new}}.",
parents = {"redlinks", "entry maintenance"},
}
for _, source in ipairs {
"Mencius",
"the Analects",
"the Book of Documents",
"the Book of Rites",
"the Classic of Poetry",
"the Han Feizi",
"the I Ching",
"the Zhuangzi",
"the Zuo Zhuan",
} do
local book = source:match("^the (.*)$")
local sort_key = book or source
local italicized = book and "the ''" .. book .. "''" or source
labels["terms derived from " .. source] = {
displaytitle = book and "{{{langname}}} terms derived from " .. italicized or nil,
parents = {{name = "terms attributed to a specific source", sort = sort_key}},
description = "{{{langname}}} terms derived from " .. italicized .. ".",
breadcrumb = italicized,
}
labels["chengyu derived from " .. source] = {
displaytitle = book and "{{{langname}}} chengyu derived from " .. italicized or nil,
parents = {{name = "chengyu", sort = sort_key}, "terms derived from " .. source},
description = "{{{langname}}} [[chengyu]] derived from " .. italicized .. ".",
breadcrumb = "derived from " .. italicized,
}
end
return {LABELS = labels, HANDLERS = handlers}
rm746jovo6y2tar6jekijha49gudmt4
88841
88840
2026-08-06T20:18:44Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Module:category_tree/fam/zhx]]
88840
Scribunto
text/plain
local labels = {}
local handlers = {}
labels["hanzi"] = {
topright = "{{wp|Chinese characters}}",
description = "{{{langname}}} symbols of the Han logographic script, which can represent sounds or convey meanings directly.",
umbrella = "Han characters",
parents = "logograms",
}
labels["chengyu"] = {
topright = "{{wp|Chengyu}}",
description = "{{{langname}}} traditional idiomatic expressions, usually consisting of four [[hanzi]]; typically derived from [[Classical Chinese]].",
additional = "Compare Japanese {{w|yojijukugo}} and Korean {{w|sajaseong-eo}}.",
parents = "idioms",
}
labels["terms with uncreated forms"] = {
description = "{{{langname}}} terms that use a hanzi box template (such as {{temp|zh-forms}}) with a form not having a page of its own, or a {{temp|zh-see}} template linking to a page without a Chinese section or a nonexistent page",
additional = "If the redlink in the hanzi box is a variant or simplified form, the page may be created with {{temp|subst:zh-new}}.",
parents = {"redlinks", "entry maintenance"},
}
for _, source in ipairs {
"Mencius",
"the Analects",
"the Book of Documents",
"the Book of Rites",
"the Classic of Poetry",
"the Han Feizi",
"the I Ching",
"the Zhuangzi",
"the Zuo Zhuan",
} do
local book = source:match("^the (.*)$")
local sort_key = book or source
local italicized = book and "the ''" .. book .. "''" or source
labels["terms derived from " .. source] = {
displaytitle = book and "{{{langname}}} terms derived from " .. italicized or nil,
parents = {{name = "terms attributed to a specific source", sort = sort_key}},
description = "{{{langname}}} terms derived from " .. italicized .. ".",
breadcrumb = italicized,
}
labels["chengyu derived from " .. source] = {
displaytitle = book and "{{{langname}}} chengyu derived from " .. italicized or nil,
parents = {{name = "chengyu", sort = sort_key}, "terms derived from " .. source},
description = "{{{langname}}} [[chengyu]] derived from " .. italicized .. ".",
breadcrumb = "derived from " .. italicized,
}
end
return {LABELS = labels, HANDLERS = handlers}
rm746jovo6y2tar6jekijha49gudmt4
Teamplaid:R:DIL
10
17922
88844
2025-12-24T04:54:04Z
en>WingerBot
0
rename {{safe page name}} to {{pagename}}
88844
wikitext
text/x-wiki
{{#invoke:quote|call_template
|template=cite-book
|entry={{{head|{{{2|{{pagename}}}}}}}}
|editors=Gregory Toner; Sharon Arbuthnot; Máire Ní Mhaonaigh; Marie-Luise Theuerkauf; Dagmar Wodtko
|title=[[w:Dictionary of the Irish Language|eDIL: Electronic Dictionary of the Irish Language]]
|entryurl=http://dil.ie/{{#ifeq:{{{1}}}|0|search?q={{urlencode:{{{head|{{{2}}}}}}}}&search_in=headword|{{urlencode:{{{1}}}}}}}
|year=2019
|propagateparams = accessdate
|allowparams = head,2,1
}}<noinclude>{{documentation}}</noinclude>
t42llq40w47jbweac37c7f6drftmtz2
88845
88844
2026-08-06T21:54:37Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Template:R:DIL]]
88844
wikitext
text/x-wiki
{{#invoke:quote|call_template
|template=cite-book
|entry={{{head|{{{2|{{pagename}}}}}}}}
|editors=Gregory Toner; Sharon Arbuthnot; Máire Ní Mhaonaigh; Marie-Luise Theuerkauf; Dagmar Wodtko
|title=[[w:Dictionary of the Irish Language|eDIL: Electronic Dictionary of the Irish Language]]
|entryurl=http://dil.ie/{{#ifeq:{{{1}}}|0|search?q={{urlencode:{{{head|{{{2}}}}}}}}&search_in=headword|{{urlencode:{{{1}}}}}}}
|year=2019
|propagateparams = accessdate
|allowparams = head,2,1
}}<noinclude>{{documentation}}</noinclude>
t42llq40w47jbweac37c7f6drftmtz2
Teamplaid:refcat
10
17923
88848
2026-04-15T19:36:27Z
en>Surjection
0
Changed protection settings for "[[Template:refcat]]" ([Edit=Allow only autopatrollers] (indefinite) [Move=Allow only autopatrollers] (indefinite))
88848
wikitext
text/x-wiki
#redirect [[Template:reference template cat]]
q8uahfu56samwe29nej3feicj9ugg5q
88849
88848
2026-08-06T22:17:31Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Template:refcat]]
88848
wikitext
text/x-wiki
#redirect [[Template:reference template cat]]
q8uahfu56samwe29nej3feicj9ugg5q
Teamplaid:reference template cat
10
17924
88850
2026-04-15T19:36:27Z
en>Surjection
0
Changed protection settings for "[[Template:reference template cat]]" ([Edit=Allow only autopatrollers] (indefinite) [Move=Allow only autopatrollers] (indefinite))
88850
wikitext
text/x-wiki
<includeonly>{{#invoke:reference template cat|main}}</includeonly><noinclude>{{documentation}}</noinclude>
luksmm5vfifoqus9yfyen1rylkztzgo
88851
88850
2026-08-06T22:17:40Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Template:reference_template_cat]]
88850
wikitext
text/x-wiki
<includeonly>{{#invoke:reference template cat|main}}</includeonly><noinclude>{{documentation}}</noinclude>
luksmm5vfifoqus9yfyen1rylkztzgo
MediaWiki:Gadget-defaultVisibilityToggles.js
8
17925
88852
2026-05-15T18:04:24Z
en>Surjection
0
support data-toggle-show-default
88852
javascript
text/javascript
/* jshint undef: true */
/* globals $, jQuery, mw, window, getComputedStyle */
(function defaultVisibilityTogglesIIFE() {
"use strict";
if (window.noDefaultVisibilityToggles) return;
/* == NavBars == */
var NavigationBarHide = "hide ▲";
var NavigationBarShow = "show ▼";
var nbsp = "\u00a0";
// Check if an element has been activated with a toggle.
// For convenience, this has the side effect of marking the element as having
// a toggle, if it is not already marked.
// Allows the functions to avoid toggleifying elements more than once, which
// can lead to multiple "show" buttons, for instance.
// The argument must be an Element, not a jQuery object.
function checkAndSetToggleified(element) {
if (element.isToggleified) {
return true;
}
element.isToggleified = true;
}
function getToggleCategory(element, defaultCategory) {
if ($(element).find("table").first().is(".translations"))
return "translations";
var heading = element;
while ((heading = heading.previousElementSibling)) {
// tagName is always uppercase:
// https://developer.mozilla.org/en-US/docs/Web/API/Element/tagName
var num = heading.tagName.match(/H(\d)/);
if (num)
num = Number(num[1]);
else
continue;
if (4 <= num && num <= 6) {
if (heading.getElementsByTagName("span")[1])
heading = heading.getElementsByTagName("span")[0];
var text = jQuery(heading).text()
.toLowerCase()
// jQuery's .text() is inconsistent about whitespace:
.replace(/^\s+|\s+$/g, "").replace(/\s+/g, " ")
// remove numbers added by the "Auto-number headings" pref:
.replace(/^[1-9][0-9.]+ ?/, "");
// Toggle category must be convertible to a valid CSS identifier so
// that it can be used in an id selector in jQuery in
// ToggleCategory.prototype.newSidebarToggle
// in [[MediaWiki:Gadget-VisibilityToggles.js]].
// Spaces must later be converted to hyphens or underscores.
// Reference: https://drafts.csswg.org/selectors-4/#id-selectors
if (/^[a-zA-Z0-9\s_-]+$/.test(text))
return text;
else
break;
} else if (num)
break;
}
return defaultCategory;
}
function createNavToggle(navFrame) {
if (checkAndSetToggleified(navFrame) || navFrame.classList.contains('no-collapse')) {
return;
}
var navHead, navContent;
for (var i = 0, children = navFrame.childNodes; i < children.length; ++i) {
var child = children[i];
if (child.nodeName === "DIV") {
var classList = child.classList;
if (classList.contains("NavHead"))
navHead = child;
if (classList.contains("NavContent"))
navContent = child;
}
}
if (!(navHead && navContent))
return;
// Step 1, don't react when a subitem is clicked.
$(navHead).find("a").on("click", function (e) {
e.stopPropagation();
e.stopImmediatePropagation();
});
// Step 2, toggle visibility when bar is clicked.
// NOTE This function was chosen due to some funny behaviour in Safari.
var $navToggle = $("<a>").attr("role", "button").attr("tabindex", "0");
$("<span>").addClass("NavToggle").attr("data-nosnippet", "")
.append($navToggle)
.prependTo(navHead);
navHead.style.cursor = "pointer";
var toggleCategory = $(navFrame).data("toggle-category")
|| getToggleCategory(navFrame, "other boxes");
navHead.onclick = window.VisibilityToggles.register(toggleCategory,
function show() {
$navToggle.text(NavigationBarHide);
if (navContent)
navContent.style.display = "block";
},
function hide() {
$navToggle.text(NavigationBarShow);
if (navContent)
navContent.style.display = "none";
},
$(navFrame).is("[data-toggle-show-default]"));
}
function createNavToggleForInflectionTable(it) {
if (checkAndSetToggleified(it)) {
return;
}
// The table caption is the clickable element
var itCaption = $(it).find("caption").get(0);
// Step 1, don't react when a subitem is clicked.
$(itCaption).find("a").on("click", function (e) {
e.stopPropagation();
e.stopImmediatePropagation();
});
// Step 2, toggle visibility when bar is clicked.
// NOTE This function was chosen due to some funny behaviour in Safari.
var $navToggle = $("<a>").attr("role", "button").attr("tabindex", "0");
$("<span>").addClass("NavToggle").attr("data-nosnippet", "")
.append($navToggle)
.prependTo(itCaption);
itCaption.style.cursor = "pointer";
var toggleCategory = $(it).data("toggle-category")
|| getToggleCategory(it, "other boxes");
itCaption.onclick = window.VisibilityToggles.register(toggleCategory,
function show() {
$navToggle.text(NavigationBarHide);
if (it) {
it.classList.remove("inflection-table-collapsed");
}
},
function hide() {
$navToggle.text(NavigationBarShow);
if (it) {
it.classList.add("inflection-table-collapsed");
}
},
$(it).is("[data-toggle-show-default]"));
// Check to see if we are on a browser that is known to support
// visibility: collapse, which permits inflection table headings to wrap.
// WebKit needs to be special-cased, as technically it does support
// visibility: collapse, but it just implements it as a synonym for
// visibility: hidden, which is useless. (as of November 2024)
// Yes, I know User-Agent sniffing is so 2004... but WebKit is the new IE
if (
CSS && CSS.supports && CSS.supports("visibility:collapse") &&
// exclude WebKit/Safari, excepting Blink engines which have a frozen WebKit version number
(navigator.userAgent.indexOf("AppleWebKit/") === -1 || navigator.userAgent.indexOf("AppleWebKit/537.36") > -1)
) {
it.classList.remove("no-vc");
} else {
// Strange behaviour occurs when you set the table caption to nowrap
// The [show/hide] toggle crashes into the caption text
// This spacer element prevents that
$("<span>").addClass("no-vc-spacer").appendTo(itCaption);
}
}
/* ==Hidden Quotes== */
function setupHiddenQuotes(li) {
if (checkAndSetToggleified(li))
return;
let HQToggleButton, liComp, dl;
function show() {
HQToggleButton.text("quotations ▲");
$(li).children("ul").show();
}
function hide() {
HQToggleButton.text("quotations ▼");
$(li).children("ul").hide();
}
for (const liComp of li.childNodes) {
// Look at each component of the definition.
if (liComp.tagName === "DL" && !dl)
dl = liComp;
// If we find a ul or dl, we have quotes or example sentences, and thus need a button.
if (liComp.tagName === "UL") {
$(li).children("ul").addClass("wikt-quote-container");
HQToggleButton = $("<a>").attr("role", "button").attr("tabindex", "0");
$(dl || liComp).before($("<span>").addClass("HQToggle").attr("data-nosnippet", "").append(HQToggleButton).css("margin-left", "5px"));
HQToggleButton.on("click", window.VisibilityToggles.register("quotations", show, hide));
break;
}
}
}
/* == View Switching == */
function viewSwitching(rootElement) {
if (checkAndSetToggleified(rootElement)) {
return;
}
var $rootElement = $(rootElement);
var showButtonText = $rootElement.data("vs-showtext") || "more ▼";
var hideButtonText = $rootElement.data("vs-hidetext") || "less ▲";
var toSkip = $rootElement.find(".vsSwitcher").find("*");
var elemsToHide = $rootElement.find(".vsHide").not(toSkip);
var elemsToShow = $rootElement.find(".vsShow").not(toSkip);
// Find the element to place the toggle button in.
var toggleElement = $rootElement.find(".vsToggleElement").not(toSkip).first();
// The toggleElement becomes clickable in its entirety, but
// we need to prevent this if a contained link is clicked instead.
toggleElement.find("a").on("click", function (e) {
e.stopPropagation();
e.stopImmediatePropagation();
});
// Add the toggle button.
var toggleButton = $("<a>").attr("role", "button").attr("tabindex", "0");
$("<span>").addClass("NavToggle").attr("data-nosnippet", "").append(toggleButton).prependTo(toggleElement);
// Determine the visibility toggle category (for the links in the bar on the left).
var toggleCategory = $rootElement.data("toggle-category");
if (!toggleCategory) {
var classNames = $rootElement.attr("class").split(/\s+/);
for (var i = 0; i < classNames.length; ++i) {
var className = classNames[i].split("-");
if (className[0] == "vsToggleCategory") {
toggleCategory = className[1];
}
}
}
if (!toggleCategory)
toggleCategory = "others";
// Register the visibility toggle.
toggleElement.css("cursor", "pointer");
toggleElement.on("click", window.VisibilityToggles.register(toggleCategory,
function show() {
toggleButton.text(hideButtonText);
elemsToShow.hide();
elemsToHide.show();
},
function hide() {
toggleButton.text(showButtonText);
elemsToShow.show();
elemsToHide.hide();
},
$rootElement.is("[data-toggle-show-default]")));
}
/* ==List switching== */
function enableListSwitchGeneric(rootElement) {
if (checkAndSetToggleified(rootElement)) {
return;
}
var $rootElement = $(rootElement);
// Create a toggle button.
var $toggleElement = $("<div>").addClass("list-switcher-element");
var $navToggle = $("<span>").addClass("NavToggle").attr("data-nosnippet", "");
var $toggleButton = $("<a>").attr("role", "button").attr("tabindex", "0");
// Add the toggle button to the DOM tree.
$navToggle.append($toggleButton).prependTo($toggleElement);
$toggleElement.insertAfter($rootElement);
$toggleElement.show();
// Determine the visibility toggle category (for the links in the bar on the
// left). It will either be the value of the "data-toggle-category"
// attribute or will be based on the text of the closest preceding
// fourth-to-sixth-level header.
var toggleCategory = $rootElement.data("toggle-category")
|| getToggleCategory($rootElement[0], "other lists");
// Determine the text for the $toggleButton.
var showButtonText = "show more ▼";
var hideButtonText = "show less ▲";
var numItems;
// special handling for [[Module:collapsible category tree]]
var $categoryTreeTag = $rootElement.children(".CategoryTreeTag");
if ($categoryTreeTag) {
if ($categoryTreeTag.attr("data-pages-left-over") !== "0") {
// some category members are omitted from the list (MediaWiki limitation)
// just use basic "show more/less" in this case for now, this is a big change
//showButtonText = "show first 200 of " + $categoryTreeTag.attr("data-pages-in-cat") + " ▼";
//hideButtonText = "show fewer ▲";
} else {
// all category members are included in the list
numItems = $categoryTreeTag.attr("data-pages-in-cat");
}
} else {
// standard list-switcher using <li> elements
numItems = $rootElement.find("li").length;
}
if (numItems) {
showButtonText = "show all " + numItems + " ▼";
hideButtonText = "show fewer ▲";
}
// Register the visibility toggle.
$toggleElement.on("click", window.VisibilityToggles.register(toggleCategory,
function show() {
$toggleButton.text(hideButtonText);
if (rootElement) {
rootElement.classList.remove("list-switcher-collapsed");
}
},
function hide() {
$toggleButton.text(showButtonText);
if (rootElement) {
rootElement.classList.add("list-switcher-collapsed");
}
},
$rootElement.is("[data-toggle-show-default]")));
// Register a resize observer to see if we need to keep the
// show/hide toggle visible
var termList = rootElement.querySelector(':scope > .term-list');
if (termList && window.ResizeObserver) {
var resizeObserver = new ResizeObserver(function(entries) {
if (entries[0] && entries[0].contentBoxSize[0]) {
// Work out what the max-height would be, in pixels
// As a hack, this value is stored in the CSS `bottom`
// property, as `max-height` is only in place when
// the list is collapsed, but we need to do this check
// even when the list is not collapsed
var maxHeightPx = parseFloat(getComputedStyle(rootElement).bottom);
// If box height is less than its max height + 20 px, suppress
// collapsibility. The 20 px buffer prevents the situation where
// clicking "show more" expands the box by just a few pixels
if (entries[0].contentBoxSize[0].blockSize <= maxHeightPx + 20) {
$toggleElement.hide();
if (rootElement.classList.contains("list-switcher-collapsed")) {
rootElement.classList.remove("list-switcher-collapsed");
rootElement.classList.add("list-switcher-collapsibility-suppressed");
}
} else {
$toggleElement.show();
if (rootElement.classList.contains("list-switcher-collapsibility-suppressed")) {
rootElement.classList.remove("list-switcher-collapsibility-suppressed");
rootElement.classList.add("list-switcher-collapsed");
}
}
}
});
resizeObserver.observe(termList);
}
}
// based on [[User:Erutuon/scripts/semhide.js]], [[User:Jberkel/semhide.js]],
// [[User:Ungoliant_MMDCCLXIV/synshide.js]]
function setupNyms(index, dlTag) {
// [[Wiktionary:Semantic relations]]
var relationClasses = [ "synonym", "antonym", "hypernym", "hyponym", "meronym",
"holonym", "troponym", "comeronym", "coordinate-term",
"near-synonym", "imperfective", "perfective", "alternative-form" ];
var relations = $(dlTag).find("dd > .nyms").get().filter(
function(element) {
return Array.prototype.some.call(element.classList, function (className) {
if (relationClasses.indexOf(className) !== -1) {
element.dataset.relationClass = className;
return true;
}
});
});
function setupToggle(elements, category, visibleByDefault) {
if (elements.length === 0) return null;
var toggler = $("<a>").attr("role", "button").attr("tabindex", "0");
var text = elements.map(function (e) {
var linkCount = e.querySelectorAll("span[lang]").length;
return e.dataset.relationClass.replace("-", " ") +
(linkCount > 1 ? "s" : "");
}).join(", ");
function show() {
toggler.text(text + nbsp + "▲");
$(dlTag).show();
$(elements).show();
}
function hide() {
toggler.text(text + nbsp + "▼");
if ($(dlTag).children().length === elements.length) {
$(dlTag).hide();
} else {
$(elements).hide();
}
}
$(dlTag).before($("<span>")
.addClass("nyms-toggle")
.attr("data-nosnippet", "")
.append(toggler)
.css("margin-left", "5px"));
toggler.click(window.VisibilityToggles.register(category, show, hide, visibleByDefault));
}
var synonyms = relations.filter(function (e) {
return ["synonym", "antonym", "near-synonym", "coordinate-term", "alternative-form"].indexOf(e.dataset.relationClass) !== -1;
});
var other = relations.filter(function (e) { return synonyms.indexOf(e) === -1; });
setupToggle(synonyms, "synonyms", true /* show by default */);
setupToggle(other, "semantic relations");
}
function setupUsageExampleCollapses(index, dlTag) {
var usexTags = $(dlTag).find("dd > .h-usage-example").get();
function setupToggle(elements, category, visibleByDefault) {
if (elements.length === 0) return null;
var toggler = $("<a>").attr("role", "button").attr("tabindex", "0");
function show() {
toggler.text(category + nbsp + "▲");
$(dlTag).show();
$(elements).show();
}
function hide() {
toggler.text(category + nbsp + "▼");
if ($(dlTag).children().length === elements.length) {
$(dlTag).hide();
} else {
$(elements).hide();
}
}
$(dlTag).before($("<span>")
.addClass("nyms-toggle")
.append(toggler)
.css("margin-left", "5px"));
toggler.click(window.VisibilityToggles.register(category, show, hide, visibleByDefault));
}
var collocations = usexTags.filter(function (e) {
return $(e).hasClass("collocation");
});
var usexes = usexTags.filter(function (e) { return collocations.indexOf(e) === -1; });
setupToggle(usexes, "usage examples", true /* show by default */);
setupToggle(collocations, "collocations", true /* show by default */);
}
window.createNavToggle = createNavToggle;
window.setupHiddenQuotes = setupHiddenQuotes;
window.viewSwitching = viewSwitching;
window.getToggleCategory = getToggleCategory;
/* == Apply four functions defined above == */
mw.hook("wikipage.content").add(function($content) {
// NavToggles
$(".NavFrame", $content).each(function(){
createNavToggle(this);
});
$(".inflection-table-collapsible", $content).each(function(){
createNavToggleForInflectionTable(this);
});
// order nyms -> usexes -> quotes, to match the conventional order in entries
// synonyms and such under definitions
// if (mw.config.get("wgNamespaceNumber") === 0) {
$("dl:has(dd > .nyms)", $content).each(setupNyms);
// }
// usage examples and collocations
var namespaceNumber = mw.config.get("wgNamespaceNumber");
if (window.defaultVisibilityTogglesForUsageExamples) {
if (namespaceNumber === 0) {
$("ol > li dl:has(dd > .h-usage-example)", $content).each(setupUsageExampleCollapses);
}
}
// quotes
if (namespaceNumber === 0 || namespaceNumber === 100 || namespaceNumber === 118) {
// First, find all the ordered lists, i.e. all the series of definitions.
$("ol > li", $content).each(function(){
setupHiddenQuotes(this);
});
}
//view switching
$(".vsSwitcher", $content).each(function(){
viewSwitching(this);
});
// list switching
$(".list-switcher", $content).each(function () {
enableListSwitchGeneric(this);
});
});
jQuery(mw).on("LivePreviewDone", function (ev, sels) {
var ols = jQuery(sels.join(",")).find("ol");
for (var i = 0; i < ols.length; i++) {
for (var j = 0; j < ols[i].childNodes.length; j++) {
var li = ols[i].childNodes[j];
if (li.nodeName.toUpperCase() == "LI") {
setupHiddenQuotes(li);
}
}
}
});
})();
0cwdst3iiscohi6qwxwwbpuhjxc4vxb
88853
88852
2026-08-06T22:32:00Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-defaultVisibilityToggles.js]]
88852
javascript
text/javascript
/* jshint undef: true */
/* globals $, jQuery, mw, window, getComputedStyle */
(function defaultVisibilityTogglesIIFE() {
"use strict";
if (window.noDefaultVisibilityToggles) return;
/* == NavBars == */
var NavigationBarHide = "hide ▲";
var NavigationBarShow = "show ▼";
var nbsp = "\u00a0";
// Check if an element has been activated with a toggle.
// For convenience, this has the side effect of marking the element as having
// a toggle, if it is not already marked.
// Allows the functions to avoid toggleifying elements more than once, which
// can lead to multiple "show" buttons, for instance.
// The argument must be an Element, not a jQuery object.
function checkAndSetToggleified(element) {
if (element.isToggleified) {
return true;
}
element.isToggleified = true;
}
function getToggleCategory(element, defaultCategory) {
if ($(element).find("table").first().is(".translations"))
return "translations";
var heading = element;
while ((heading = heading.previousElementSibling)) {
// tagName is always uppercase:
// https://developer.mozilla.org/en-US/docs/Web/API/Element/tagName
var num = heading.tagName.match(/H(\d)/);
if (num)
num = Number(num[1]);
else
continue;
if (4 <= num && num <= 6) {
if (heading.getElementsByTagName("span")[1])
heading = heading.getElementsByTagName("span")[0];
var text = jQuery(heading).text()
.toLowerCase()
// jQuery's .text() is inconsistent about whitespace:
.replace(/^\s+|\s+$/g, "").replace(/\s+/g, " ")
// remove numbers added by the "Auto-number headings" pref:
.replace(/^[1-9][0-9.]+ ?/, "");
// Toggle category must be convertible to a valid CSS identifier so
// that it can be used in an id selector in jQuery in
// ToggleCategory.prototype.newSidebarToggle
// in [[MediaWiki:Gadget-VisibilityToggles.js]].
// Spaces must later be converted to hyphens or underscores.
// Reference: https://drafts.csswg.org/selectors-4/#id-selectors
if (/^[a-zA-Z0-9\s_-]+$/.test(text))
return text;
else
break;
} else if (num)
break;
}
return defaultCategory;
}
function createNavToggle(navFrame) {
if (checkAndSetToggleified(navFrame) || navFrame.classList.contains('no-collapse')) {
return;
}
var navHead, navContent;
for (var i = 0, children = navFrame.childNodes; i < children.length; ++i) {
var child = children[i];
if (child.nodeName === "DIV") {
var classList = child.classList;
if (classList.contains("NavHead"))
navHead = child;
if (classList.contains("NavContent"))
navContent = child;
}
}
if (!(navHead && navContent))
return;
// Step 1, don't react when a subitem is clicked.
$(navHead).find("a").on("click", function (e) {
e.stopPropagation();
e.stopImmediatePropagation();
});
// Step 2, toggle visibility when bar is clicked.
// NOTE This function was chosen due to some funny behaviour in Safari.
var $navToggle = $("<a>").attr("role", "button").attr("tabindex", "0");
$("<span>").addClass("NavToggle").attr("data-nosnippet", "")
.append($navToggle)
.prependTo(navHead);
navHead.style.cursor = "pointer";
var toggleCategory = $(navFrame).data("toggle-category")
|| getToggleCategory(navFrame, "other boxes");
navHead.onclick = window.VisibilityToggles.register(toggleCategory,
function show() {
$navToggle.text(NavigationBarHide);
if (navContent)
navContent.style.display = "block";
},
function hide() {
$navToggle.text(NavigationBarShow);
if (navContent)
navContent.style.display = "none";
},
$(navFrame).is("[data-toggle-show-default]"));
}
function createNavToggleForInflectionTable(it) {
if (checkAndSetToggleified(it)) {
return;
}
// The table caption is the clickable element
var itCaption = $(it).find("caption").get(0);
// Step 1, don't react when a subitem is clicked.
$(itCaption).find("a").on("click", function (e) {
e.stopPropagation();
e.stopImmediatePropagation();
});
// Step 2, toggle visibility when bar is clicked.
// NOTE This function was chosen due to some funny behaviour in Safari.
var $navToggle = $("<a>").attr("role", "button").attr("tabindex", "0");
$("<span>").addClass("NavToggle").attr("data-nosnippet", "")
.append($navToggle)
.prependTo(itCaption);
itCaption.style.cursor = "pointer";
var toggleCategory = $(it).data("toggle-category")
|| getToggleCategory(it, "other boxes");
itCaption.onclick = window.VisibilityToggles.register(toggleCategory,
function show() {
$navToggle.text(NavigationBarHide);
if (it) {
it.classList.remove("inflection-table-collapsed");
}
},
function hide() {
$navToggle.text(NavigationBarShow);
if (it) {
it.classList.add("inflection-table-collapsed");
}
},
$(it).is("[data-toggle-show-default]"));
// Check to see if we are on a browser that is known to support
// visibility: collapse, which permits inflection table headings to wrap.
// WebKit needs to be special-cased, as technically it does support
// visibility: collapse, but it just implements it as a synonym for
// visibility: hidden, which is useless. (as of November 2024)
// Yes, I know User-Agent sniffing is so 2004... but WebKit is the new IE
if (
CSS && CSS.supports && CSS.supports("visibility:collapse") &&
// exclude WebKit/Safari, excepting Blink engines which have a frozen WebKit version number
(navigator.userAgent.indexOf("AppleWebKit/") === -1 || navigator.userAgent.indexOf("AppleWebKit/537.36") > -1)
) {
it.classList.remove("no-vc");
} else {
// Strange behaviour occurs when you set the table caption to nowrap
// The [show/hide] toggle crashes into the caption text
// This spacer element prevents that
$("<span>").addClass("no-vc-spacer").appendTo(itCaption);
}
}
/* ==Hidden Quotes== */
function setupHiddenQuotes(li) {
if (checkAndSetToggleified(li))
return;
let HQToggleButton, liComp, dl;
function show() {
HQToggleButton.text("quotations ▲");
$(li).children("ul").show();
}
function hide() {
HQToggleButton.text("quotations ▼");
$(li).children("ul").hide();
}
for (const liComp of li.childNodes) {
// Look at each component of the definition.
if (liComp.tagName === "DL" && !dl)
dl = liComp;
// If we find a ul or dl, we have quotes or example sentences, and thus need a button.
if (liComp.tagName === "UL") {
$(li).children("ul").addClass("wikt-quote-container");
HQToggleButton = $("<a>").attr("role", "button").attr("tabindex", "0");
$(dl || liComp).before($("<span>").addClass("HQToggle").attr("data-nosnippet", "").append(HQToggleButton).css("margin-left", "5px"));
HQToggleButton.on("click", window.VisibilityToggles.register("quotations", show, hide));
break;
}
}
}
/* == View Switching == */
function viewSwitching(rootElement) {
if (checkAndSetToggleified(rootElement)) {
return;
}
var $rootElement = $(rootElement);
var showButtonText = $rootElement.data("vs-showtext") || "more ▼";
var hideButtonText = $rootElement.data("vs-hidetext") || "less ▲";
var toSkip = $rootElement.find(".vsSwitcher").find("*");
var elemsToHide = $rootElement.find(".vsHide").not(toSkip);
var elemsToShow = $rootElement.find(".vsShow").not(toSkip);
// Find the element to place the toggle button in.
var toggleElement = $rootElement.find(".vsToggleElement").not(toSkip).first();
// The toggleElement becomes clickable in its entirety, but
// we need to prevent this if a contained link is clicked instead.
toggleElement.find("a").on("click", function (e) {
e.stopPropagation();
e.stopImmediatePropagation();
});
// Add the toggle button.
var toggleButton = $("<a>").attr("role", "button").attr("tabindex", "0");
$("<span>").addClass("NavToggle").attr("data-nosnippet", "").append(toggleButton).prependTo(toggleElement);
// Determine the visibility toggle category (for the links in the bar on the left).
var toggleCategory = $rootElement.data("toggle-category");
if (!toggleCategory) {
var classNames = $rootElement.attr("class").split(/\s+/);
for (var i = 0; i < classNames.length; ++i) {
var className = classNames[i].split("-");
if (className[0] == "vsToggleCategory") {
toggleCategory = className[1];
}
}
}
if (!toggleCategory)
toggleCategory = "others";
// Register the visibility toggle.
toggleElement.css("cursor", "pointer");
toggleElement.on("click", window.VisibilityToggles.register(toggleCategory,
function show() {
toggleButton.text(hideButtonText);
elemsToShow.hide();
elemsToHide.show();
},
function hide() {
toggleButton.text(showButtonText);
elemsToShow.show();
elemsToHide.hide();
},
$rootElement.is("[data-toggle-show-default]")));
}
/* ==List switching== */
function enableListSwitchGeneric(rootElement) {
if (checkAndSetToggleified(rootElement)) {
return;
}
var $rootElement = $(rootElement);
// Create a toggle button.
var $toggleElement = $("<div>").addClass("list-switcher-element");
var $navToggle = $("<span>").addClass("NavToggle").attr("data-nosnippet", "");
var $toggleButton = $("<a>").attr("role", "button").attr("tabindex", "0");
// Add the toggle button to the DOM tree.
$navToggle.append($toggleButton).prependTo($toggleElement);
$toggleElement.insertAfter($rootElement);
$toggleElement.show();
// Determine the visibility toggle category (for the links in the bar on the
// left). It will either be the value of the "data-toggle-category"
// attribute or will be based on the text of the closest preceding
// fourth-to-sixth-level header.
var toggleCategory = $rootElement.data("toggle-category")
|| getToggleCategory($rootElement[0], "other lists");
// Determine the text for the $toggleButton.
var showButtonText = "show more ▼";
var hideButtonText = "show less ▲";
var numItems;
// special handling for [[Module:collapsible category tree]]
var $categoryTreeTag = $rootElement.children(".CategoryTreeTag");
if ($categoryTreeTag) {
if ($categoryTreeTag.attr("data-pages-left-over") !== "0") {
// some category members are omitted from the list (MediaWiki limitation)
// just use basic "show more/less" in this case for now, this is a big change
//showButtonText = "show first 200 of " + $categoryTreeTag.attr("data-pages-in-cat") + " ▼";
//hideButtonText = "show fewer ▲";
} else {
// all category members are included in the list
numItems = $categoryTreeTag.attr("data-pages-in-cat");
}
} else {
// standard list-switcher using <li> elements
numItems = $rootElement.find("li").length;
}
if (numItems) {
showButtonText = "show all " + numItems + " ▼";
hideButtonText = "show fewer ▲";
}
// Register the visibility toggle.
$toggleElement.on("click", window.VisibilityToggles.register(toggleCategory,
function show() {
$toggleButton.text(hideButtonText);
if (rootElement) {
rootElement.classList.remove("list-switcher-collapsed");
}
},
function hide() {
$toggleButton.text(showButtonText);
if (rootElement) {
rootElement.classList.add("list-switcher-collapsed");
}
},
$rootElement.is("[data-toggle-show-default]")));
// Register a resize observer to see if we need to keep the
// show/hide toggle visible
var termList = rootElement.querySelector(':scope > .term-list');
if (termList && window.ResizeObserver) {
var resizeObserver = new ResizeObserver(function(entries) {
if (entries[0] && entries[0].contentBoxSize[0]) {
// Work out what the max-height would be, in pixels
// As a hack, this value is stored in the CSS `bottom`
// property, as `max-height` is only in place when
// the list is collapsed, but we need to do this check
// even when the list is not collapsed
var maxHeightPx = parseFloat(getComputedStyle(rootElement).bottom);
// If box height is less than its max height + 20 px, suppress
// collapsibility. The 20 px buffer prevents the situation where
// clicking "show more" expands the box by just a few pixels
if (entries[0].contentBoxSize[0].blockSize <= maxHeightPx + 20) {
$toggleElement.hide();
if (rootElement.classList.contains("list-switcher-collapsed")) {
rootElement.classList.remove("list-switcher-collapsed");
rootElement.classList.add("list-switcher-collapsibility-suppressed");
}
} else {
$toggleElement.show();
if (rootElement.classList.contains("list-switcher-collapsibility-suppressed")) {
rootElement.classList.remove("list-switcher-collapsibility-suppressed");
rootElement.classList.add("list-switcher-collapsed");
}
}
}
});
resizeObserver.observe(termList);
}
}
// based on [[User:Erutuon/scripts/semhide.js]], [[User:Jberkel/semhide.js]],
// [[User:Ungoliant_MMDCCLXIV/synshide.js]]
function setupNyms(index, dlTag) {
// [[Wiktionary:Semantic relations]]
var relationClasses = [ "synonym", "antonym", "hypernym", "hyponym", "meronym",
"holonym", "troponym", "comeronym", "coordinate-term",
"near-synonym", "imperfective", "perfective", "alternative-form" ];
var relations = $(dlTag).find("dd > .nyms").get().filter(
function(element) {
return Array.prototype.some.call(element.classList, function (className) {
if (relationClasses.indexOf(className) !== -1) {
element.dataset.relationClass = className;
return true;
}
});
});
function setupToggle(elements, category, visibleByDefault) {
if (elements.length === 0) return null;
var toggler = $("<a>").attr("role", "button").attr("tabindex", "0");
var text = elements.map(function (e) {
var linkCount = e.querySelectorAll("span[lang]").length;
return e.dataset.relationClass.replace("-", " ") +
(linkCount > 1 ? "s" : "");
}).join(", ");
function show() {
toggler.text(text + nbsp + "▲");
$(dlTag).show();
$(elements).show();
}
function hide() {
toggler.text(text + nbsp + "▼");
if ($(dlTag).children().length === elements.length) {
$(dlTag).hide();
} else {
$(elements).hide();
}
}
$(dlTag).before($("<span>")
.addClass("nyms-toggle")
.attr("data-nosnippet", "")
.append(toggler)
.css("margin-left", "5px"));
toggler.click(window.VisibilityToggles.register(category, show, hide, visibleByDefault));
}
var synonyms = relations.filter(function (e) {
return ["synonym", "antonym", "near-synonym", "coordinate-term", "alternative-form"].indexOf(e.dataset.relationClass) !== -1;
});
var other = relations.filter(function (e) { return synonyms.indexOf(e) === -1; });
setupToggle(synonyms, "synonyms", true /* show by default */);
setupToggle(other, "semantic relations");
}
function setupUsageExampleCollapses(index, dlTag) {
var usexTags = $(dlTag).find("dd > .h-usage-example").get();
function setupToggle(elements, category, visibleByDefault) {
if (elements.length === 0) return null;
var toggler = $("<a>").attr("role", "button").attr("tabindex", "0");
function show() {
toggler.text(category + nbsp + "▲");
$(dlTag).show();
$(elements).show();
}
function hide() {
toggler.text(category + nbsp + "▼");
if ($(dlTag).children().length === elements.length) {
$(dlTag).hide();
} else {
$(elements).hide();
}
}
$(dlTag).before($("<span>")
.addClass("nyms-toggle")
.append(toggler)
.css("margin-left", "5px"));
toggler.click(window.VisibilityToggles.register(category, show, hide, visibleByDefault));
}
var collocations = usexTags.filter(function (e) {
return $(e).hasClass("collocation");
});
var usexes = usexTags.filter(function (e) { return collocations.indexOf(e) === -1; });
setupToggle(usexes, "usage examples", true /* show by default */);
setupToggle(collocations, "collocations", true /* show by default */);
}
window.createNavToggle = createNavToggle;
window.setupHiddenQuotes = setupHiddenQuotes;
window.viewSwitching = viewSwitching;
window.getToggleCategory = getToggleCategory;
/* == Apply four functions defined above == */
mw.hook("wikipage.content").add(function($content) {
// NavToggles
$(".NavFrame", $content).each(function(){
createNavToggle(this);
});
$(".inflection-table-collapsible", $content).each(function(){
createNavToggleForInflectionTable(this);
});
// order nyms -> usexes -> quotes, to match the conventional order in entries
// synonyms and such under definitions
// if (mw.config.get("wgNamespaceNumber") === 0) {
$("dl:has(dd > .nyms)", $content).each(setupNyms);
// }
// usage examples and collocations
var namespaceNumber = mw.config.get("wgNamespaceNumber");
if (window.defaultVisibilityTogglesForUsageExamples) {
if (namespaceNumber === 0) {
$("ol > li dl:has(dd > .h-usage-example)", $content).each(setupUsageExampleCollapses);
}
}
// quotes
if (namespaceNumber === 0 || namespaceNumber === 100 || namespaceNumber === 118) {
// First, find all the ordered lists, i.e. all the series of definitions.
$("ol > li", $content).each(function(){
setupHiddenQuotes(this);
});
}
//view switching
$(".vsSwitcher", $content).each(function(){
viewSwitching(this);
});
// list switching
$(".list-switcher", $content).each(function () {
enableListSwitchGeneric(this);
});
});
jQuery(mw).on("LivePreviewDone", function (ev, sels) {
var ols = jQuery(sels.join(",")).find("ol");
for (var i = 0; i < ols.length; i++) {
for (var j = 0; j < ols[i].childNodes.length; j++) {
var li = ols[i].childNodes[j];
if (li.nodeName.toUpperCase() == "LI") {
setupHiddenQuotes(li);
}
}
}
});
})();
0cwdst3iiscohi6qwxwwbpuhjxc4vxb
MediaWiki:Gadget-VisibilityToggles.js
8
17926
88854
2025-09-23T17:18:09Z
en>Surjection
0
88854
javascript
text/javascript
/* eslint-env es5, browser, jquery */
/* eslint semi: "error" */
/* jshint esversion: 5, eqeqeq: true */
/* globals $, mw */
/* requires mw.cookie, mw.storage */
(function VisibilityTogglesIIFE () {
"use strict";
// Toggle object that is constructed so that `toggle.status = !toggle.status`
// automatically calls either `toggle.show()` or `toggle.hide()` as appropriate.
// Creating toggle also automatically calls either the show or the hide function.
function Toggle (showFunction, hideFunction) {
this.show = showFunction, this.hide = hideFunction;
}
Toggle.prototype = {
get status () {
return this._status;
},
set status (newStatus) {
if (typeof newStatus !== "boolean")
throw new TypeError("Value of 'status' must be a boolean.");
if (newStatus === this._status)
return;
this._status = newStatus;
if (this._status !== this.toggleCategory.status)
this.toggleCategory.updateToggle(this._status);
if (this._status)
this.show();
else
this.hide();
},
};
/*
* Handles storing a boolean value associated with a `name` stored in
* localStorage under `key`.
*
* The `get` method returns `true`, `false`, or `undefined` (if the storage
* hasn't been tampered with).
* The `set` method only allows setting `true` or `false`.
*/
function BooleanStorage(key, name) {
if (typeof key !== "string")
throw new TypeError("Expected string");
if (!(typeof name === "string" && name !== "")) {
throw new TypeError("Expected non-empty string");
}
this.key = key; // key for localStorage
this.name = name; // name of toggle category
function convertOldCookie(cookie) {
return cookie.split(';')
.filter(function(e) { return e !== ''; })
.reduce(function(memo, currentValue) {
var match = /(.+?)=(\d)/.exec(currentValue); // only to test for temporary =[01] format
if (match) {
memo[match[1]] = Boolean(Number(match[2]));
} else {
memo[currentValue] = true;
}
return memo;
}, {});
}
// Look for cookie in old format.
var cookie = mw.cookie.get(key);
if (cookie !== null) {
this.obj = $.extend(this.obj, convertOldCookie(cookie));
mw.cookie.set(key, null); // Remove cookie.
}
}
BooleanStorage.prototype = {
get: function () {
return this.obj[this.name];
},
set: function (value) {
if (typeof value !== "boolean")
throw new TypeError("Expected boolean");
var obj = this.obj;
if (obj[this.name] !== value) {
obj[this.name] = value;
this.obj = obj;
}
},
// obj allows getting and setting the object version of the stored value.
get obj() {
if (typeof this.rawValue !== "string")
return {};
try {
return JSON.parse(this.rawValue);
} catch (e) {
if (e instanceof SyntaxError) {
return {};
} else {
throw e;
}
}
},
set obj(value) {
// throws TypeError ("cyclic object value")
this.rawValue = JSON.stringify(value);
},
// rawValue allows simple getting and setting of the stringified object.
get rawValue () {
return mw.storage.get(this.key);
},
set rawValue (value) {
return mw.storage.set(this.key, value);
},
};
// This is a version of the actual CSS identifier syntax (described here:
// https://stackoverflow.com/a/2812097), with only ASCII and that must begin
// with an alphabetic character.
var asciiCssIdentifierRegex = /^[a-zA-Z][a-zA-Z0-9_-]+$/;
function ToggleCategory (name, defaultStatus) {
this.name = name;
this.sidebarToggle = this.newSidebarToggle();
this.storage = new BooleanStorage("Visibility", name);
this.status = this.getInitialStatus(defaultStatus);
}
// Have toggle category inherit array methods.
ToggleCategory.prototype = [];
ToggleCategory.prototype.addToggle = function (showFunction, hideFunction) {
var toggle = new Toggle(showFunction, hideFunction);
toggle.toggleCategory = this;
this.push(toggle);
toggle.status = this.status;
return toggle;
};
// Generate an identifier consisting of a lowercase ASCII letter and a random integer.
function randomAsciiCssIdentifier() {
var digits = 9;
var lowCodepoint = "a".codePointAt(0), highCodepoint = "z".codePointAt(0);
return String.fromCodePoint(
lowCodepoint + Math.floor(Math.random() * (highCodepoint - lowCodepoint)))
+ String(Math.floor(Math.random() * Math.pow(10, digits)));
}
function getCssIdentifier(name) {
name = name.replace(/\s+/g, "-");
// Generate a valid ASCII CSS identifier.
if (!asciiCssIdentifierRegex.test(name)) {
// Remove characters that are invalid in an ASCII CSS identifier.
name = name.replace(/^[^a-zA-Z]+/, "").replace(/[^a-zA-Z_-]+/g, "");
if (!asciiCssIdentifierRegex.test(name))
name = randomAsciiCssIdentifier();
}
return name;
}
// Add a new global toggle to the sidebar.
ToggleCategory.prototype.newSidebarToggle = function () {
var name = getCssIdentifier(this.name);
var id = "p-visibility-" + name;
var sidebarToggle = $("#" + id);
if (sidebarToggle.length > 0)
return sidebarToggle;
var listEntry = $("<li>");
if (mw.config.get("skin") === "vector-2022")
listEntry.addClass("mw-list-item");
sidebarToggle = $("<a>", {
id: id,
href: "#visibility-" + this.name,
})
.click((function () {
this.status = !this.status;
this.storage.set(this.status);
return false;
}).bind(this));
listEntry.append(sidebarToggle).appendTo(this.buttons);
return sidebarToggle;
};
// Update the status of the sidebar toggle for the category when all of its
// toggles on the page are toggled one way.
ToggleCategory.prototype.updateToggle = function (status) {
if (this.length > 0 && this.every(function (toggle) { return toggle.status === status; }))
this.status = status;
};
// getInitialStatus is only called when a category is first created.
ToggleCategory.prototype.getInitialStatus = function (defaultStatus) {
function isFragmentSet(name) {
return location.hash.toLowerCase().split("_")[0] === "#" + name.toLowerCase();
}
function isHideCatsSet(name) {
var match = /^.+?\?(?:.*?&)*?hidecats=(.+?)(?:&.*)?$/.exec(location.href);
if (match !== null) {
var hidecats = match[1].split(",");
for (var i = 0; i < hidecats.length; ++i) {
switch (hidecats[i]) {
case name: case "all":
return false;
case "!" + name: case "none":
return true;
}
}
}
return false;
}
function isWiktionaryPreferencesCookieSet() {
return mw.cookie.get("WiktionaryPreferencesShowNav") === "true";
}
// TODO check category-specific cookies
return isFragmentSet(this.name)
|| isHideCatsSet(this.name)
|| isWiktionaryPreferencesCookieSet()
|| (function(storedValue) {
return storedValue !== undefined ? storedValue : Boolean(defaultStatus);
}(this.storage.get()));
};
Object.defineProperties(ToggleCategory.prototype, {
status: {
get: function () {
return this._status;
},
set: function (status) {
if (typeof status !== "boolean")
throw new TypeError("Value of 'status' must be a boolean.");
if (status === this._status)
return;
this._status = status;
// Change the state of all Toggles in the ToggleCategory.
for (var i = 0; i < this.length; i++)
this[i].status = status;
this.sidebarToggle.text((status ? "Hide " : "Show ") + this.name);
},
},
buttons: {
get: function () {
var buttons = $("#p-visibility ul");
if (buttons.length > 0)
return buttons;
buttons = $("<ul>");
// unused var collapsed = mw.cookie.get("vector-nav-p-visibility") === "false";
if (mw.config.get("skin") === "vector-2022") {
/* add to right-hand side ('tools') bar */
var toolbox = $("<div>", {
"class": "vector-menu mw-portlet mw-portlet-visibility",
"id": "p-visibility"
})
.append($('<div id="p-visibility-label" aria-label="" class="vector-menu-heading">Visibility</div>'))
.append($("<div>", { class: "vector-menu-content" }).append(buttons.addClass("vector-menu-content-list")));
$('#vector-page-tools').append(toolbox);
} else {
var toolbox = $("<div>", {
"class": "vector-menu vector-menu-portal portal portlet",
"id": "p-visibility"
})
.append($('<label id="p-visibility-label" aria-label="" class="vector-menu-heading"><span class="vector-menu-heading-label">Visibility</span></label>'))
.append($("<div>", { class: "pBody body vector-menu-content" }).append(buttons));
var insert = document.getElementById("p-lang") || document.getElementById("p-feedback");
if (insert) {
$(insert).before(toolbox);
} else {
var sidebar = document.getElementById("mw-panel") || document.getElementById("column-one");
$(sidebar).append(toolbox);
}
}
return buttons;
}
}
});
function VisibilityToggles () {
// table containing ToggleCategories
this.togglesByCategory = {};
}
// Add a new toggle, adds a Show/Hide category button in the toolbar.
// Returns a function that when called, calls showFunction and hideFunction
// alternately and updates the sidebar toggle for the category if necessary.
VisibilityToggles.prototype.register = function (category, showFunction, hideFunction, defaultStatus) {
if (!(typeof category === "string" && category !== ""))
return;
var toggle = this.addToggleCategory(category, defaultStatus)
.addToggle(showFunction, hideFunction);
return function () {
toggle.status = !toggle.status;
};
};
VisibilityToggles.prototype.addToggleCategory = function (name, defaultStatus) {
return (this.togglesByCategory[name] = this.togglesByCategory[name] || new ToggleCategory(name, defaultStatus));
};
window.alternativeVisibilityToggles = new VisibilityToggles();
window.VisibilityToggles = window.alternativeVisibilityToggles;
})();
8qjkkje953u8o41wk9sjre7nivtbk7y
88855
88854
2026-08-06T22:32:26Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-VisibilityToggles.js]]
88854
javascript
text/javascript
/* eslint-env es5, browser, jquery */
/* eslint semi: "error" */
/* jshint esversion: 5, eqeqeq: true */
/* globals $, mw */
/* requires mw.cookie, mw.storage */
(function VisibilityTogglesIIFE () {
"use strict";
// Toggle object that is constructed so that `toggle.status = !toggle.status`
// automatically calls either `toggle.show()` or `toggle.hide()` as appropriate.
// Creating toggle also automatically calls either the show or the hide function.
function Toggle (showFunction, hideFunction) {
this.show = showFunction, this.hide = hideFunction;
}
Toggle.prototype = {
get status () {
return this._status;
},
set status (newStatus) {
if (typeof newStatus !== "boolean")
throw new TypeError("Value of 'status' must be a boolean.");
if (newStatus === this._status)
return;
this._status = newStatus;
if (this._status !== this.toggleCategory.status)
this.toggleCategory.updateToggle(this._status);
if (this._status)
this.show();
else
this.hide();
},
};
/*
* Handles storing a boolean value associated with a `name` stored in
* localStorage under `key`.
*
* The `get` method returns `true`, `false`, or `undefined` (if the storage
* hasn't been tampered with).
* The `set` method only allows setting `true` or `false`.
*/
function BooleanStorage(key, name) {
if (typeof key !== "string")
throw new TypeError("Expected string");
if (!(typeof name === "string" && name !== "")) {
throw new TypeError("Expected non-empty string");
}
this.key = key; // key for localStorage
this.name = name; // name of toggle category
function convertOldCookie(cookie) {
return cookie.split(';')
.filter(function(e) { return e !== ''; })
.reduce(function(memo, currentValue) {
var match = /(.+?)=(\d)/.exec(currentValue); // only to test for temporary =[01] format
if (match) {
memo[match[1]] = Boolean(Number(match[2]));
} else {
memo[currentValue] = true;
}
return memo;
}, {});
}
// Look for cookie in old format.
var cookie = mw.cookie.get(key);
if (cookie !== null) {
this.obj = $.extend(this.obj, convertOldCookie(cookie));
mw.cookie.set(key, null); // Remove cookie.
}
}
BooleanStorage.prototype = {
get: function () {
return this.obj[this.name];
},
set: function (value) {
if (typeof value !== "boolean")
throw new TypeError("Expected boolean");
var obj = this.obj;
if (obj[this.name] !== value) {
obj[this.name] = value;
this.obj = obj;
}
},
// obj allows getting and setting the object version of the stored value.
get obj() {
if (typeof this.rawValue !== "string")
return {};
try {
return JSON.parse(this.rawValue);
} catch (e) {
if (e instanceof SyntaxError) {
return {};
} else {
throw e;
}
}
},
set obj(value) {
// throws TypeError ("cyclic object value")
this.rawValue = JSON.stringify(value);
},
// rawValue allows simple getting and setting of the stringified object.
get rawValue () {
return mw.storage.get(this.key);
},
set rawValue (value) {
return mw.storage.set(this.key, value);
},
};
// This is a version of the actual CSS identifier syntax (described here:
// https://stackoverflow.com/a/2812097), with only ASCII and that must begin
// with an alphabetic character.
var asciiCssIdentifierRegex = /^[a-zA-Z][a-zA-Z0-9_-]+$/;
function ToggleCategory (name, defaultStatus) {
this.name = name;
this.sidebarToggle = this.newSidebarToggle();
this.storage = new BooleanStorage("Visibility", name);
this.status = this.getInitialStatus(defaultStatus);
}
// Have toggle category inherit array methods.
ToggleCategory.prototype = [];
ToggleCategory.prototype.addToggle = function (showFunction, hideFunction) {
var toggle = new Toggle(showFunction, hideFunction);
toggle.toggleCategory = this;
this.push(toggle);
toggle.status = this.status;
return toggle;
};
// Generate an identifier consisting of a lowercase ASCII letter and a random integer.
function randomAsciiCssIdentifier() {
var digits = 9;
var lowCodepoint = "a".codePointAt(0), highCodepoint = "z".codePointAt(0);
return String.fromCodePoint(
lowCodepoint + Math.floor(Math.random() * (highCodepoint - lowCodepoint)))
+ String(Math.floor(Math.random() * Math.pow(10, digits)));
}
function getCssIdentifier(name) {
name = name.replace(/\s+/g, "-");
// Generate a valid ASCII CSS identifier.
if (!asciiCssIdentifierRegex.test(name)) {
// Remove characters that are invalid in an ASCII CSS identifier.
name = name.replace(/^[^a-zA-Z]+/, "").replace(/[^a-zA-Z_-]+/g, "");
if (!asciiCssIdentifierRegex.test(name))
name = randomAsciiCssIdentifier();
}
return name;
}
// Add a new global toggle to the sidebar.
ToggleCategory.prototype.newSidebarToggle = function () {
var name = getCssIdentifier(this.name);
var id = "p-visibility-" + name;
var sidebarToggle = $("#" + id);
if (sidebarToggle.length > 0)
return sidebarToggle;
var listEntry = $("<li>");
if (mw.config.get("skin") === "vector-2022")
listEntry.addClass("mw-list-item");
sidebarToggle = $("<a>", {
id: id,
href: "#visibility-" + this.name,
})
.click((function () {
this.status = !this.status;
this.storage.set(this.status);
return false;
}).bind(this));
listEntry.append(sidebarToggle).appendTo(this.buttons);
return sidebarToggle;
};
// Update the status of the sidebar toggle for the category when all of its
// toggles on the page are toggled one way.
ToggleCategory.prototype.updateToggle = function (status) {
if (this.length > 0 && this.every(function (toggle) { return toggle.status === status; }))
this.status = status;
};
// getInitialStatus is only called when a category is first created.
ToggleCategory.prototype.getInitialStatus = function (defaultStatus) {
function isFragmentSet(name) {
return location.hash.toLowerCase().split("_")[0] === "#" + name.toLowerCase();
}
function isHideCatsSet(name) {
var match = /^.+?\?(?:.*?&)*?hidecats=(.+?)(?:&.*)?$/.exec(location.href);
if (match !== null) {
var hidecats = match[1].split(",");
for (var i = 0; i < hidecats.length; ++i) {
switch (hidecats[i]) {
case name: case "all":
return false;
case "!" + name: case "none":
return true;
}
}
}
return false;
}
function isWiktionaryPreferencesCookieSet() {
return mw.cookie.get("WiktionaryPreferencesShowNav") === "true";
}
// TODO check category-specific cookies
return isFragmentSet(this.name)
|| isHideCatsSet(this.name)
|| isWiktionaryPreferencesCookieSet()
|| (function(storedValue) {
return storedValue !== undefined ? storedValue : Boolean(defaultStatus);
}(this.storage.get()));
};
Object.defineProperties(ToggleCategory.prototype, {
status: {
get: function () {
return this._status;
},
set: function (status) {
if (typeof status !== "boolean")
throw new TypeError("Value of 'status' must be a boolean.");
if (status === this._status)
return;
this._status = status;
// Change the state of all Toggles in the ToggleCategory.
for (var i = 0; i < this.length; i++)
this[i].status = status;
this.sidebarToggle.text((status ? "Hide " : "Show ") + this.name);
},
},
buttons: {
get: function () {
var buttons = $("#p-visibility ul");
if (buttons.length > 0)
return buttons;
buttons = $("<ul>");
// unused var collapsed = mw.cookie.get("vector-nav-p-visibility") === "false";
if (mw.config.get("skin") === "vector-2022") {
/* add to right-hand side ('tools') bar */
var toolbox = $("<div>", {
"class": "vector-menu mw-portlet mw-portlet-visibility",
"id": "p-visibility"
})
.append($('<div id="p-visibility-label" aria-label="" class="vector-menu-heading">Visibility</div>'))
.append($("<div>", { class: "vector-menu-content" }).append(buttons.addClass("vector-menu-content-list")));
$('#vector-page-tools').append(toolbox);
} else {
var toolbox = $("<div>", {
"class": "vector-menu vector-menu-portal portal portlet",
"id": "p-visibility"
})
.append($('<label id="p-visibility-label" aria-label="" class="vector-menu-heading"><span class="vector-menu-heading-label">Visibility</span></label>'))
.append($("<div>", { class: "pBody body vector-menu-content" }).append(buttons));
var insert = document.getElementById("p-lang") || document.getElementById("p-feedback");
if (insert) {
$(insert).before(toolbox);
} else {
var sidebar = document.getElementById("mw-panel") || document.getElementById("column-one");
$(sidebar).append(toolbox);
}
}
return buttons;
}
}
});
function VisibilityToggles () {
// table containing ToggleCategories
this.togglesByCategory = {};
}
// Add a new toggle, adds a Show/Hide category button in the toolbar.
// Returns a function that when called, calls showFunction and hideFunction
// alternately and updates the sidebar toggle for the category if necessary.
VisibilityToggles.prototype.register = function (category, showFunction, hideFunction, defaultStatus) {
if (!(typeof category === "string" && category !== ""))
return;
var toggle = this.addToggleCategory(category, defaultStatus)
.addToggle(showFunction, hideFunction);
return function () {
toggle.status = !toggle.status;
};
};
VisibilityToggles.prototype.addToggleCategory = function (name, defaultStatus) {
return (this.togglesByCategory[name] = this.togglesByCategory[name] || new ToggleCategory(name, defaultStatus));
};
window.alternativeVisibilityToggles = new VisibilityToggles();
window.VisibilityToggles = window.alternativeVisibilityToggles;
})();
8qjkkje953u8o41wk9sjre7nivtbk7y
MediaWiki:Gadgets-definition
8
17927
88856
2026-08-01T13:57:56Z
en>Fenakhay
0
88856
wikitext
text/x-wiki
{{shortcut|WT:GD}}
See [[Special:Gadgets]] for documentation of these gadgets, and [[Wiktionary:Gadgets]] for general information about gadgets on Wiktionary. See also [[WT:PREFS]] for other preferences.
{{gadgets pages}}
== appearance ==
* MonobookSmallerHeadings[ResourceLoader|default|skins=monobook]|MonobookSmallerHeadings.css
* LanguagesAndScripts[ResourceLoader|default]|LanguagesAndScripts.css
* VectorClassic[ResourceLoader|type=styles|skins=vector]|VectorClassic.css
* CatchMyAttention[ResourceLoader|type=styles]|CatchMyAttention.css
* HideLogo[ResourceLoader|type=styles]|HideLogo.css
* vectorTabs[ResourceLoader|skins=vector|type=styles]|vectorTabs.js|vectorTabs.css
* WiktCountryFlags[ResourceLoader|type=styles|namespaces=0]|WiktCountryFlags.css
* WiktPreviewRightTOCs[ResourceLoader|namespaces=0]|WiktPreviewRightTOCs.css
* LogoTiles[ResourceLoader|type=styles]|LogoTiles.css
* RoundBullets4Lists[ResourceLoader|type=styles]|RoundBullets4Lists.css
* BC-AD[ResourceLoader]|BC-AD.js
* AutoContrastFixer[ResourceLoader|skins=vector-2022,minerva|dependencies=ext.gadget.WiktGadgetPrefs]|AutoContrastFixer.js
* ShowIDs[ResourceLoader|dependencies=mediawiki.util|peers=ShowIDs-pagestyles]|ShowIDs.js
* TreeDescs[ResourceLoader|peers=TreeDescs-pagestyles]|TreeDescs.js
* NoMiniToc[ResourceLoader|type=general]|NoMiniToc.js|NoMiniToc.css
* NsfwOverride[ResourceLoader|dependencies=mediawiki.util]|NsfwOverride.js|NsfwOverride.css
* MobileCollapseCategories[ResourceLoader|skins=minerva|dependencies=mediawiki.util,ext.gadget.VisibilityToggles]|MobileCollapseCategories.js
== interface-gadgets ==
* TargetedTranslations[ResourceLoader|default|dependencies=site,mediawiki.storage,mediawiki.cookie,ext.gadget.StorageUtils|categories=Entries with translation boxes]|TargetedTranslations.js|TargetedTranslations.css
* BlockInfo[ResourceLoader|default|dependencies=site,mediawiki.api,mediawiki.language.months,mediawiki.util|namespaces=-1,2,3]|BlockInfo.js
* RevdelInfo[ResourceLoader|dependencies=site,mediawiki.api,mediawiki.language.months,mediawiki.util]|RevdelInfo.js
* CodeLinks[ResourceLoader|dependencies=site,mediawiki.util]|CodeLinks.js
* purgetab[ResourceLoader|dependencies=mediawiki.api,mediawiki.util]|purgetab.js
* Navigation_popups[ResourceLoader]|popups.js
* PagePreviews[ResourceLoader|default|dependencies=site,mediawiki.api,mediawiki.util,mediawiki.Title|skins=vector,vector-2022,monobook,timeless,modern,cologneblue]|PagePreviews.js
* CommentsInLocalTime[ResourceLoader]|CommentsInLocalTime.js
* UTCLiveClock[ResourceLoader|type=general|dependencies=mediawiki.util,mediawiki.api|peers=UTCLiveClock-pagestyles]|UTCLiveClock.js|UTCLiveClock.css
* WiktSidebarTranslation[ResourceLoader|dependencies=ext.uls.interlanguage]|WiktSidebarTranslation.js
* searchFocus[ResourceLoader]|searchFocus.js
* TabbedLanguages[ResourceLoader|dependencies=mediawiki.cookie,mediawiki.util|type=general|skins=vector,vector-2022,monobook,timeless,modern,cologneblue]|TabbedLanguages.js|TabbedLanguages.css
* FastRollback[ResourceLoader|rights=rollback|dependencies=mediawiki.api]|FastRollback.js
* AudioBotBlacklist[ResourceLoader|default|dependencies=mediawiki.api,oojs-ui-core,oojs-ui-widgets,oojs-ui-windows,ext.gadget.WiktGadgetPrefs|rights=autoconfirmed]| AudioBotBlacklist.js
* OrangeLinks[ResourceLoader|dependencies=mediawiki.api,mediawiki.Title,mediawiki.util]|OrangeLinks.js
* U2693[ResourceLoader|dependencies=mediawiki.Title,mediawiki.util|skins=vector,vector-2022,monobook,timeless,modern,cologneblue]|U2693.js
* AggregateInterprojectLinks[ResourceLoader|dependencies=mediawiki.cookie]|AggregateInterprojectLinks.js
* MoreMenu-local[ResourceLoader|dependencies=mediawiki.api,mediawiki.util,user.options]|MoreMenu-local.js
* nearby[ResourceLoader|dependencies=mediawiki.util,mediawiki.api,ext.gadget.UnsupportedTitles|namespaces=0,100,118]|nearby.js
* linkLanguageHeaders[ResourceLoader|dependencies=mediawiki.api|namespaces=0,118]|linkLanguageHeaders.js
* ReferenceTooltips[ResourceLoader|default|type=general|dependencies=mediawiki.cookie,jquery.client,site]|ReferenceTooltips.js
* categoryTreeLanguageNames[ResourceLoader|dependencies=mediawiki.api,mediawiki.util|namespaces=0,14,100,118]|categoryTreeLanguageNames.js
* Streamline[ResourceLoader|dependencies=mediawiki.api,mediawiki.util,ext.gadget.WiktGadgetPrefs,ext.gadget.defaultVisibilityToggles|namespaces=0,4,118]|Streamline.js
* minitoc[ResourceLoader|default|dependencies=site|categories=Pages calling Template:minitoc,Mammoth pages]|minitoc.js
* EtytreeZoom[ResourceLoader|default|dependencies=site,mediawiki.util|categories=Pages with etymology trees]|EtytreeZoom.js
* CategoryJumpTo[ResourceLoader|dependencies=mediawiki.api,mediawiki.notification,mediawiki.util,oojs-ui-core|namespaces=14]|CategoryJumpTo.js
* Anchor[ResourceLoader|dependencies=mediawiki.notification,mediawiki.util,ext.gadget.WiktGadgetPrefs]|Anchor.js
* JumpToError[ResourceLoader|dependencies=mediawiki.notification]|JumpToError.js
* VetVoters[ResourceLoader|dependencies=mediawiki.util,mediawiki.api|namespaces=4,5]|VetVoters.js
* VisibilityToggles[ResourceLoader|dependencies=user,mediawiki.cookie]|VisibilityToggles.js
* defaultVisibilityToggles[ResourceLoader|default|dependencies=ext.gadget.VisibilityToggles]|defaultVisibilityToggles.js
== editing-gadgets ==
* TranslationAdder[ResourceLoader|default|dependencies=site,ext.gadget.Editor,mediawiki.cookie,ext.gadget.LanguageUtils,mediawiki.util,ext.gadget.TranslationAdder-Data|categories=Entries with translation boxes]|TranslationAdder.js
* RhymesAdder[ResourceLoader|default|dependencies=site,ext.gadget.Editor,mediawiki.util,ext.gadget.LanguageUtils|namespaces=106]|RhymesAdder.js
* PatrollingEnhancements[ResourceLoader|default|dependencies=site|rights=patrol]|PatrollingEnhancements.js
* RegexMenuFramework[ResourceLoader]|RegexMenuFramework.js
* AcceleratedFormCreation[ResourceLoader|dependencies=mediawiki.util]|AcceleratedFormCreation.css|AcceleratedFormCreation.js
* HotCat[ResourceLoader]|HotCat.js
* FastRevert[ResourceLoader|dependencies=mediawiki.util]|FastRevert.js
* ChangesByLanguage[ResourceLoader]|ChangesByLanguage.js
* DefSideBoxes[ResourceLoader|dependencies=mediawiki.cookie,mediawiki.util,ext.gadget.DefinitionsAdder|type=general]|DefSideBoxes.js|DefSideBoxes.css
* DotsSyntaxHighlighter[ResourceLoader]|DotsSyntaxHighlighter.js
* DeveloperEditorTweaks[ResourceLoader|dependencies=ext.wikiEditor,mediawiki.util]|DeveloperEditorTweaks.js
* AjaxEdit[ResourceLoader|dependencies=mediawiki.util,mediawiki.user,mediawiki.api|skins=vector,vector-2022,monobook,timeless,modern,cologneblue]|AjaxEdit.js
* EditHere[ResourceLoader]|EditHere.js
* FlagMovedPageForDeletion[ResourceLoader|default|dependencies=site,mediawiki.api|namespaces=-1]|FlagMovedPageForDeletion.js
== misc-gadgets ==
* zhDialMap[ResourceLoader|default|dependencies=site,oojs-ui-core|categories=Chinese dialectal equivalent maps]|zhDialMap.js|zhDialMap.css
* DialectMap[ResourceLoader|default|dependencies=site]|DialectMap.js|DialectMap.css
* NoAutoCaseRedirect[ResourceLoader]|NoAutoCaseRedirect.js
* aWa[ResourceLoader|rights=autopatrol|dependencies=mediawiki.util,mediawiki.api,mediawiki.user,user.options,mediawiki.Title|type=general]|aWa.js|aWa.css
* QQ[ResourceLoader|dependencies=jquery.ui,mediawiki.util|type=general]|QQ.js|QQ.css
* AutoGlossary[ResourceLoader|default|dependencies=site,mediawiki.api|categories=Pages using auto-glossary]|AutoGlossary.js
== Hidden gadgets and utils ==
* AGprefs[ResourceLoader|hidden|dependencies=mediawiki.util,oojs-ui-core]|AGprefs.js
* DocTabs[ResourceLoader|hidden|default|dependencies=mediawiki.api,mediawiki.util|namespaces=0,1,10,11,114,828,829]|DocTabs.js|DocTabs.css
* Editor[ResourceLoader|hidden|dependencies=mediawiki.util,mediawiki.api]|Editor.js
* StorageUtils[ResourceLoader|hidden|dependencies=mediawiki.cookie,mediawiki.storage]|StorageUtils.js
* LanguageUtils[ResourceLoader|hidden|dependencies=mediawiki.api,ext.gadget.StorageUtils]|LanguageUtils.js
* DefinitionsAdder[ResourceLoader|hidden|dependencies=mediawiki.api,mediawiki.util,ext.gadget.Editor]|DefinitionsAdder.js
* TranslationAdder-Data[ResourceLoader|hidden|dependencies=mediawiki.api,ext.gadget.LanguageUtils|categories=Entries with translation boxes]|TranslationAdder-Data.js
* catfixRegrouper[ResourceLoader|hidden|default|dependencies=site,mediawiki.Title,ext.gadget.catfixRegrouper-Data|namespaces=14]|catfixRegrouper.js
* catfixRegrouper-Data[ResourceLoader|hidden|default|dependencies=site|namespaces=14]|catfixRegrouper-Data.js
* catfix[ResourceLoader|hidden|default|dependencies=site,mediawiki.Title|categories=Pages using catfix]|catfix.js
* Edittools[ResourceLoader|hidden|default|dependencies=mediawiki.cookie,jquery.textSelection]|Edittools.js|Edittools.css
* UnsupportedTitles[ResourceLoader|hidden|default|package]|UnsupportedTitles.js|UnsupportedTitles.json
* CssTest[ResourceLoader|hidden|type=styles]|CssTest.css
* Palette[ResourceLoader|hidden|default]|Palette.css
* UTCLiveClock-pagestyles[hidden|skins=vector,monobook]|UTCLiveClock-pagestyles.css
* Site[ResourceLoader|hidden|default]|Site.css
* SiteJS[ResourceLoader|hidden|default]|Site.js
* InflectionTable[ResourceLoader|hidden|default]|InflectionTable.css
* WiktGadgetPrefs[ResourceLoader|hidden|default|dependencies=mediawiki.user,user.options]|WiktGadgetPrefs.js
* ShowIDs-pagestyles[hidden|type=styles]|ShowIDs-pagestyles.css
* TreeDescs-pagestyles[hidden|type=styles]|TreeDescs-pagestyles.css
cq6u60f8n5buu87gslulknl40c84pjs
88857
88856
2026-08-06T22:32:57Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadgets-definition]]
88856
wikitext
text/x-wiki
{{shortcut|WT:GD}}
See [[Special:Gadgets]] for documentation of these gadgets, and [[Wiktionary:Gadgets]] for general information about gadgets on Wiktionary. See also [[WT:PREFS]] for other preferences.
{{gadgets pages}}
== appearance ==
* MonobookSmallerHeadings[ResourceLoader|default|skins=monobook]|MonobookSmallerHeadings.css
* LanguagesAndScripts[ResourceLoader|default]|LanguagesAndScripts.css
* VectorClassic[ResourceLoader|type=styles|skins=vector]|VectorClassic.css
* CatchMyAttention[ResourceLoader|type=styles]|CatchMyAttention.css
* HideLogo[ResourceLoader|type=styles]|HideLogo.css
* vectorTabs[ResourceLoader|skins=vector|type=styles]|vectorTabs.js|vectorTabs.css
* WiktCountryFlags[ResourceLoader|type=styles|namespaces=0]|WiktCountryFlags.css
* WiktPreviewRightTOCs[ResourceLoader|namespaces=0]|WiktPreviewRightTOCs.css
* LogoTiles[ResourceLoader|type=styles]|LogoTiles.css
* RoundBullets4Lists[ResourceLoader|type=styles]|RoundBullets4Lists.css
* BC-AD[ResourceLoader]|BC-AD.js
* AutoContrastFixer[ResourceLoader|skins=vector-2022,minerva|dependencies=ext.gadget.WiktGadgetPrefs]|AutoContrastFixer.js
* ShowIDs[ResourceLoader|dependencies=mediawiki.util|peers=ShowIDs-pagestyles]|ShowIDs.js
* TreeDescs[ResourceLoader|peers=TreeDescs-pagestyles]|TreeDescs.js
* NoMiniToc[ResourceLoader|type=general]|NoMiniToc.js|NoMiniToc.css
* NsfwOverride[ResourceLoader|dependencies=mediawiki.util]|NsfwOverride.js|NsfwOverride.css
* MobileCollapseCategories[ResourceLoader|skins=minerva|dependencies=mediawiki.util,ext.gadget.VisibilityToggles]|MobileCollapseCategories.js
== interface-gadgets ==
* TargetedTranslations[ResourceLoader|default|dependencies=site,mediawiki.storage,mediawiki.cookie,ext.gadget.StorageUtils|categories=Entries with translation boxes]|TargetedTranslations.js|TargetedTranslations.css
* BlockInfo[ResourceLoader|default|dependencies=site,mediawiki.api,mediawiki.language.months,mediawiki.util|namespaces=-1,2,3]|BlockInfo.js
* RevdelInfo[ResourceLoader|dependencies=site,mediawiki.api,mediawiki.language.months,mediawiki.util]|RevdelInfo.js
* CodeLinks[ResourceLoader|dependencies=site,mediawiki.util]|CodeLinks.js
* purgetab[ResourceLoader|dependencies=mediawiki.api,mediawiki.util]|purgetab.js
* Navigation_popups[ResourceLoader]|popups.js
* PagePreviews[ResourceLoader|default|dependencies=site,mediawiki.api,mediawiki.util,mediawiki.Title|skins=vector,vector-2022,monobook,timeless,modern,cologneblue]|PagePreviews.js
* CommentsInLocalTime[ResourceLoader]|CommentsInLocalTime.js
* UTCLiveClock[ResourceLoader|type=general|dependencies=mediawiki.util,mediawiki.api|peers=UTCLiveClock-pagestyles]|UTCLiveClock.js|UTCLiveClock.css
* WiktSidebarTranslation[ResourceLoader|dependencies=ext.uls.interlanguage]|WiktSidebarTranslation.js
* searchFocus[ResourceLoader]|searchFocus.js
* TabbedLanguages[ResourceLoader|dependencies=mediawiki.cookie,mediawiki.util|type=general|skins=vector,vector-2022,monobook,timeless,modern,cologneblue]|TabbedLanguages.js|TabbedLanguages.css
* FastRollback[ResourceLoader|rights=rollback|dependencies=mediawiki.api]|FastRollback.js
* AudioBotBlacklist[ResourceLoader|default|dependencies=mediawiki.api,oojs-ui-core,oojs-ui-widgets,oojs-ui-windows,ext.gadget.WiktGadgetPrefs|rights=autoconfirmed]| AudioBotBlacklist.js
* OrangeLinks[ResourceLoader|dependencies=mediawiki.api,mediawiki.Title,mediawiki.util]|OrangeLinks.js
* U2693[ResourceLoader|dependencies=mediawiki.Title,mediawiki.util|skins=vector,vector-2022,monobook,timeless,modern,cologneblue]|U2693.js
* AggregateInterprojectLinks[ResourceLoader|dependencies=mediawiki.cookie]|AggregateInterprojectLinks.js
* MoreMenu-local[ResourceLoader|dependencies=mediawiki.api,mediawiki.util,user.options]|MoreMenu-local.js
* nearby[ResourceLoader|dependencies=mediawiki.util,mediawiki.api,ext.gadget.UnsupportedTitles|namespaces=0,100,118]|nearby.js
* linkLanguageHeaders[ResourceLoader|dependencies=mediawiki.api|namespaces=0,118]|linkLanguageHeaders.js
* ReferenceTooltips[ResourceLoader|default|type=general|dependencies=mediawiki.cookie,jquery.client,site]|ReferenceTooltips.js
* categoryTreeLanguageNames[ResourceLoader|dependencies=mediawiki.api,mediawiki.util|namespaces=0,14,100,118]|categoryTreeLanguageNames.js
* Streamline[ResourceLoader|dependencies=mediawiki.api,mediawiki.util,ext.gadget.WiktGadgetPrefs,ext.gadget.defaultVisibilityToggles|namespaces=0,4,118]|Streamline.js
* minitoc[ResourceLoader|default|dependencies=site|categories=Pages calling Template:minitoc,Mammoth pages]|minitoc.js
* EtytreeZoom[ResourceLoader|default|dependencies=site,mediawiki.util|categories=Pages with etymology trees]|EtytreeZoom.js
* CategoryJumpTo[ResourceLoader|dependencies=mediawiki.api,mediawiki.notification,mediawiki.util,oojs-ui-core|namespaces=14]|CategoryJumpTo.js
* Anchor[ResourceLoader|dependencies=mediawiki.notification,mediawiki.util,ext.gadget.WiktGadgetPrefs]|Anchor.js
* JumpToError[ResourceLoader|dependencies=mediawiki.notification]|JumpToError.js
* VetVoters[ResourceLoader|dependencies=mediawiki.util,mediawiki.api|namespaces=4,5]|VetVoters.js
* VisibilityToggles[ResourceLoader|dependencies=user,mediawiki.cookie]|VisibilityToggles.js
* defaultVisibilityToggles[ResourceLoader|default|dependencies=ext.gadget.VisibilityToggles]|defaultVisibilityToggles.js
== editing-gadgets ==
* TranslationAdder[ResourceLoader|default|dependencies=site,ext.gadget.Editor,mediawiki.cookie,ext.gadget.LanguageUtils,mediawiki.util,ext.gadget.TranslationAdder-Data|categories=Entries with translation boxes]|TranslationAdder.js
* RhymesAdder[ResourceLoader|default|dependencies=site,ext.gadget.Editor,mediawiki.util,ext.gadget.LanguageUtils|namespaces=106]|RhymesAdder.js
* PatrollingEnhancements[ResourceLoader|default|dependencies=site|rights=patrol]|PatrollingEnhancements.js
* RegexMenuFramework[ResourceLoader]|RegexMenuFramework.js
* AcceleratedFormCreation[ResourceLoader|dependencies=mediawiki.util]|AcceleratedFormCreation.css|AcceleratedFormCreation.js
* HotCat[ResourceLoader]|HotCat.js
* FastRevert[ResourceLoader|dependencies=mediawiki.util]|FastRevert.js
* ChangesByLanguage[ResourceLoader]|ChangesByLanguage.js
* DefSideBoxes[ResourceLoader|dependencies=mediawiki.cookie,mediawiki.util,ext.gadget.DefinitionsAdder|type=general]|DefSideBoxes.js|DefSideBoxes.css
* DotsSyntaxHighlighter[ResourceLoader]|DotsSyntaxHighlighter.js
* DeveloperEditorTweaks[ResourceLoader|dependencies=ext.wikiEditor,mediawiki.util]|DeveloperEditorTweaks.js
* AjaxEdit[ResourceLoader|dependencies=mediawiki.util,mediawiki.user,mediawiki.api|skins=vector,vector-2022,monobook,timeless,modern,cologneblue]|AjaxEdit.js
* EditHere[ResourceLoader]|EditHere.js
* FlagMovedPageForDeletion[ResourceLoader|default|dependencies=site,mediawiki.api|namespaces=-1]|FlagMovedPageForDeletion.js
== misc-gadgets ==
* zhDialMap[ResourceLoader|default|dependencies=site,oojs-ui-core|categories=Chinese dialectal equivalent maps]|zhDialMap.js|zhDialMap.css
* DialectMap[ResourceLoader|default|dependencies=site]|DialectMap.js|DialectMap.css
* NoAutoCaseRedirect[ResourceLoader]|NoAutoCaseRedirect.js
* aWa[ResourceLoader|rights=autopatrol|dependencies=mediawiki.util,mediawiki.api,mediawiki.user,user.options,mediawiki.Title|type=general]|aWa.js|aWa.css
* QQ[ResourceLoader|dependencies=jquery.ui,mediawiki.util|type=general]|QQ.js|QQ.css
* AutoGlossary[ResourceLoader|default|dependencies=site,mediawiki.api|categories=Pages using auto-glossary]|AutoGlossary.js
== Hidden gadgets and utils ==
* AGprefs[ResourceLoader|hidden|dependencies=mediawiki.util,oojs-ui-core]|AGprefs.js
* DocTabs[ResourceLoader|hidden|default|dependencies=mediawiki.api,mediawiki.util|namespaces=0,1,10,11,114,828,829]|DocTabs.js|DocTabs.css
* Editor[ResourceLoader|hidden|dependencies=mediawiki.util,mediawiki.api]|Editor.js
* StorageUtils[ResourceLoader|hidden|dependencies=mediawiki.cookie,mediawiki.storage]|StorageUtils.js
* LanguageUtils[ResourceLoader|hidden|dependencies=mediawiki.api,ext.gadget.StorageUtils]|LanguageUtils.js
* DefinitionsAdder[ResourceLoader|hidden|dependencies=mediawiki.api,mediawiki.util,ext.gadget.Editor]|DefinitionsAdder.js
* TranslationAdder-Data[ResourceLoader|hidden|dependencies=mediawiki.api,ext.gadget.LanguageUtils|categories=Entries with translation boxes]|TranslationAdder-Data.js
* catfixRegrouper[ResourceLoader|hidden|default|dependencies=site,mediawiki.Title,ext.gadget.catfixRegrouper-Data|namespaces=14]|catfixRegrouper.js
* catfixRegrouper-Data[ResourceLoader|hidden|default|dependencies=site|namespaces=14]|catfixRegrouper-Data.js
* catfix[ResourceLoader|hidden|default|dependencies=site,mediawiki.Title|categories=Pages using catfix]|catfix.js
* Edittools[ResourceLoader|hidden|default|dependencies=mediawiki.cookie,jquery.textSelection]|Edittools.js|Edittools.css
* UnsupportedTitles[ResourceLoader|hidden|default|package]|UnsupportedTitles.js|UnsupportedTitles.json
* CssTest[ResourceLoader|hidden|type=styles]|CssTest.css
* Palette[ResourceLoader|hidden|default]|Palette.css
* UTCLiveClock-pagestyles[hidden|skins=vector,monobook]|UTCLiveClock-pagestyles.css
* Site[ResourceLoader|hidden|default]|Site.css
* SiteJS[ResourceLoader|hidden|default]|Site.js
* InflectionTable[ResourceLoader|hidden|default]|InflectionTable.css
* WiktGadgetPrefs[ResourceLoader|hidden|default|dependencies=mediawiki.user,user.options]|WiktGadgetPrefs.js
* ShowIDs-pagestyles[hidden|type=styles]|ShowIDs-pagestyles.css
* TreeDescs-pagestyles[hidden|type=styles]|TreeDescs-pagestyles.css
cq6u60f8n5buu87gslulknl40c84pjs
MediaWiki:Gadget-ReferenceTooltips.js
8
17928
88858
2024-08-15T02:37:51Z
en>Ioaxxere
0
try this
88858
javascript
text/javascript
if (document.querySelector(".reference") && mw.config.values.skin !== "minerva") {
mw.loader.load('https://en.wikipedia.org/w/load.php?modules=ext.gadget.ReferenceTooltips&only=scripts');
mw.loader.load('https://en.wikipedia.org/w/load.php?modules=ext.gadget.ReferenceTooltips&only=styles', 'text/css');
}
338nz65nbt8zoj2jtwrzzy2sx41v3kz
88859
88858
2026-08-06T22:33:34Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-ReferenceTooltips.js]]
88858
javascript
text/javascript
if (document.querySelector(".reference") && mw.config.values.skin !== "minerva") {
mw.loader.load('https://en.wikipedia.org/w/load.php?modules=ext.gadget.ReferenceTooltips&only=scripts');
mw.loader.load('https://en.wikipedia.org/w/load.php?modules=ext.gadget.ReferenceTooltips&only=styles', 'text/css');
}
338nz65nbt8zoj2jtwrzzy2sx41v3kz
MediaWiki:Gadget-Site.css
8
17929
88860
2026-01-22T08:36:32Z
en>This, that and the other
0
suppress collapsing of NavFrames with class no-collapse
88860
css
text/css
/* ----------
<nowiki>
CSS Style
* Indent with tab characters
* One line space between each declaration
* One selector per line
* Opening brace on the same line as the last selector
* Don't omit optional semicolons
* Closing brace on its own line
* Closing brace indented to match the preceding style declarations
* Alternatively, very short style rules may be all on one line
---------- */
/* csslint adjoining-classes:false */
/* See also [[MediaWiki:Minerva.css]], [[MediaWiki:Vector.css]] */
sup,
sub {
/* prevent odd line-height for raised and lowered characters */
line-height: 1em;
}
/* and keep links at normal font size/weight, else they inherit the header
attributes */
.editsection {
font-size: 100%;
font-weight: normal;
}
pre code,
code code {
border: none;
padding: 0;
}
pre var,
code var {
color: var(--wikt-palette-grey-9,#777);
}
/* This fixes a bug where certain Tibetan terms are cut off at the bottom... */
#firstHeading {
overflow: visible;
}
.ns-0 .timed-text {
display: none;
}
.client-js .audiometa {
display: none;
}
.client-nojs .audiofile {
display: none;
}
.nowrap {
white-space: nowrap;
}
/* wikitable is part of core. prettytable is deprecated but still used in some
places, until fully replaced: */
table.prettytable {
margin: 1em 1em 1em 0;
background: var(--wikt-palette-paleblue, #f8f9fa);
border: 1px var(--border-color-base, #aaaaaa) solid;
border-collapse: collapse;
}
table.prettytable th,
table.prettytable td {
border: 1px var(--border-color-base, #aaaaaa) solid;
padding: 0.2em;
}
table.prettytable th {
background: var(--wikt-palette-lightergrey, #eeeeee);
text-align: center;
}
table.prettytable caption {
margin-left: inherit;
margin-right: inherit;
}
/* default setting for {{qualifier}} and {{sense}}.
(Related classes that can be overridden: .qualifier-comma,
.qualifier-content, .ib-brac, .qualifier-brac, .sense-qualifier-colon.) */
.ib-comma {
font-style: normal;
}
.ib-content {
font-style: italic;
}
.ib-content i,
.ib-content em {
font-style: normal;
}
.context-qualifier-colon {
display: none;
}
/* default style for indented "see also" */
.disambig-see-also {
text-indent: 2em;
}
.mention-tr {
font-style: italic;
}
/* add left-to-right mark */
.mention-gloss-paren::before,
.mention-tr-paren::before {
content: "";
}
/* non-italic and non-bold gloss */
.mention-gloss,
.mention-gloss-double-quote,
.mention-gloss-paren,
.mention-tr-gloss-separator-comma,
.mention-tr-paren,
.mention-tr-gloss-separator-comma {
font-style: normal;
font-weight: inherit;
}
/* default style for "form of" definitions */
.use-with-mention,
.form-of-definition {
font-style: italic;
}
.use-with-mention i,
.form-of-definition-link {
font-style: normal;
}
.use-with-mention .mention,
.form-of-definition-link .mention {
font-style: normal;
font-weight: bold;
}
/* display normalization in {{ux}}/{{usex}}, {{quote}}, {{quote-*}} in italics */
.e-normalization {
font-style: italic;
}
/* Inflection tables
----------------------------- */
/* To colour links in inflection tables (currently done with
{{notred}}) black without using #ifexist and allowing for user
override */
.inflection-table a.new {
color: inherit;
}
/* fill a parent NavContent collapsing box */
.NavContent table.inflection,
.NavContent table.inflection-table {
margin-top: 0;
width: 100%;
display: table;
border: 0;
}
/* basic inflection table styles */
table.inflection {
border-collapse: collapse;
border: 1px solid;
background: #ffffff;
/* TODO consider palettizing */
}
table.inflection caption {
caption-side: bottom;
padding: 0.33em 1em;
text-align: left;
font-size: smaller;
}
/* subtle grey for alternate row backgrounds */
table.inflection tr:nth-child(odd) {
background: #fcfcfc;
/* TODO consider palettizing */
}
/* and for row headers */
table.inflection tr:nth-child(odd):not(.rowgroup) th {
background: #f6f6f6;
/* TODO consider palettizing */
}
/* dark mode background colors for table.inflection */
@media screen {
html.skin-theme-clientpref-night table.inflection {
background-color: #242424;
}
html.skin-theme-clientpref-night table.inflection tr:nth-child(odd) {
background-color: #2E2E2E;
}
html.skin-theme-clientpref-night table.inflection tr:nth-child(odd):not(.rowgroup) th {
background-color: #303030;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os table.inflection {
background-color: #242424;
}
html.skin-theme-clientpref-os table.inflection tr:nth-child(odd) {
background-color: #2E2E2E;
}
html.skin-theme-clientpref-os table.inflection tr:nth-child(odd):not(.rowgroup) th {
background-color: #303030;
}
}
table.inflection td,
table.inflection th {
padding: 0.33em 1em;
vertical-align: baseline;
}
/* but vertically center multi-row cells */
table.inflection td[rowspan] {
vertical-align: middle;
}
table.inflection td {
text-align: center;
}
/* regular row headers, in the left column */
table.inflection th {
background: var(--wikt-palette-paleblue, #f8f9fa);
text-align: left;
}
/* rowgroup header rows */
table.inflection tr.rowgroup th {
background: var(--wikt-palette-lightergrey, #eeeeee);
text-align: center;
}
/* first-of-several rowgroup header cells is aligned left */
table.inflection tr.rowgroup th:first-child {
text-align: left;
}
table.inflection tr.rowgroup th:first-child:only-child {
text-align: center;
}
/* rows with dividers above */
table.inflection tr.divider th {
border-top: 3px solid var(--wikt-palette-grey-7,#999999);
}
/* transliterations */
table.inflection .translit {
color: var(--wikt-palette-grey-8,#888);
}
/* header links aren’t coloured until hover/active */
table.inflection th a:link,
table.inflection th a:visited {
color: inherit;
text-decoration: inherit;
}
table.inflection th a:hover {
color: var(--wikt-palette-blue-9,#0645ad);
text-decoration: underline;
}
table.inflection th a:active {
color: var(--wikt-palette-gold,#faa700);
text-decoration: underline;
}
/* ----
end of Inflection tables */
/* Hide interproject links used by [[MediaWiki:Gadget-AggregateInterprojectLinks.js]] */
.interProject {
display: none;
clear: both;
border-top: 2px dotted var(--border-color-base,#AAAAAA);
margin-top: 2em;
}
/* Translation styling */
.checktrans,
.rfc-trans,
.trreq,
.ttbc {
background: var(--wikt-palette-palegreen, #f0fff0);
}
/* t+ t t- templates:
TODO are these still wanted? */
.tneg,
.tpos,
.tunk {
vertical-align: 20%;
font-size: 80%;
}
.tneg a {
color: var(--wikt-palette-red,#cc2200) !important;
}
/* monobook redlink colour, standard #ba0000 */
.tneg a:visited {
color: var(--wikt-palette-grey-red-9,#a55858) !important;
}
/* standard visited redlink colour */
.tpos a {
color: var(--wikt-palette-deepblue);
}
/* standard bluelink colour */
.tpos a:visited {
color: var(--wikt-palette-blue);
}
/* standard bluelink colour */
.tunk a {
color: var(--wikt-palette-blue-9,#3366BB) !important;
}
/* standard external interwiki colour */
.tunk a:active {
color: var(--wikt-palette-blue-9,#3366BB) !important;
}
/* redundant */
/* for parentheses, we use .tlc and .tlcp, the latter has no definition here
(display:inline) */
.tlc {
display: none;
}
/* Main page fixes */
#interwiki-completelist {
font-weight: bold;
}
/* Try to make search results more obviously links */
.mw-special-Search #bodyContent li a {
font-weight: bold;
}
.mw-special-Search .searchmatch {
font-weight: normal;
color: var(--wikt-palette-black, #000000);
background-color: var(--wikt-palette-brightyellow, #FFEE77);
}
/* [[WT:FEED]] */
#p-feedback a {
cursor: pointer;
}
/* Old revisions */
#mw-revision-info {
font-size: 110%;
margin: 5px 0;
}
#mw-editingold {
font-size: 120%;
}
/* maintenance line */
.maintenance-line {
color: #777777;
/* TODO consider palettizing */
}
/* dark mode text color for maintenance line */
@media screen {
html.skin-theme-clientpref-night .maintenance-line {
color: #adb7bf;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .maintenance-line {
color: #adb7bf;
}
}
/* Night mode logo appearance, see [[Wiktionary:Grease pit/2025/January#Top left Wiktionary logo in dark mode]] */
@media screen {
html.skin-theme-clientpref-night img.mw-logo-icon {
color-scheme: light;
filter: invert(1) brightness(55%) contrast(250%) hue-rotate(180deg);
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os img.mw-logo-icon {
color-scheme: light;
filter: invert(1) brightness(55%) contrast(250%) hue-rotate(180deg);
}
}
/* Make Recent changes look nice again */
span.newpage,
span.minor,
span.searchmatch,
span.bot,
span.unpatrolled {
font-family: sans-serif !important;
}
/* Styling for the different kinds of collapsible boxes and lists */
/* NavFrames */
div.NavFrame,
details.NavFrame {
border: 1px solid var(--wikt-palette-grey, #9e9e9e);
text-align: center;
border-collapse: collapse;
font-size: 95%;
overflow: auto;
width: auto;
}
div.NavFrame>ul,
div.NavFrame>ol {
text-align: left;
}
div.NavFrame div.NavHead,
details.NavFrame summary.NavHead {
margin: 2px;
min-height: 1.6em;
font-weight: bold;
font-size: 100%;
text-align: left;
padding: 1px 5px 1px 10px;
background-color: var(--wikt-palette-dullcyan, #eaecf0);
}
div.NavFrame p {
font-size: 100%;
}
div.NavFrame div.NavContent,
details.NavFrame div.NavContent {
font-size: 100%;
border-top: 1px solid var(--wikt-palette-grey, #9e9e9e);
}
div.NavFrame div.NavContent p,
details.NavFrame div.NavContent p {
font-size: 100%;
}
/* Used by box-top/box-bottom */
.NavContent.boxcontent {
padding: 0.25em 0.5em;
}
.NavContent.boxcontent>p:last-child {
margin-bottom: 0.5em;
/* from 1em to avoid some extra asymmetric space */
}
/* Override default added margins which mess up the background */
.NavContent.derivedterms>p,
.NavContent.columns-bg>p {
margin: 0;
}
.NavContent.derivedterms>p,
.NavContent.derivedterms>ul,
.NavContent.columns-bg>p,
.NavContent.columns-bg>ul {
padding: 0.5em;
}
/* Collapsible term lists */
.list-switcher {
/* This is a hack so we can use getComputedStyle to find the
maximum permissible height value of a list-switcher.
It must be kept in sync with .list-switcher-collapsed `max-height` */
bottom: calc(3.2 * 1.6em);
/* for browsers that don't support `lh` units */
bottom: 3.2lh;
}
.list-switcher-wrapper,
:not(.list-switcher-wrapper)>.list-switcher {
contain: layout;
/* stop floating elements from messing with layout */
margin-top: 0.3em;
/* replaces margin lost on <ul> in following ruleset */
}
.list-switcher-wrapper+.list-switcher-wrapper {
margin-top: 0.6em;
}
.list-switcher>div>ul,
.list-switcher-wrapper>div>ul {
margin-top: 0;
/* needed to avoid jumps when collapsing/uncollapsing */
}
.list-switcher ul ul {
margin-top: 0.1em;
/* make margin above nested lists equal to margin at bottom of <li>s */
}
.list-switcher-collapsed {
/* If changing this value, change .list-switcher `bottom` above */
max-height: calc(3.2 * 1.6em);
/* for browsers that don't support `lh` units */
max-height: 3.2lh;
overflow-y: hidden;
transform: translateZ(0); /* Fix for iOS Safari overflow clipping */
}
.list-switcher-header {
background: var(--wikt-palette-grey-indigo-2);
font-weight: bold;
font-size: 95%;
padding: 0.2em 0.4em 0.1em 0.6em;
}
.list-switcher-header+.list-switcher .term-list,
.list-switcher-header+.term-list {
padding-top: 0.1em;
}
.list-switcher-edit {
margin-left: 1.5em;
font-size: 87.75%;
/* exactly match font size of [show more] button (in Chromium-based browsers at least) */
font-weight: normal;
}
.list-switcher-edit::before {
content: "[";
}
.list-switcher-edit::after {
content: "]";
}
.list-switcher-large-text {
/* Keep in sync with next ruleset */
bottom: calc(4 * 1.6em);
/* for browsers that don't support `lh` units */
bottom: 4lh;
}
.list-switcher-large-text.list-switcher-collapsed {
/* Keep in sync with previous ruleset */
max-height: calc(4 * 1.6em);
/* for browsers that don't support `lh` units */
max-height: 4lh;
}
.list-switcher-element {
text-align: center;
cursor: pointer;
background: var(--wikt-palette-grey-indigo-2);
border-bottom-left-radius: 0.3lh;
border-bottom-right-radius: 0.3lh;
}
.list-switcher-element .NavToggle {
float: none;
}
/* this is a legacy rule that only applies to {{rootsee}} and
{{script appendix}} - these need a fairly wide width */
.columns-bg:not(.term-list)>div>div>.CategoryTreeChildren,
.columns-bg:not(.term-list)>div>.CategoryTreeChildren {
-moz-column-width: 25em;
column-width: 25em;
}
/* Hidden quotes */
.client-js .ns-0 ol>li>ul {
display: none;
}
/* Hidden Quotes and *nyms toggle */
.HQToggle,
.nyms-toggle,
.usex-toggle {
font-size: smaller;
position: relative;
bottom: 1px;
left: 5px;
}
.NavToggle {
float: right;
font-weight: normal;
font-size: smaller;
margin-top: 1px;
margin-left: 0.5em;
}
/* wrap toggle links with [ and ] */
.HQToggle:before,
.NavToggle:before,
.nyms-toggle:before,
.usex-toggle:before {
content: '[';
}
.HQToggle:after,
.NavToggle:after,
.nyms-toggle:after,
.usex-toggle:after {
content: ']';
}
.HQToggle a,
.NavToggle a,
.nyms-toggle a,
.usex-toggle a {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
color: var(--wikt-palette-black);
}
.HQToggle a:focus:not(:hover),
.NavToggle a:focus:not(:hover),
.nyms-toggle a:focus:not(:hover),
.usex-toggle a:focus:not(:hover) {
text-decoration: none;
}
.client-js .NavFrame:not(.no-collapse) .NavContent {
display: none;
}
/* View switcher */
.vsShow {
display: none;
}
/* End styling for collapsible lists */
/* Quote clarifications, e.g. {{quote-gloss}}, {{quote-qualifier}} */
.quote-note {
color: var(--wikt-palette-grey-9,#999);
}
/* Use same formatting for <mark> as used for bolding with certain scripts. */
mark {
background-color: var(--wikt-palette-pink, #ffe0f0);
color: var(--wikt-palette-black, #202122);
}
/* stuff visible when previewing an edit only */
.previewonly {
display: none;
}
#wikiPreview .previewonly {
display: inline;
}
/* Highlight the relevant sense when the user follows a link to a {{senseid}}.
The class="senseid" should ideally be on the enclosing <li> tag. We attempt to make the
highlight work when the class="senseid" is on a <span> inside the <li>, but the :has
selector was not supported by all browsers as of late 2023. This feature was nonetheless
requested by Benwing at [[Wiktionary:Grease_pit/2023/November#Why_does_Template%3Asenseid_use_HTML_li_tag%3F]].
THe version with :has is provided as a separate declaration to ensure maximum compatibility. */
.senseid:target {
background: var(--wikt-palette-lightblue, #DEF);
}
li:has(> span.senseid:target) {
background: var(--wikt-palette-lightblue, #DEF);
}
/* highlight the next paragraph for an etymid */
.etymid:target+p, .etymonid:target+p {
background: var(--wikt-palette-lightblue, #DEF);
}
/* Highlight selected source in a bibliography */
.bibliography-id:target {
background: var(--wikt-palette-lightblue, #DEF);
}
/* Skin CSS highlights table rows targeted by the current browser location
(tr:target). This rule highlight table rows containing target cells so that
there can be multiple IDs in a table row in [[Wiktionary:List of languages]]
and [[Wiktionary:List of languages/special#Etymology-only languages]]. */
tr:has(> :target),
tr:has(> td > code:target) {
background: var(--wikt-palette-lightblue, #DEF);
}
/* show the "serial and" in the disambiguation see also */
.serial-and {
display: inline;
}
/* hide the serial commas */
.serial-comma {
display: none;
}
/* Gender and number templates */
.gender,
.number,
.noun-class {
font-style: italic;
}
abbr {
border-bottom-width: 0;
}
/* hide "did you mean" on Noarticletext self-links */
.did-you-mean strong {
display: none;
}
/* make script/module errors smaller */
.scribunto-error {
font-size: 75%;
}
/* allow inline hieroglyphs */
.mw-hiero-outer {
display: inline-table !important;
/* override skin styling on mobile */
vertical-align: middle;
font-size: 10px;
}
/* allow mirrored custom hieroglyph (and other) images */
.mw-mirrored img {
transform: scaleX(-1);
}
/* softer highlighting of 'updated since last visit' */
span.updatedmarker {
background-color: var(--wikt-palette-mint, #c0e4c0);
}
/* {{TOC limit}} */
/* Allow limiting of which header levels are shown in a TOC;
<div class="toclimit-3">, for instance, will limit to showing ==headings==
and ===headings=== but no further (as long as there are no =headings= on the
page, which there shouldn't be according to the MoS). */
.toclimit-2 .toclevel-1 ul,
.toclimit-3 .toclevel-2 ul,
.toclimit-4 .toclevel-3 ul,
.toclimit-5 .toclevel-4 ul,
.toclimit-6 .toclevel-5 ul,
.toclimit-7 .toclevel-6 ul {
display: none;
}
/*
* Script and language-related styles are in [[MediaWiki:Gadget-LanguagesAndScripts.css]]
*/
/* A kludge that lets the members of [[:Category:Redirected combining characters]] be displayed. */
body.page-Category_Combining_characters div#mw-pages li,
body.page-Category_Redirected_combining_characters div#mw-pages li,
body.page-Category_IPA_combining_characters div#mw-pages li {
letter-spacing: 0.3em;
}
/* Grease Pit edit button fix */
.page-Wiktionary_Grease_pit.skin-vector #ca-edit,
.page-Wiktionary_Etymology_scriptorium.skin-vector #ca-edit,
.page-Wiktionary_Information_desk.skin-vector #ca-edit,
.page-Wiktionary_Beer_parlour.skin-vector #ca-edit,
.page-Wiktionary_Tea_room.skin-vector #ca-edit {
display: none;
}
/* unit tests filter */
table.unit-tests.unit-tests-hide-passing tr.unit-test-pass {
display: none;
}
table.unit-tests th.unit-tests-img-corner {
background: var(--wikt-palette-lightblue, #d9ebff);
min-width: 1em;
}
table.unit-tests.unit-tests-hide-passing th.unit-tests-img-corner {
background: red;
}
/* multi-column lists */
li,
.group-in-column {
-moz-column-break-inside: avoid;
break-inside: avoid;
}
/* {{shortcut}} */
.shortcut-box,
.category-edit-box {
border: 1px solid var(--border-color-base, #aaaaaa);
color: var(--color-base, black);
background: var(--wikt-palette-paleblue, #f9f9f9);
margin: 0 0 .5em 1em;
text-align: center;
padding: 5px;
float: right;
clear: both;
font-weight: bold;
font-size: smaller;
}
/* Display tabs with 4 spaces, see [[phab:T59824]] and [[phab:T59825]] */
textarea#wpTextbox1+div:last-child,
/* workaround for [[mw:User:Remember the dot/Syntax highlighter]] */
textarea,
pre,
code,
.mw-highlight {
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
}
/* stop double-bolding of self-links like in [[acquit]], [[fishbone]] */
/* old code: b .selflink, strong .selflink { font-weight: inherit; } */
b,
strong {
font-weight: bold;
}
tr:target {
background: var(--wikt-palette-lightblue, #DEF);
}
#firstHeading var,
.headword var {
color: var(--wikt-palette-grey-9, #888);
}
/* Display a {{tlb}} / {{term-label}} template on its own line if it comes right after a headword template.
It is too easy to miss after headwords (especially long ones). */
p .headword-line~.usage-label-term::before {
content: "\a";
/* newline */
white-space: pre;
}
/* Temporary kludge to be used because categoryTree is generating a newline. This CSS line should be removed after the developers fix it. */
.CategoryTreeTag+p br:first-child {
display: none;
}
/* Used by column templates (see [[Module:columns]]). */
.ul-column-count>ul,
.ul-column-count>ol,
.ul-column-count>dl {
-moz-column-gap: 20px;
column-gap: 20px;
}
.ul-column-count[data-column-count="2"]>ul,
.ul-column-count[data-column-count="2"]>ol,
.ul-column-count[data-column-count="2"]>dl {
-moz-column-count: 2;
column-count: 2;
}
.ul-column-count[data-column-count="3"]>ul,
.ul-column-count[data-column-count="3"]>ol,
.ul-column-count[data-column-count="3"]>dl {
-moz-column-count: 3;
column-count: 3;
}
.ul-column-count[data-column-count="4"]>ul,
.ul-column-count[data-column-count="4"]>ol,
.ul-column-count[data-column-count="4"]>dl {
-moz-column-count: 4;
column-count: 4;
}
.ul-column-count[data-column-count="5"]>ul,
.ul-column-count[data-column-count="5"]>ol,
.ul-column-count[data-column-count="5"]>dl {
-moz-column-count: 5;
column-count: 5;
}
.ul-column-count[data-column-count="6"]>ul,
.ul-column-count[data-column-count="6"]>ol,
.ul-column-count[data-column-count="6"]>dl {
-moz-column-count: 6;
column-count: 6;
}
.columns-bg {
/* Default background color for multi-column lists. */
background: var(--wikt-palette-lavender, #f8f8ff);
/* prevent text crashing into edge of box */
padding-right: 0.3em;
}
/* Default style for hypothetical inflections -- currently italic. */
.hypothetical {
font-style: italic;
}
/* Style for entries using deprecated templates */
.deprecated {
color: olivedrab;
}
.deprecated a {
color: darkgreen;
}
/* Style for entries using deprecated labels */
.deprecated-label {
color: olivedrab;
text-decoration: line-through;
}
.deprecated-label a {
color: darkgreen;
text-decoration: line-through;
}
/* Style for notes added using the bad= param in {{audio}} */
.bad-audio-note {
color: var(--wikt-palette-red, #FF2200);
font-weight: bold;
}
/* WikiHiero kludge */
/* disable for now, apparently not necessary and breaks wrapping
.wikt-hierokludge {
display: inline-block;
} */
/* Support lists that automatically split into limited-width columns (for translations tables and the like): */
.multicolumn-list>ul {
-moz-column-width: 25em;
column-width: 25em;
-moz-column-gap: 20px;
column-gap: 20px;
}
.multicolumn-list-wide>ul {
-moz-column-width: 55em;
column-width: 55em;
-moz-column-gap: 20px;
column-gap: 20px;
}
.multicolumn-list-narrow>ul {
-moz-column-width: 22em;
column-width: 22em;
-moz-column-gap: 20px;
column-gap: 20px;
}
/* Horizontal rules */
body.ns-0 .mw-parser-output> :not(.collapsible-block:not(.open-block))+h2:not(:first-of-type),
body.ns-0 .mw-parser-output> .mw-heading2 ~ :not(.collapsible-block:not(.open-block))+.mw-heading2 {
border-top: 1px solid var(--border-color-base, #a2a9b1);
padding-top: 1em;
margin-top: 0.2em;
}
/* [[phab:T156351]]: Support for Parsoid's Cite implementation */
a[ rel="mw:referencedBy"]::before {
content: " ^";
}
/* [[phab:T316670]]: fix header issues */
.mw-heading * {
word-break: normal;
}
/* From [[Module:shortcut box/styles.css]] */
a .redlink {
color: var(--color-destructive, #d73333);
}
a:visited .redlink {
color: var(--color-destructive--visited, #a55858);
}
a:visited:hover .redlink {
color: var(--color-destructive--visited, #a55858);
}
a:hover .redlink {
color: var(--color-destructive--hover, #ff4242);
text-decoration: underline;
}
a:active .redlink {
color: var(--color-destructive--active, #b32424);
text-decoration: underline;
}
a:focus .redlink {
outline-color: var(--outline-color-progressive--focus, #36c);
}
/* Change the external link icon to a PDF icon for all PDF files */
.mw-parser-output a[href$=".pdf"].external,
.mw-parser-output a[href*=".pdf?"].external,
.mw-parser-output a[href*=".pdf#"].external,
.mw-parser-output a[href$=".PDF"].external,
.mw-parser-output a[href*=".PDF?"].external,
.mw-parser-output a[href*=".PDF#"].external {
background: url("//upload.wikimedia.org/wikipedia/commons/4/4d/Icon_pdf_file.png") no-repeat right;
/* @noflip */
padding: 8px 18px 8px 0;
}
/* Add YouTube icon after links to YouTube. */
.mw-parser-output a[href*="youtube.com"].external,
.mw-parser-output a[href*="youtu.be"].external {
background: url("//upload.wikimedia.org/wikipedia/commons/f/fd/YouTube_full-color_icon_(2024).svg") no-repeat right;
background-size: auto;
padding: 8px 18px 8px 0;
background-size: 16px;
}
/* approximate replacement for .toccolours, which is not defined in the Vector 2022 skin */
.standard-box {
background-color: var(--wikt-palette-paleblue, #f8f9fa);
border: 1px solid var(--wikt-palette-grey, #9e9e9e);
/* was #aabbdd but no real equivalent in our palette */
padding: 5px;
}
/* fix for long titles e.g. [[Cnile]] */
.cited-source {
overflow-wrap: break-word;
}
/* </nowiki> */
cwtiynx3ccovf6knw72g4ojp9cyxz0c
88861
88860
2026-08-06T22:47:37Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-Site.css]]
88860
css
text/css
/* ----------
<nowiki>
CSS Style
* Indent with tab characters
* One line space between each declaration
* One selector per line
* Opening brace on the same line as the last selector
* Don't omit optional semicolons
* Closing brace on its own line
* Closing brace indented to match the preceding style declarations
* Alternatively, very short style rules may be all on one line
---------- */
/* csslint adjoining-classes:false */
/* See also [[MediaWiki:Minerva.css]], [[MediaWiki:Vector.css]] */
sup,
sub {
/* prevent odd line-height for raised and lowered characters */
line-height: 1em;
}
/* and keep links at normal font size/weight, else they inherit the header
attributes */
.editsection {
font-size: 100%;
font-weight: normal;
}
pre code,
code code {
border: none;
padding: 0;
}
pre var,
code var {
color: var(--wikt-palette-grey-9,#777);
}
/* This fixes a bug where certain Tibetan terms are cut off at the bottom... */
#firstHeading {
overflow: visible;
}
.ns-0 .timed-text {
display: none;
}
.client-js .audiometa {
display: none;
}
.client-nojs .audiofile {
display: none;
}
.nowrap {
white-space: nowrap;
}
/* wikitable is part of core. prettytable is deprecated but still used in some
places, until fully replaced: */
table.prettytable {
margin: 1em 1em 1em 0;
background: var(--wikt-palette-paleblue, #f8f9fa);
border: 1px var(--border-color-base, #aaaaaa) solid;
border-collapse: collapse;
}
table.prettytable th,
table.prettytable td {
border: 1px var(--border-color-base, #aaaaaa) solid;
padding: 0.2em;
}
table.prettytable th {
background: var(--wikt-palette-lightergrey, #eeeeee);
text-align: center;
}
table.prettytable caption {
margin-left: inherit;
margin-right: inherit;
}
/* default setting for {{qualifier}} and {{sense}}.
(Related classes that can be overridden: .qualifier-comma,
.qualifier-content, .ib-brac, .qualifier-brac, .sense-qualifier-colon.) */
.ib-comma {
font-style: normal;
}
.ib-content {
font-style: italic;
}
.ib-content i,
.ib-content em {
font-style: normal;
}
.context-qualifier-colon {
display: none;
}
/* default style for indented "see also" */
.disambig-see-also {
text-indent: 2em;
}
.mention-tr {
font-style: italic;
}
/* add left-to-right mark */
.mention-gloss-paren::before,
.mention-tr-paren::before {
content: "";
}
/* non-italic and non-bold gloss */
.mention-gloss,
.mention-gloss-double-quote,
.mention-gloss-paren,
.mention-tr-gloss-separator-comma,
.mention-tr-paren,
.mention-tr-gloss-separator-comma {
font-style: normal;
font-weight: inherit;
}
/* default style for "form of" definitions */
.use-with-mention,
.form-of-definition {
font-style: italic;
}
.use-with-mention i,
.form-of-definition-link {
font-style: normal;
}
.use-with-mention .mention,
.form-of-definition-link .mention {
font-style: normal;
font-weight: bold;
}
/* display normalization in {{ux}}/{{usex}}, {{quote}}, {{quote-*}} in italics */
.e-normalization {
font-style: italic;
}
/* Inflection tables
----------------------------- */
/* To colour links in inflection tables (currently done with
{{notred}}) black without using #ifexist and allowing for user
override */
.inflection-table a.new {
color: inherit;
}
/* fill a parent NavContent collapsing box */
.NavContent table.inflection,
.NavContent table.inflection-table {
margin-top: 0;
width: 100%;
display: table;
border: 0;
}
/* basic inflection table styles */
table.inflection {
border-collapse: collapse;
border: 1px solid;
background: #ffffff;
/* TODO consider palettizing */
}
table.inflection caption {
caption-side: bottom;
padding: 0.33em 1em;
text-align: left;
font-size: smaller;
}
/* subtle grey for alternate row backgrounds */
table.inflection tr:nth-child(odd) {
background: #fcfcfc;
/* TODO consider palettizing */
}
/* and for row headers */
table.inflection tr:nth-child(odd):not(.rowgroup) th {
background: #f6f6f6;
/* TODO consider palettizing */
}
/* dark mode background colors for table.inflection */
@media screen {
html.skin-theme-clientpref-night table.inflection {
background-color: #242424;
}
html.skin-theme-clientpref-night table.inflection tr:nth-child(odd) {
background-color: #2E2E2E;
}
html.skin-theme-clientpref-night table.inflection tr:nth-child(odd):not(.rowgroup) th {
background-color: #303030;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os table.inflection {
background-color: #242424;
}
html.skin-theme-clientpref-os table.inflection tr:nth-child(odd) {
background-color: #2E2E2E;
}
html.skin-theme-clientpref-os table.inflection tr:nth-child(odd):not(.rowgroup) th {
background-color: #303030;
}
}
table.inflection td,
table.inflection th {
padding: 0.33em 1em;
vertical-align: baseline;
}
/* but vertically center multi-row cells */
table.inflection td[rowspan] {
vertical-align: middle;
}
table.inflection td {
text-align: center;
}
/* regular row headers, in the left column */
table.inflection th {
background: var(--wikt-palette-paleblue, #f8f9fa);
text-align: left;
}
/* rowgroup header rows */
table.inflection tr.rowgroup th {
background: var(--wikt-palette-lightergrey, #eeeeee);
text-align: center;
}
/* first-of-several rowgroup header cells is aligned left */
table.inflection tr.rowgroup th:first-child {
text-align: left;
}
table.inflection tr.rowgroup th:first-child:only-child {
text-align: center;
}
/* rows with dividers above */
table.inflection tr.divider th {
border-top: 3px solid var(--wikt-palette-grey-7,#999999);
}
/* transliterations */
table.inflection .translit {
color: var(--wikt-palette-grey-8,#888);
}
/* header links aren’t coloured until hover/active */
table.inflection th a:link,
table.inflection th a:visited {
color: inherit;
text-decoration: inherit;
}
table.inflection th a:hover {
color: var(--wikt-palette-blue-9,#0645ad);
text-decoration: underline;
}
table.inflection th a:active {
color: var(--wikt-palette-gold,#faa700);
text-decoration: underline;
}
/* ----
end of Inflection tables */
/* Hide interproject links used by [[MediaWiki:Gadget-AggregateInterprojectLinks.js]] */
.interProject {
display: none;
clear: both;
border-top: 2px dotted var(--border-color-base,#AAAAAA);
margin-top: 2em;
}
/* Translation styling */
.checktrans,
.rfc-trans,
.trreq,
.ttbc {
background: var(--wikt-palette-palegreen, #f0fff0);
}
/* t+ t t- templates:
TODO are these still wanted? */
.tneg,
.tpos,
.tunk {
vertical-align: 20%;
font-size: 80%;
}
.tneg a {
color: var(--wikt-palette-red,#cc2200) !important;
}
/* monobook redlink colour, standard #ba0000 */
.tneg a:visited {
color: var(--wikt-palette-grey-red-9,#a55858) !important;
}
/* standard visited redlink colour */
.tpos a {
color: var(--wikt-palette-deepblue);
}
/* standard bluelink colour */
.tpos a:visited {
color: var(--wikt-palette-blue);
}
/* standard bluelink colour */
.tunk a {
color: var(--wikt-palette-blue-9,#3366BB) !important;
}
/* standard external interwiki colour */
.tunk a:active {
color: var(--wikt-palette-blue-9,#3366BB) !important;
}
/* redundant */
/* for parentheses, we use .tlc and .tlcp, the latter has no definition here
(display:inline) */
.tlc {
display: none;
}
/* Main page fixes */
#interwiki-completelist {
font-weight: bold;
}
/* Try to make search results more obviously links */
.mw-special-Search #bodyContent li a {
font-weight: bold;
}
.mw-special-Search .searchmatch {
font-weight: normal;
color: var(--wikt-palette-black, #000000);
background-color: var(--wikt-palette-brightyellow, #FFEE77);
}
/* [[WT:FEED]] */
#p-feedback a {
cursor: pointer;
}
/* Old revisions */
#mw-revision-info {
font-size: 110%;
margin: 5px 0;
}
#mw-editingold {
font-size: 120%;
}
/* maintenance line */
.maintenance-line {
color: #777777;
/* TODO consider palettizing */
}
/* dark mode text color for maintenance line */
@media screen {
html.skin-theme-clientpref-night .maintenance-line {
color: #adb7bf;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .maintenance-line {
color: #adb7bf;
}
}
/* Night mode logo appearance, see [[Wiktionary:Grease pit/2025/January#Top left Wiktionary logo in dark mode]] */
@media screen {
html.skin-theme-clientpref-night img.mw-logo-icon {
color-scheme: light;
filter: invert(1) brightness(55%) contrast(250%) hue-rotate(180deg);
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os img.mw-logo-icon {
color-scheme: light;
filter: invert(1) brightness(55%) contrast(250%) hue-rotate(180deg);
}
}
/* Make Recent changes look nice again */
span.newpage,
span.minor,
span.searchmatch,
span.bot,
span.unpatrolled {
font-family: sans-serif !important;
}
/* Styling for the different kinds of collapsible boxes and lists */
/* NavFrames */
div.NavFrame,
details.NavFrame {
border: 1px solid var(--wikt-palette-grey, #9e9e9e);
text-align: center;
border-collapse: collapse;
font-size: 95%;
overflow: auto;
width: auto;
}
div.NavFrame>ul,
div.NavFrame>ol {
text-align: left;
}
div.NavFrame div.NavHead,
details.NavFrame summary.NavHead {
margin: 2px;
min-height: 1.6em;
font-weight: bold;
font-size: 100%;
text-align: left;
padding: 1px 5px 1px 10px;
background-color: var(--wikt-palette-dullcyan, #eaecf0);
}
div.NavFrame p {
font-size: 100%;
}
div.NavFrame div.NavContent,
details.NavFrame div.NavContent {
font-size: 100%;
border-top: 1px solid var(--wikt-palette-grey, #9e9e9e);
}
div.NavFrame div.NavContent p,
details.NavFrame div.NavContent p {
font-size: 100%;
}
/* Used by box-top/box-bottom */
.NavContent.boxcontent {
padding: 0.25em 0.5em;
}
.NavContent.boxcontent>p:last-child {
margin-bottom: 0.5em;
/* from 1em to avoid some extra asymmetric space */
}
/* Override default added margins which mess up the background */
.NavContent.derivedterms>p,
.NavContent.columns-bg>p {
margin: 0;
}
.NavContent.derivedterms>p,
.NavContent.derivedterms>ul,
.NavContent.columns-bg>p,
.NavContent.columns-bg>ul {
padding: 0.5em;
}
/* Collapsible term lists */
.list-switcher {
/* This is a hack so we can use getComputedStyle to find the
maximum permissible height value of a list-switcher.
It must be kept in sync with .list-switcher-collapsed `max-height` */
bottom: calc(3.2 * 1.6em);
/* for browsers that don't support `lh` units */
bottom: 3.2lh;
}
.list-switcher-wrapper,
:not(.list-switcher-wrapper)>.list-switcher {
contain: layout;
/* stop floating elements from messing with layout */
margin-top: 0.3em;
/* replaces margin lost on <ul> in following ruleset */
}
.list-switcher-wrapper+.list-switcher-wrapper {
margin-top: 0.6em;
}
.list-switcher>div>ul,
.list-switcher-wrapper>div>ul {
margin-top: 0;
/* needed to avoid jumps when collapsing/uncollapsing */
}
.list-switcher ul ul {
margin-top: 0.1em;
/* make margin above nested lists equal to margin at bottom of <li>s */
}
.list-switcher-collapsed {
/* If changing this value, change .list-switcher `bottom` above */
max-height: calc(3.2 * 1.6em);
/* for browsers that don't support `lh` units */
max-height: 3.2lh;
overflow-y: hidden;
transform: translateZ(0); /* Fix for iOS Safari overflow clipping */
}
.list-switcher-header {
background: var(--wikt-palette-grey-indigo-2);
font-weight: bold;
font-size: 95%;
padding: 0.2em 0.4em 0.1em 0.6em;
}
.list-switcher-header+.list-switcher .term-list,
.list-switcher-header+.term-list {
padding-top: 0.1em;
}
.list-switcher-edit {
margin-left: 1.5em;
font-size: 87.75%;
/* exactly match font size of [show more] button (in Chromium-based browsers at least) */
font-weight: normal;
}
.list-switcher-edit::before {
content: "[";
}
.list-switcher-edit::after {
content: "]";
}
.list-switcher-large-text {
/* Keep in sync with next ruleset */
bottom: calc(4 * 1.6em);
/* for browsers that don't support `lh` units */
bottom: 4lh;
}
.list-switcher-large-text.list-switcher-collapsed {
/* Keep in sync with previous ruleset */
max-height: calc(4 * 1.6em);
/* for browsers that don't support `lh` units */
max-height: 4lh;
}
.list-switcher-element {
text-align: center;
cursor: pointer;
background: var(--wikt-palette-grey-indigo-2);
border-bottom-left-radius: 0.3lh;
border-bottom-right-radius: 0.3lh;
}
.list-switcher-element .NavToggle {
float: none;
}
/* this is a legacy rule that only applies to {{rootsee}} and
{{script appendix}} - these need a fairly wide width */
.columns-bg:not(.term-list)>div>div>.CategoryTreeChildren,
.columns-bg:not(.term-list)>div>.CategoryTreeChildren {
-moz-column-width: 25em;
column-width: 25em;
}
/* Hidden quotes */
.client-js .ns-0 ol>li>ul {
display: none;
}
/* Hidden Quotes and *nyms toggle */
.HQToggle,
.nyms-toggle,
.usex-toggle {
font-size: smaller;
position: relative;
bottom: 1px;
left: 5px;
}
.NavToggle {
float: right;
font-weight: normal;
font-size: smaller;
margin-top: 1px;
margin-left: 0.5em;
}
/* wrap toggle links with [ and ] */
.HQToggle:before,
.NavToggle:before,
.nyms-toggle:before,
.usex-toggle:before {
content: '[';
}
.HQToggle:after,
.NavToggle:after,
.nyms-toggle:after,
.usex-toggle:after {
content: ']';
}
.HQToggle a,
.NavToggle a,
.nyms-toggle a,
.usex-toggle a {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
color: var(--wikt-palette-black);
}
.HQToggle a:focus:not(:hover),
.NavToggle a:focus:not(:hover),
.nyms-toggle a:focus:not(:hover),
.usex-toggle a:focus:not(:hover) {
text-decoration: none;
}
.client-js .NavFrame:not(.no-collapse) .NavContent {
display: none;
}
/* View switcher */
.vsShow {
display: none;
}
/* End styling for collapsible lists */
/* Quote clarifications, e.g. {{quote-gloss}}, {{quote-qualifier}} */
.quote-note {
color: var(--wikt-palette-grey-9,#999);
}
/* Use same formatting for <mark> as used for bolding with certain scripts. */
mark {
background-color: var(--wikt-palette-pink, #ffe0f0);
color: var(--wikt-palette-black, #202122);
}
/* stuff visible when previewing an edit only */
.previewonly {
display: none;
}
#wikiPreview .previewonly {
display: inline;
}
/* Highlight the relevant sense when the user follows a link to a {{senseid}}.
The class="senseid" should ideally be on the enclosing <li> tag. We attempt to make the
highlight work when the class="senseid" is on a <span> inside the <li>, but the :has
selector was not supported by all browsers as of late 2023. This feature was nonetheless
requested by Benwing at [[Wiktionary:Grease_pit/2023/November#Why_does_Template%3Asenseid_use_HTML_li_tag%3F]].
THe version with :has is provided as a separate declaration to ensure maximum compatibility. */
.senseid:target {
background: var(--wikt-palette-lightblue, #DEF);
}
li:has(> span.senseid:target) {
background: var(--wikt-palette-lightblue, #DEF);
}
/* highlight the next paragraph for an etymid */
.etymid:target+p, .etymonid:target+p {
background: var(--wikt-palette-lightblue, #DEF);
}
/* Highlight selected source in a bibliography */
.bibliography-id:target {
background: var(--wikt-palette-lightblue, #DEF);
}
/* Skin CSS highlights table rows targeted by the current browser location
(tr:target). This rule highlight table rows containing target cells so that
there can be multiple IDs in a table row in [[Wiktionary:List of languages]]
and [[Wiktionary:List of languages/special#Etymology-only languages]]. */
tr:has(> :target),
tr:has(> td > code:target) {
background: var(--wikt-palette-lightblue, #DEF);
}
/* show the "serial and" in the disambiguation see also */
.serial-and {
display: inline;
}
/* hide the serial commas */
.serial-comma {
display: none;
}
/* Gender and number templates */
.gender,
.number,
.noun-class {
font-style: italic;
}
abbr {
border-bottom-width: 0;
}
/* hide "did you mean" on Noarticletext self-links */
.did-you-mean strong {
display: none;
}
/* make script/module errors smaller */
.scribunto-error {
font-size: 75%;
}
/* allow inline hieroglyphs */
.mw-hiero-outer {
display: inline-table !important;
/* override skin styling on mobile */
vertical-align: middle;
font-size: 10px;
}
/* allow mirrored custom hieroglyph (and other) images */
.mw-mirrored img {
transform: scaleX(-1);
}
/* softer highlighting of 'updated since last visit' */
span.updatedmarker {
background-color: var(--wikt-palette-mint, #c0e4c0);
}
/* {{TOC limit}} */
/* Allow limiting of which header levels are shown in a TOC;
<div class="toclimit-3">, for instance, will limit to showing ==headings==
and ===headings=== but no further (as long as there are no =headings= on the
page, which there shouldn't be according to the MoS). */
.toclimit-2 .toclevel-1 ul,
.toclimit-3 .toclevel-2 ul,
.toclimit-4 .toclevel-3 ul,
.toclimit-5 .toclevel-4 ul,
.toclimit-6 .toclevel-5 ul,
.toclimit-7 .toclevel-6 ul {
display: none;
}
/*
* Script and language-related styles are in [[MediaWiki:Gadget-LanguagesAndScripts.css]]
*/
/* A kludge that lets the members of [[:Category:Redirected combining characters]] be displayed. */
body.page-Category_Combining_characters div#mw-pages li,
body.page-Category_Redirected_combining_characters div#mw-pages li,
body.page-Category_IPA_combining_characters div#mw-pages li {
letter-spacing: 0.3em;
}
/* Grease Pit edit button fix */
.page-Wiktionary_Grease_pit.skin-vector #ca-edit,
.page-Wiktionary_Etymology_scriptorium.skin-vector #ca-edit,
.page-Wiktionary_Information_desk.skin-vector #ca-edit,
.page-Wiktionary_Beer_parlour.skin-vector #ca-edit,
.page-Wiktionary_Tea_room.skin-vector #ca-edit {
display: none;
}
/* unit tests filter */
table.unit-tests.unit-tests-hide-passing tr.unit-test-pass {
display: none;
}
table.unit-tests th.unit-tests-img-corner {
background: var(--wikt-palette-lightblue, #d9ebff);
min-width: 1em;
}
table.unit-tests.unit-tests-hide-passing th.unit-tests-img-corner {
background: red;
}
/* multi-column lists */
li,
.group-in-column {
-moz-column-break-inside: avoid;
break-inside: avoid;
}
/* {{shortcut}} */
.shortcut-box,
.category-edit-box {
border: 1px solid var(--border-color-base, #aaaaaa);
color: var(--color-base, black);
background: var(--wikt-palette-paleblue, #f9f9f9);
margin: 0 0 .5em 1em;
text-align: center;
padding: 5px;
float: right;
clear: both;
font-weight: bold;
font-size: smaller;
}
/* Display tabs with 4 spaces, see [[phab:T59824]] and [[phab:T59825]] */
textarea#wpTextbox1+div:last-child,
/* workaround for [[mw:User:Remember the dot/Syntax highlighter]] */
textarea,
pre,
code,
.mw-highlight {
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
}
/* stop double-bolding of self-links like in [[acquit]], [[fishbone]] */
/* old code: b .selflink, strong .selflink { font-weight: inherit; } */
b,
strong {
font-weight: bold;
}
tr:target {
background: var(--wikt-palette-lightblue, #DEF);
}
#firstHeading var,
.headword var {
color: var(--wikt-palette-grey-9, #888);
}
/* Display a {{tlb}} / {{term-label}} template on its own line if it comes right after a headword template.
It is too easy to miss after headwords (especially long ones). */
p .headword-line~.usage-label-term::before {
content: "\a";
/* newline */
white-space: pre;
}
/* Temporary kludge to be used because categoryTree is generating a newline. This CSS line should be removed after the developers fix it. */
.CategoryTreeTag+p br:first-child {
display: none;
}
/* Used by column templates (see [[Module:columns]]). */
.ul-column-count>ul,
.ul-column-count>ol,
.ul-column-count>dl {
-moz-column-gap: 20px;
column-gap: 20px;
}
.ul-column-count[data-column-count="2"]>ul,
.ul-column-count[data-column-count="2"]>ol,
.ul-column-count[data-column-count="2"]>dl {
-moz-column-count: 2;
column-count: 2;
}
.ul-column-count[data-column-count="3"]>ul,
.ul-column-count[data-column-count="3"]>ol,
.ul-column-count[data-column-count="3"]>dl {
-moz-column-count: 3;
column-count: 3;
}
.ul-column-count[data-column-count="4"]>ul,
.ul-column-count[data-column-count="4"]>ol,
.ul-column-count[data-column-count="4"]>dl {
-moz-column-count: 4;
column-count: 4;
}
.ul-column-count[data-column-count="5"]>ul,
.ul-column-count[data-column-count="5"]>ol,
.ul-column-count[data-column-count="5"]>dl {
-moz-column-count: 5;
column-count: 5;
}
.ul-column-count[data-column-count="6"]>ul,
.ul-column-count[data-column-count="6"]>ol,
.ul-column-count[data-column-count="6"]>dl {
-moz-column-count: 6;
column-count: 6;
}
.columns-bg {
/* Default background color for multi-column lists. */
background: var(--wikt-palette-lavender, #f8f8ff);
/* prevent text crashing into edge of box */
padding-right: 0.3em;
}
/* Default style for hypothetical inflections -- currently italic. */
.hypothetical {
font-style: italic;
}
/* Style for entries using deprecated templates */
.deprecated {
color: olivedrab;
}
.deprecated a {
color: darkgreen;
}
/* Style for entries using deprecated labels */
.deprecated-label {
color: olivedrab;
text-decoration: line-through;
}
.deprecated-label a {
color: darkgreen;
text-decoration: line-through;
}
/* Style for notes added using the bad= param in {{audio}} */
.bad-audio-note {
color: var(--wikt-palette-red, #FF2200);
font-weight: bold;
}
/* WikiHiero kludge */
/* disable for now, apparently not necessary and breaks wrapping
.wikt-hierokludge {
display: inline-block;
} */
/* Support lists that automatically split into limited-width columns (for translations tables and the like): */
.multicolumn-list>ul {
-moz-column-width: 25em;
column-width: 25em;
-moz-column-gap: 20px;
column-gap: 20px;
}
.multicolumn-list-wide>ul {
-moz-column-width: 55em;
column-width: 55em;
-moz-column-gap: 20px;
column-gap: 20px;
}
.multicolumn-list-narrow>ul {
-moz-column-width: 22em;
column-width: 22em;
-moz-column-gap: 20px;
column-gap: 20px;
}
/* Horizontal rules */
body.ns-0 .mw-parser-output> :not(.collapsible-block:not(.open-block))+h2:not(:first-of-type),
body.ns-0 .mw-parser-output> .mw-heading2 ~ :not(.collapsible-block:not(.open-block))+.mw-heading2 {
border-top: 1px solid var(--border-color-base, #a2a9b1);
padding-top: 1em;
margin-top: 0.2em;
}
/* [[phab:T156351]]: Support for Parsoid's Cite implementation */
a[ rel="mw:referencedBy"]::before {
content: " ^";
}
/* [[phab:T316670]]: fix header issues */
.mw-heading * {
word-break: normal;
}
/* From [[Module:shortcut box/styles.css]] */
a .redlink {
color: var(--color-destructive, #d73333);
}
a:visited .redlink {
color: var(--color-destructive--visited, #a55858);
}
a:visited:hover .redlink {
color: var(--color-destructive--visited, #a55858);
}
a:hover .redlink {
color: var(--color-destructive--hover, #ff4242);
text-decoration: underline;
}
a:active .redlink {
color: var(--color-destructive--active, #b32424);
text-decoration: underline;
}
a:focus .redlink {
outline-color: var(--outline-color-progressive--focus, #36c);
}
/* Change the external link icon to a PDF icon for all PDF files */
.mw-parser-output a[href$=".pdf"].external,
.mw-parser-output a[href*=".pdf?"].external,
.mw-parser-output a[href*=".pdf#"].external,
.mw-parser-output a[href$=".PDF"].external,
.mw-parser-output a[href*=".PDF?"].external,
.mw-parser-output a[href*=".PDF#"].external {
background: url("//upload.wikimedia.org/wikipedia/commons/4/4d/Icon_pdf_file.png") no-repeat right;
/* @noflip */
padding: 8px 18px 8px 0;
}
/* Add YouTube icon after links to YouTube. */
.mw-parser-output a[href*="youtube.com"].external,
.mw-parser-output a[href*="youtu.be"].external {
background: url("//upload.wikimedia.org/wikipedia/commons/f/fd/YouTube_full-color_icon_(2024).svg") no-repeat right;
background-size: auto;
padding: 8px 18px 8px 0;
background-size: 16px;
}
/* approximate replacement for .toccolours, which is not defined in the Vector 2022 skin */
.standard-box {
background-color: var(--wikt-palette-paleblue, #f8f9fa);
border: 1px solid var(--wikt-palette-grey, #9e9e9e);
/* was #aabbdd but no real equivalent in our palette */
padding: 5px;
}
/* fix for long titles e.g. [[Cnile]] */
.cited-source {
overflow-wrap: break-word;
}
/* </nowiki> */
cwtiynx3ccovf6knw72g4ojp9cyxz0c
Teamplaid:shortcut
10
17930
88862
2024-09-15T09:46:13Z
en>Theknightwho
0
88862
wikitext
text/x-wiki
<includeonly>{{#invoke:shortcut box|shortcut_t}}</includeonly><noinclude>{{documentation}}</noinclude>
exsyrlhl8x8eep173m56lwf7p5s2adf
88863
88862
2026-08-06T22:50:17Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Template:shortcut]]
88862
wikitext
text/x-wiki
<includeonly>{{#invoke:shortcut box|shortcut_t}}</includeonly><noinclude>{{documentation}}</noinclude>
exsyrlhl8x8eep173m56lwf7p5s2adf
Teamplaid:gadgets pages
10
17931
88864
2026-03-14T03:36:05Z
en>ExcarnateSojourner
0
transclude documentation
88864
wikitext
text/x-wiki
{| style="width: 100%; text-align: center" class="wikitable"
|-
! colspan="2" | All about [[Wiktionary:Gadgets|gadgets]]
|-
! | For users
| [[Special:Gadgets]] · [[Special:Preferences]] · [[Wiktionary:Gadget preferences]] · [[Wiktionary:Preferences for users without an account]]
|-
! | For developers
| [[Special:GadgetUsage]] · [[MediaWiki:Gadgets-definition]] · [[mw:Extension:Gadgets]] · [[:Category:User JavaScript coder]]
|}<noinclude>{{documentation}}</noinclude>
s3t1569tehyk3ndg0tsc2kedrd7gwrn
88865
88864
2026-08-06T22:50:28Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Template:gadgets_pages]]
88864
wikitext
text/x-wiki
{| style="width: 100%; text-align: center" class="wikitable"
|-
! colspan="2" | All about [[Wiktionary:Gadgets|gadgets]]
|-
! | For users
| [[Special:Gadgets]] · [[Special:Preferences]] · [[Wiktionary:Gadget preferences]] · [[Wiktionary:Preferences for users without an account]]
|-
! | For developers
| [[Special:GadgetUsage]] · [[MediaWiki:Gadgets-definition]] · [[mw:Extension:Gadgets]] · [[:Category:User JavaScript coder]]
|}<noinclude>{{documentation}}</noinclude>
s3t1569tehyk3ndg0tsc2kedrd7gwrn
MediaWiki:Gadget-MonobookSmallerHeadings.css
8
17932
88866
2014-07-26T10:37:52Z
en>Kephir
0
moved from [[MediaWiki:Monobook.css]]
88866
css
text/css
/* headings a bit more balanced in size */
h1 {
font-size:160%;
font-weight:bold;
}
h2 {
font-size:140%;
font-weight:bold;
}
h3 {
font-size:130%;
}
h4 {
font-size:120%;
}
h5 {
font-size:110%;
}
h6 {
font-size:100%;
}
sc9io20biiqcj7j28lpmq4tf913rv08
88867
88866
2026-08-06T23:07:52Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-MonobookSmallerHeadings.css]]
88866
css
text/css
/* headings a bit more balanced in size */
h1 {
font-size:160%;
font-weight:bold;
}
h2 {
font-size:140%;
font-weight:bold;
}
h3 {
font-size:130%;
}
h4 {
font-size:120%;
}
h5 {
font-size:110%;
}
h6 {
font-size:100%;
}
sc9io20biiqcj7j28lpmq4tf913rv08
MediaWiki:Gadget-LanguagesAndScripts.css
8
17933
88868
2026-08-05T12:55:12Z
en>Catonif
0
remove Code2000 from .pjt-Latn, .Cyrl, .Cyrs, .Polyt and .enPR
88868
css
text/css
/*
Support for scripts. See [[Wiktionary:Scripts]].
*/
/* Default style for mentions outside of "form of" definitions.
See [[Wiktionary:Votes/2007-10/style for mentioned terms]] */
.mention {
font-style: italic;
}
.mention i, i .mention {
font-style: normal;
}
/* The following scripts do not support italic text, but do support bold text.
Note: we repeat the first class selector twice to increase CSS 'specificity'. */
.Aghb.Aghb, .Aghb *,
.Ahom.Ahom, .Ahom *,
.Armi.Armi, .Armi *,
.Armn.Armn, .Armn *,
.Avst.Avst, .Avst *,
.Bali.Bali, .Bali *,
.Bamu.Bamu, .Bamu *,
.Batk.Batk, .Batk *,
.Beng.Beng, .Beng *,
.as-Beng.as-Beng, .as-Beng *,
.Bopo.Bopo, .Bopo *,
.Brai.Brai, .Brai *,
.Bugi.Bugi, .Bugi *,
.Buhd.Buhd, .Buhd *,
.Cakm.Cakm, .Cakm *,
.Cans.Cans, .Cans *,
.Cari.Cari, .Cari *,
.Cham.Cham, .Cham *,
.Cher.Cher, .Cher *,
.Copt.Copt, .Copt *,
.Cprt.Cprt, .Cprt *,
.Cyrl.Cyrl, /* .Cyrl *, */
.Cyrs.Cyrs, .Cyrs *,
.Deva.Deva, .Deva *,
.Dsrt.Dsrt, .Dsrt *,
.Egyp.Egyp, .Egyp *,
.Ethi.Ethi, .Ethi *,
.Geok.Geok, .Geok *,
.Geor.Geor, .Geor *,
.Glag.Glag, .Glag *,
.Goth.Goth, .Goth *,
.Grek.Grek, /* .Grek *, */
.Gujr.Gujr, .Gujr *,
.Guru.Guru, .Guru *,
.Hang.Hang, .Hang *,
.Hani.Hani, .Hani *,
.Hano.Hano, .Hano *,
.Hans.Hans, .Hans *,
.Hant.Hant, .Hant *,
.Hira.Hira, .Hira *,
.Hluw.Hluw, .Hluw *,
.IPA.IPA, .IPA *,
.Ipach.Ipach, .Ipach *,
.IPAchar.IPAchar, .IPAchar *,
.Ital.Ital, .Ital *,
.Java.Java, .Java *,
.Jpan.Jpan, .Jpan *,
.Kali.Kali, .Kali *,
.Kana.Kana, .Kana *,
.Khar.Khar, .Khar *,
.Khmr.Khmr, .Khmr *,
.Knda.Knda, .Knda *,
.Kore.Kore, .Kore *,
.Kthi.Kthi, .Kthi *,
.Lana.Lana, .Lana *,
.Laoo.Laoo, .Laoo *,
.Lepc.Lepc, .Lepc *,
.Limb.Limb, .Limb *,
.Lisu.Lisu, .Lisu *,
.Lyci.Lyci, .Lyci *,
.Lydi.Lydi, .Lydi *,
.Mand.Mand, .Mand *,
.Merc.Merc, .Merc *,
.Mero.Mero, .Mero *,
.Mlym.Mlym, .Mlym *,
.Modi.Modi, .Modi *,
.Mtei.Mtei, .Mtei *,
.Music.Music, .Music *,
.musical.musical, .musical *,
.Narb.Narb, .Narb *,
.Nkoo.Nkoo, .Nkoo *,
.Olck.Olck, .Olck *,
.Orya.Orya, .Orya *,
.Osma.Osma, .Osma *,
.Phag.Phag, .Phag *,
.Phli.Phli, .Phli *,
.Phlv.Phlv, .Phlv *,
.Phnx.Phnx, .Phnx *,
.Plrd.Plrd, .Plrd *,
.Polyt.Polyt, .Polyt *,
.polytonic.polytonic, .polytonic *,
.Prti.Prti, .Prti *,
.Rjng.Rjng, .Rjng *,
.Samr.Samr, .Samr *,
.Sarb.Sarb, .Sarb *,
.Saur.Saur, .Saur *,
.Sgnw.Sgnw, .Sgnw *,
.Shaw.Shaw, .Shaw *,
.Shrd.Shrd, .Shrd *,
.Sinh.Sinh, .Sinh *,
.Sora.Sora, .Sora *,
.Sund.Sund, .Sund *,
.Sylo.Sylo, .Sylo *,
.Tagb.Tagb, .Tagb *,
.Tale.Tale, .Tale *,
.Talu.Talu, .Talu *,
.Taml.Taml, .Taml *,
.Tavt.Tavt, .Tavt *,
.Tayo.Tayo, .Tayo *,
.Telu.Telu, .Telu *,
.Tfng.Tfng, .Tfng *,
.Tglg.Tglg, .Tglg *,
.Thaa.Thaa, .Thaa *,
.Thai.Thai, .Thai *,
.Tibt.Tibt, .Tibt *,
.Ugar.Ugar, .Ugar *,
.Vaii.Vaii, .Vaii *,
.Xpeo.Xpeo, .Xpeo *,
.Yiii.Yiii, .Yiii *,
.Zmth.Zmth, .Zmth *,
.Zname.Zname, .Zname *,
.Zsym.Zsym, .Zsym * {
font-style: normal;
}
/* The following scripts do not support neither bold nor italic text.
Note: we repeat the first class selector twice to increase CSS 'specificity'. */
.Arab.Arab, .Arab *,
.fa-Arab.fa-Arab, .fa-Arab *,
.glk-Arab.glk-Arab, .glk-Arab *,
.kk-Arab.kk-Arab, .kk-Arab *,
.ks-Arab.ks-Arab, .ks-Arab *,
.ku-Arab.ku-Arab, .ku-Arab *,
.mzn-Arab.mzn-Arab, .mzn-Arab *,
.ota-Arab.ota-Arab, .ota-Arab *,
.pa-Arab.pa-Arab, .pa-Arab *,
.ps-Arab.ps-Arab, .ps-Arab *,
.sd-Arab.sd-Arab, .sd-Arab *,
.tt-Arab.tt-Arab, .tt-Arab *,
.ug-Arab.ug-Arab, .ug-Arab *,
.ur-Arab.ur-Arab, .ur-Arab *,
.ms-Arab.ms-Arab, .ms-Arab *,
.Brah.Brah, .Brah *,
.Elba.Elba, .Elba *,
.Hebr.Hebr, .Hebr *,
.Lina.Lina, .Lina *,
.Linb.Linb, .Linb *,
.Mani.Mani, .Mani *,
.Mymr.Mymr, .Mymr *,
.Mong.Mong, .Mong *,
.mnc-Mong.mnc-Mong, .mnc-Mong *,
.sjo-Mong.sjo-Mong, .sjo-Mong *,
.xwo-Mong.xwo-Mong, .xwo-Mong *,
.Ogam.Ogam, .Ogam *,
.Orkh.Orkh, .Orkh *,
.Ougr.Ougr, .Ougr *,
.Runr.Runr, .Runr *,
.Sogd.Sogd, .Sogd *,
.Syrc.Syrc, .Syrc *,
.Tang.Tang, .Tang *,
.Vith.Vith, .Vith *,
.Xsux.Xsux, .Xsux * {
font-style: normal;
font-weight: normal;
}
/* For scripts where bolding is not used or can be hard to see, the
quoted word is highlighted in pink. */
.Arab.e-example b, .Arab.e-quotation b, .Arab.e-normalization b,
.Brah.e-example b, .Brah.e-quotation b, .Brah.e-normalization b,
.Deva.e-example b, .Deva.e-quotation b, .Deva.e-normalization b,
.Elba.e-example b, .Elba.e-quotation b, .Elba.e-normalization b,
.Hebr.e-example b, .Hebr.e-quotation b, .Hebr.e-normalization b,
.Lina.e-example b, .Lina.e-quotation b, .Lina.e-normalization b,
.Linb.e-example b, .Linb.e-quotation b, .Linb.e-normalization b,
.Mani.e-example b, .Mani.e-quotation b, .Mani.e-normalization b,
.Mong.e-example b, .Mong.e-quotation b, .Mong.e-normalization b,
.Ogam.e-example b, .Ogam.e-quotation b, .Ogam.e-normalization b,
.Orkh.e-example b, .Orkh.e-quotation b, .Orkh.e-normalization b,
.Ougr.e-example b, .Ougr.e-quotation b, .Ougr.e-normalization b,
.Runr.e-example b, .Runr.e-quotation b, .Runr.e-normalization b,
.Sogd.e-example b, .Sogd.e-quotation b, .Sogd.e-normalization b,
.Syrc.e-example b, .Syrc.e-quotation b, .Syrc.e-normalization b,
.Ugar.e-example b, .Ugar.e-quotation b, .Ugar.e-normalization b,
.Vith.e-example b, .Vith.e-quotation b, .Vith.e-normalization b,
.Xpeo.e-example b, .Xpeo.e-quotation b, .Xpeo.e-normalization b,
.Xsux.e-example b, .Xsux.e-quotation b, .Xsux.e-normalization b {
background-color: var(--wikt-palette-pink, #ffe0f0);
font-size: inherit;
}
/* Latin (.Latn)
This is the default script, so it has no definitions. */
/* This is needed to overcome the "smartness" of certain web browsers */
.Latn:lang(ja),
.Latn:lang(ko),
.Latn:lang(zh) {
font-family: inherit;
}
/* Latin Fraktur */
.Latf {
font-family: UnifrakturMaguntia, UnifrakturCook, Unifraktur, sans-serif;
font-size: 125%;
}
/* Navajo */
.nv-Latn {
font-family: Calibri, 'Aboriginal Sans', 'DejaVu Sans', 'Arial Unicode MS', sans-serif !important;
}
/* Pitjantjatjara (ḻ ṉ ṟ ṯ and capitals) */
.pjt-Latn {
font-family: Arial, 'Microsoft Sans Serif', Tahoma, sans-serif;
}
/* Latin extended */
/* no styles for .Latnx, .Latinx, .unicode, .Unicode */
/* Adlam */
.Adlm {
font-family: 'Noto Sans Adlam', sans-serif;
}
/* Aghwan */
.Aghb {
font-family: OptimaModoki, 'Noto Sans Caucasian Albanian', sans-serif;
}
/* Ahom */
.Ahom {
font-family: AhomUnicode, 'Noto Serif Ahom', 'Unifont Upper', sans-serif;
}
/* Arabic */
.Arab,
.fa-Arab,
.glk-Arab,
.kk-Arab,
.ks-Arab,
.ku-Arab,
.mzn-Arab,
.ota-Arab,
.pa-Arab,
.ps-Arab,
.sd-Arab,
.tt-Arab,
.ug-Arab,
.ur-Arab,
.ms-Arab {
font-family: 'Noto Naskh Arabic', 'Iranian Sans', Tahoma, 'Microsoft Sans Serif', 'Arial Unicode MS', sans-serif;
font-size: 133%;
direction: rtl;
unicode-bidi: isolate;
}
/* Persian */
.fa-Arab {
font-family: 'Iranian Sans', 'Geeza Pro', Tahoma, sans-serif;
}
/* Kashmiri */
.ks-Arab {
font-family: 'Noto Naskh Arabic', Amiri, Mirza, 'Scheherazade New', Scheherazade, 'Geeza Pro', Tahoma, sans-serif;
}
/* Kurdish */
.ku-Arab {
font-family: 'Noto Naskh Arabic', Tahoma, 'Arial Unicode MS', 'UT Cairo', 'UT Naskh', sans-serif;
}
/* Punjabi Shahmukhi, Urdu */
.pa-Arab,
.ur-Arab {
font-family: 'Noto Nastaliq Urdu', Tahoma, 'Arial Unicode MS', 'UT Cairo', 'UT Naskh', sans-serif;
}
/* Pashto */
.ps-Arab {
font-family: Pokhto, 'Pashto Kror Asiatype', 'Scheherazade New', Scheherazade, sans-serif;
}
/* Sindhi */
.sd-Arab {
font-family: 'MB Sindhi', Tahoma, 'MBSarang Sattar', 'MB Bhitai Sattar', 'MB Lateefi', 'Ayaz Gul', sans-serif;
}
/* Uyghur */
.ug-Arab {
font-family: 'UKIJ Tuz', 'Microsoft Uighur', 'Scheherazade New', Scheherazade, sans-serif;
}
/* Imperial Aramaic */
.Armi {
font-family: 'Segoe UI Historic', 'Aramaic Imperial Yeb', 'Noto Sans Imperial Aramaic', sans-serif;
direction: rtl;
unicode-bidi: isolate;
}
/* Armenian */
.Armn {
font-family: Mshtakan, Arial, 'Segoe UI', Tahoma, 'Arian AMU', 'DejaVu Sans', sans-serif;
}
/* Avestan */
.Avst {
font-family: Avestan, Ahuramzda, 'Noto Sans Avestan', sans-serif;
font-size: 125%;
direction: rtl;
unicode-bidi: isolate;
font-variant-ligatures: none; /* Avestan language */
}
.Avst:lang(pal) { /* Pazend (Middle Persian in Avestan script) */
font-variant-ligatures: normal;
}
/* Balinese */
.Bali {
font-family: 'Noto Sans Balinese', sans-serif;
}
/* Bamum */
.Bamu {
font-family: 'Noto Sans Bamum', sans-serif;
}
/* Bassa */
.Bass {
font-family: 'Noto Sans Bassa Vah', sans-serif;
}
/* Batak */
.Batk {
font-family: Batak-Unicode, 'Noto Sans Batak', sans-serif;
}
/* Bengali, Assamese */
.Beng,
.as-Beng {
font-family: 'Bangla Sangam MN', UniBangla, 'Code2000', Likhan, 'UT Bengali Dhaka', Vrinda, 'Noto Sans Bengali', sans-serif;
font-size: 130%;
}
/* Bhaiksuki */
.Bhks {
font-family: 'Noto Sans Bhaiksuki', sans-serif;
}
/* Zhuyin (Bopomofo) */
.Bopo {
font-family: MOESongUN, DFKai-SB, 'Microsoft Yahei', 'Microsoft Jhenghei', 'Source Han Sans TC', 'Source Han Sans TW', 'Noto Sans CJK TC', sans-serif;
}
/* Brahmi */
.Brah {
font-family: 'Segoe UI Historic', 'Noto Sans Brahmi', sans-serif;
}
/* Braille */
.Brai {
font-size: 150%;
}
/* Buginese */
.Bugi {
font-family: Saweri, 'Noto Sans Buginese', sans-serif;
}
/* Buhid [[Wiktionary:Grease pit/2025/February#Buhid]] */
.Buhd {
font-family: 'Noto Sans Buhid', Quivira, sans-serif;
font-size: 110%;
}
/* Chakma */
.Cakm {
font-family: RibengUni, 'Noto Sans Chakma', sans-serif;
}
/* Unified Canadian Aboriginal Syllabics */
.Cans {
font-family: 'Euphemia UCAS', Euphemia, 'Aboriginal Sans', OskiBlackfoot, 'Code2000', 'Everson Mono Unicode', 'Noto Sans Canadian Aboriginal', sans-serif;
font-size: 110%;
}
/* Carian */
.Cari {
font-family: 'Segoe UI Historic', Aegean, 'Noto Sans Carian', sans-serif;
font-size: 125%;
}
/* Cham */
.Cham {
font-family: 'Code2000', 'JG ChamCambodia', 'Noto Sans Cham', sans-serif;
font-size: 110%;
}
/* Cherokee */
.Cher {
font-family: Digohweli, 'Aboriginal Sans', 'Code2000', Marin, 'Rotinonhsonni Sans', 'Everson Mono Unicode', 'Noto Sans Cherokee', sans-serif;
font-size: 110%;
}
/* Khwarezmian */
.Chrs {
font-family: 'Noto Sans Chorasmian', sans-serif;
}
/* Coptic */
.Copt {
font-family: Quivira, Antinoou, 'New Athena Unicode', Analecta, FreeSerifAvvaShenouda, 'Arial Coptic', 'Sophia Nubian', 'Code2000', 'Noto Sans Coptic', sans-serif;
font-size: 130%;
}
/* Cypro-Minoan */
.Cpmn {
font-family: 'Noto Sans Cypro Minoan', sans-serif;
}
/* Cypriot */
.Cprt {
font-family: 'Segoe UI Historic', Aegean, 'Noto Sans Cypriot', sans-serif;
font-size: 115%;
direction: rtl;
unicode-bidi: isolate;
}
/* Cyrillic */
.Cyrl {
font-family: Helvetica, Geneva, 'Arial Unicode MS', 'Lucida Sans Unicode', sans-serif;
}
/* Old Cyrillic (Old Church Slavonic, Old East Slavic) */
.Cyrs {
font-family: Monomakh, 'Monomakh Unicode', 'Monomakh Unicode TT', Menaion, 'Menaion Unicode', 'Menaion Unicode TT', Ponomar, 'Ponomar Unicode', 'Ponomar Unicode TT', Fedorovsk, 'Fedorovsk Unicode', 'Fedorovsk Unicode TT', Pochaevsk, 'Pochaevsk Unicode', 'Pochaevsk Unicode TT', Triodion, 'Triodion Unicode', 'Triodion Unicode TT', 'Acathist', 'Shafarik', Vilnius, BukyVede, 'Kliment Std', 'RomanCyrillic Std', 'Monomachus', 'Old Standard', 'Old Standard TT', Dilyana, 'Hirmos Ponomar', 'Hirmos Ponomar TT', 'Menaion Medieval', Lazov, 'DejaVu Sans', 'Lucida Grande', 'Arial Unicode MS', 'Lucida Sans Unicode', sans-serif;
font-size: 125%;
}
/* Devanagari */
.Deva {
font-family: 'Tiro Devanagari Sanskrit', 'Sanskrit Text', 'Adobe Devanagari', 'Noto Serif Devanagari', Utsaah, 'Devanagari MT', Raghu, Gargi, JanaSanskrit, JanaHindi, Siddhanta, 'Noto Sans Devanagari', sans-serif;
font-size: 120%;
}
/* Dhives Akuru */
.Diak {
font-family: 'Noto Serif Dives Akuru', serif;
}
/* Dogra */
.Dogr {
font-family: 'Noto Serif Dogra', sans-serif;
}
/* Deseret */
.Dsrt {
font-family: 'Segoe UI Symbol', 'Code2001', 'MPH 2B Damase', 'Everson Mono', sans-serif;
}
/* Duployan */
.Dupl {
font-family: 'Noto Sans Duployan', sans-serif;
}
/* Demotic (Egyptian) */
.Egyd, .Egyd * {
font-style: normal;
direction: rtl;
}
/* Egyptian hieroglyphs */
.Egyp {
font-family: 'NewGardiner', 'Noto Sans Egyptian Hieroglyphs', Abydos, Aegyptus, 'Segoe UI Historic', sans-serif;
font-size: 150%;
}
/* Elbasan */
.Elba {
font-family: 'Noto Sans Elbasan', sans-serif;
}
/* Elymaic */
.Elym {
font-family: 'Noto Sans Elymaic', sans-serif;
}
/* Ethiopic (Ge'ez) */
.Ethi {
font-family: 'Abyssinica SIL', Nyala, 'Code2000', 'Ethiopia Jiret', 'GF Zemen Unicode', 'TITUS Cyberbit Basic', 'Visual Geez Unicode', 'Visual Geez Unicode Agazian', 'Visual Geez Unicode Title', 'Noto Sans Ethiopic', sans-serif;
font-size: 120%;
}
/* Georgian */
.Geor {
font-family: 'DejaVu Sans', 'Arial Unicode MS', Sylfaen, sans-serif;
}
/* Glagolitic */
.Glag {
font-family: Shafarik, Menaion, 'Menaion Unicode', 'Menaion Unicode TT', 'Segoe UI Historic', BukyVede, Dilyana, 'Noto Sans Glagolitic', sans-serif;
font-size: 150%;
}
/* Gunjala Gondi */
.Gong {
font-family: 'Noto Sans Gunjala Gondi', sans-serif;
}
/* Masaram Gondi */
.Gonm {
font-family: 'Noto Sans Masaram Gondi', sans-serif;
}
/* Gothic */
.Goth {
font-family: 'Segoe UI Historic', 'Code2001', Skeirs, 'MPH 2B Damase', 'Noto Sans Gothic', sans-serif;
}
b.Goth,
strong.Goth {
font-size: larger;
}
/* Grantha */
.Gran {
font-family: 'Noto Sans Grantha', sans-serif;
}
/* Greek (.Grek) removed - [[Wiktionary:Grease pit/2025/February#Sans-serif Greek font]] */
/* Albanian, Pontic and Tsakonian in Greek script */
.Grek:lang(sq),
.Grek:lang(pnt),
.Grek:lang(tsd) {
font-family: 'Gentium Plus', 'Gentium', 'Lucida Sans Unicode', sans-serif;
}
/* Polytonic Greek */
.polytonic, .Polyt {
font-family: 'SBL Greek', 'SBL BibLit', 'New Athena Unicode', 'DejaVu Sans', Athena, 'Gentium Plus', Gentium, 'Palatino Linotype', Menaion, Times, 'Arial Unicode MS', 'Lucida Sans Unicode', 'Lucida Grande', sans-serif;
}
/* Messapic */
.Polyt:lang(cms) {
font-family: 'Noto Serif', 'Lucida Sans Unicode', 'Gentium Plus', 'Gentium', 'Noto Sans', sans-serif;
}
/* Gujarati */
.Gujr {
font-family: 'Noto Sans Gujarati', 'Gujarati Sangam MN', 'Gujarati MT', Shruti, 'Lucida Grande', 'Arial Unicode MS', 'Lucida Sans Unicode', 'Code2000', 'TITUS Cyberbit Basic', 'Chrysanthi Unicode', 'Bitstream Cyberbit', 'Bitstream CyberBase', 'Bitstream Vera', 'Visual Geez Unicode', sans-serif;
font-size: 125%;
}
/* Gurmukhi */
.Guru {
font-family: 'Gurmukhi MN', 'Gurmukhi MT', 'UT Punjabi Amritsar', 'Lucida Grande', 'Arial Unicode MS', 'Lucida Sans Unicode', 'Code2000', 'TITUS Cyberbit Basic', 'Chrysanthi Unicode', 'Bitstream Cyberbit', 'Bitstream CyberBase', 'Bitstream Vera', 'Visual Geez Unicode', 'Noto Sans Gurmukhi', sans-serif;
font-size: 125%;
}
/* Chinese (Han)
.Hani = generic
.Hans = simplified
.Hant = traditional
*/
.Hans {
font-family: 'PingFang SC', DengXian, 'Source Han Sans SC', 'Source Han Sans CN', 'Noto Sans CJK SC', 'Microsoft Yahei', SimHei, SimSun, NSimSun, SimSun-ExtB, Song, 'Heiti SC', HanaMinA, HanaMinB, sans-serif;
}
.Hani,
.Hant {
font-family: 'PingFang TC', 'Source Han Sans TC', 'Source Han Sans TW', 'Noto Sans CJK TC', 'Microsoft Jhenghei', MOESongUN, PMingLiU, PMingLiU-ExtB, MingLiU, MingLiU-ExtB, Ming, 'Heiti TC', HanaMinA, HanaMinB, sans-serif;
}
.Hani,
.Hans,
.Hant {
font-size: 120%;
line-height: 1;
}
.Hani:lang(vi),
.Hani:lang(tyz) {
font-family: 'Nom Na Tong', 'HAN NOM A', 'HAN NOM B', Sun-ExtA, Sun-ExtB, Ming-Lt-HKSCS-UNI-H, Ming-Lt-HKSCS-ExtB, HanaMinA, HanaMinB, HanaMin, 'PingFang TC', MingLiU, MingLiU-ExtB, 'MingLiU_HKSCS', 'MingLiU_HKSCS-ExtB', SimSun, SimSun-ExtB, 'Arial Unicode MS', 'TITUS Cyberbit Basic', sans-serif;
/* CJK Unified Ideographs Extension C and Extension D (U+2A700..U+2B734, U+2B740..U+2B81F)
font-family: Sun-ExtB, 'MingLiU_HKSCS-ExtB', Ming-Lt-HKSCS-ExtB, HanaMinB, sans-serif;
**/
}
/* Hanunoo */
.Hano {
font-family: 'Noto Sans Hanunoo', Quivira, 'MPH 2B Damase', sans-serif;
font-size: 110%;
}
/* Hatran */
.Hatr {
font-family: 'Noto Sans Hatran', sans-serif;
direction: rtl;
}
/* Hebrew */
.Hebr {
font-family: 'SBL Hebrew', 'SBL BibLit', 'Taamey David CLM', 'Taamey Frank CLM', Alef, 'Noto Sans Hebrew', Narkisim, Miriam, Kinryu, 'Arial Hebrew', Arial, 'Adobe Hebrew', serif;
font-size: 133%;
direction: rtl;
unicode-bidi: isolate;
}
/* Hiragana: see .Jpan */
/* Anatolian Hieroglyphs */
.Hluw {
font-family: 'Noto Sans Anatolian Hieroglyphs', Anatolian, sans-serif;
font-size: 200%;
}
/* Pahawh Hmong */
.Hmng {
font-family: 'Noto Sans Pahawh Hmong', sans-serif;
}
/* Nyiakeng Puachue Hmong */
.Hmnp {
font-family: 'Noto Serif Nyiakeng Puachue Hmong', serif;
}
/* Old Hungarian */
.Hung {
font-family: 'Noto Sans Old Hungarian', sans-serif;
}
/* Italic */
.Ital {
font-family: 'Segoe UI Historic', Aegean, 'Noto Sans Old Italic', sans-serif;
font-size: 125%;
}
/* Javanese */
.Java {
font-family: 'Tuladha Jejeg', 'Javanese Text', adjisaka, 'Noto Sans Javanese', sans-serif;
}
/* Japanese scripts
.Hira = Hiragana
.Kana = Katakana (used for Ainu language)
.Jpan = Hiragana + Katakana + Kanji
*/
.Hira,
.Jpan,
.Kana {
font-family: 'Hiragino Kaku Gothic Pro', Osaka, 'Yu Gothic', Meiryo, 'Source Han Sans J', 'Source Han Sans JP', 'Noto Sans CJK JP', 'Droid Sans Japanese', 'MS PGothic', 'MS Gothic', 'MS PMincho', 'MS Mincho', HanaMinA, HanaMinB, sans-serif;
font-size: 120%;
line-height: 1;
}
.Jpan ruby rt {
font-family: 'Yu Gothic UI', 'Meiryo UI', 'MS UI Gothic', sans-serif;
font-size: 60%;
}
/* Kayah Li */
.Kali {
font-family: 'Code2000', 'Noto Sans Kayah Li', sans-serif;
}
/* Katakana: see .Jpan */
/* Kharoshthi */
.Khar {
font-family: 'Segoe UI Historic', 'MPH 2B Damase', 'Noto Sans Kharoshthi', sans-serif;
font-size: 110%;
direction: rtl;
unicode-bidi: isolate;
}
/* Khmer */
.Khmr {
font-family: 'Khmer OS', 'Khmer OS Content', 'Leelawadee UI', 'Noto Sans Khmer', 'Code2000', 'Khmer Mondulkiri U OT ls', sans-serif;
font-size: 125%;
}
/* Khojki */
.Khoj {
font-family: 'Noto Sans Khojki', sans-serif;
}
/* Khitan small script */
.Kits {
font-family: 'Noto Serif Khitan Small Script', serif;
}
/* Kannada */
.Knda {
font-family: 'Kannada Sangam MN', JanaKannada, Tunga, Kedage, RaghuKannada, Sampige, 'Arial Unicode MS', 'Code2000', 'Bitstream Cyberbit', 'Bitstream CyberBase', 'Noto Sans Kannada', sans-serif;
font-size: 125%;
}
/* Korean */
.Kore,
.Hang {
font-family: 'Apple SD Gothic Neo', 'Malgun Gothic', Dotum, Gulim, 'NanumBarunGothic YetHangul', NanumBarunGothic, UnDotum, 'Source Han Sans K', 'Source Han Sans KR', 'Noto Sans CJK KR', NanumGothic, 'NanumMyeongjo YetHangul', NanumMyeongjo, Batang, UnBatang, sans-serif;
font-size: 120%;
line-height: 1;
}
/* Kaithi */
.Kthi {
font-family: 'Noto Sans Kaithi', sans-serif;
}
/* Lanna */
.Lana {
font-family: 'Lanna Alif', 'Noto Sans Tai Tham', sans-serif;
}
/* Lao */
.Laoo {
font-family: 'Phetsarath OT', 'Saysettha OT', 'JG Basic Lao Opentype', 'JG Basic2 Lao Opentype', 'JG LaoTimes Opentype', 'Phagnoum Lao Unicode Opentype', 'JG Lao Old Arial Opentype', DokChampa, 'Code2000', 'JG Lao Classic Opentype', 'Alice0 Unicode', 'Alice1 Unicode', 'Alice2 Unicode', 'Alice3 Unicode', 'Alice4 Unicode', 'Alice5 Unicode', 'Noto Sans Lao', 'Noto Sans Lao Looped', sans-serif;
font-size: 125%;
}
/* Lepcha */
.Lepc {
font-family: 'Noto Sans Lepcha', Mingzat, sans-serif;
}
/* Limbu */
.Limb {
font-family: 'Code2000', 'MPH 2B Damase', 'Noto Sans Limbu', sans-serif;
}
/* Linear A */
.Lina {
font-family: Aegean, 'Noto Sans Linear A', sans-serif;
font-size: 125%;
}
/* Linear B */
.Linb {
font-family: Aegean, 'Noto Sans Linear B', sans-serif;
font-size: 125%;
}
/* Lisu */
.Lisu {
font-family: 'Noto Sans Lisu', Quivira, sans-serif;
}
/* Lycian */
.Lyci {
font-family: 'Segoe UI Historic', Aegean, 'Noto Sans Lycian', sans-serif;
font-size: 125%;
}
/* Lydian */
.Lydi {
font-family: 'Segoe UI Historic', Aegean, 'Noto Sans Lydian', sans-serif;
font-size: 125%;
}
/* Mahajani */
.Mahj {
font-family: 'Noto Sans Mahajani', sans-serif;
font-size: 125%;
}
/* Makasar */
.Maka {
font-family: 'Noto Serif Makasar', serif;
}
/* Mandaic */
.Mand {
font-family: 'Noto Sans Mandaic', sans-serif;
direction: rtl;
unicode-bidi: isolate;
}
/* Manichaean */
.Mani {
font-family: 'Noto Sans Manichaean', sans-serif;
font-size: 133%;
direction: rtl;
unicode-bidi: isolate;
}
/* Marchen */
.Marc {
font-family: 'Noto Sans Marchen', sans-serif;
}
/* Medefaidrin */
.Medf {
font-family: 'Noto Sans Medefaidrin', sans-serif;
}
/* Mende */
.Mend {
font-family: 'Noto Sans Mende Kikakui', sans-serif;
}
/* Meroitic cursive */
.Merc {
font-family: 'Segoe UI Historic', Aegyptus, 'Noto Sans Meroitic', sans-serif;
font-size: 150%;
}
/* Meroitic hieroglyphs */
.Mero {
font-family: Aegyptus, 'Noto Sans Meroitic', sans-serif;
font-size: 150%;
}
/* Malayalam */
.Mlym {
font-family: 'Malayalam Sangam MN', Kartika, 'Code2000', 'Lucida Grande', 'Arial Unicode MS', 'Lucida Sans Unicode', 'TITUS Cyberbit Basic', 'Chrysanthi Unicode', 'Bitstream Cyberbit', 'Bitstream CyberBase', 'Bitstream Vera', 'Visual Geez Unicode', 'Noto Sans Malayalam', sans-serif;
font-size: 125%;
}
/* Modi */
.Modi {
font-family: 'Noto Sans Modi', sans-serif;
}
/* Classical Mongolian, Manchu, Xibe, Clear Script */
.Mong,
.mnc-Mong, /* Manchu */
.sjo-Mong, /* Xibe */
.xwo-Mong /* Clear Script, Todo */ {
font-family: 'Mongolian Baiti', 'Noto Sans Mongolian', sans-serif;
font-size: 140%;
-webkit-writing-mode: vertical-lr;
-moz-writing-mode: vertical-lr;
writing-mode: vertical-lr;
}
/* Mro */
.Mroo {
font-family: 'Noto Sans Mro', sans-serif;
}
/* Meitei Mayek */
.Mtei {
font-family: 'Eeyek Unicode', 'Noto Sans Meetei Mayek', sans-serif;
}
/* Multani */
.Mult {
font-family: 'Noto Sans Multani', sans-serif;
}
/* Burmese */
.Mymr {
font-family: TharLon, Padauk, 'Myanmar Text', 'Myanmar3', 'Myanmar2', 'Myanmar1', ParabaikSans, MyMyanmar, 'Noto Sans Myanmar', sans-serif;
font-size: 130%;
}
big.Mymr,
strong.Mymr,
b.Mymr {
font-size: 162%;
}
.Mymr b {
font-size: 130%;
}
/* Nandinagari */
.Nand {
font-family: 'Noto Sans Nandinagari', sans-serif;
}
/* North Arabian */
.Narb {
font-family: 'Noto Sans Old North Arabian', sans-serif;
}
/* Newa */
.Newa {
font-family: 'Noto Sans Newa', sans-serif;
}
/* N'Ko */
.Nkoo {
font-family: "Noto Sans N'Ko", sans-serif;
}
/* Nüshu or Nushu */
.Nshu {
font-family: 'Noto Sans Nushu', 'Noto Traditional Nushu', 'Unicode Nushu', sans-serif;
font-size: 130%;
}
/* Ogham */
.Ogam {
font-family: 'Segoe UI Historic', Beth-Luis-Nion, Pollach, 'Maigh Nuad', 'Craobh Ruadh', 'Everson Mono Ogham', Cog, Crosta, 'TITUS Ogham', 'Ragnarok Ogham', 'Noto Sans Ogham', sans-serif;
font-size: 125%;
}
big.Ogam, strong.Ogam, b.Ogam, b .Ogam {
font-size: 156%;
}
.Ogam b {
font-size: 125%;
}
/* Ol Chiki */
.Olck {
font-family: 'Code2000', 'Noto Sans Ol Chiki', sans-serif;
}
/* Orkhon Runes */
.Orkh {
font-family: 'Segoe UI Historic', Orkun, 'Old Turkic', 'Noto Sans Old Turkic', sans-serif;
}
/* Odia */
.Orya {
font-family: 'Oriya Sangam MN', 'Noto Sans Oriya', sans-serif;
font-size: 125%;
}
/* Osage */
.Osge {
font-family: 'Noto Sans Osage', sans-serif;
}
/* Osmanya */
.Osma {
font-family: 'Noto Sans Osmanya', sans-serif;
}
/* Old Uyghur */
.Ougr {
font-family: 'Noto Serif Old Uyghur', serif;
font-size: 133%;
direction: rtl;
unicode-bidi: isolate;
}
/* Palmyrene */
.Palm {
font-family: 'Noto Sans Palmyrene', sans-serif;
}
/* Pau Cin Hau */
.Pauc {
font-family: 'Noto Sans Pau Cin Hau', sans-serif;
}
/* Old Permic */
.Perm {
font-family: 'Noto Sans Old Permic', sans-serif;
}
/* Phags-pa */
.Phag {
font-family: 'BabelStone Phags-pa Book', 'Microsoft PhagsPa', 'Noto Sans Phags-pa', sans-serif;
-webkit-writing-mode: vertical-lr;
-moz-writing-mode: vertical-lr;
writing-mode: vertical-lr;
layout-flow: vertical-ideographic;
}
/* Inscriptional Pahlavi */
.Phli {
font-family: 'Segoe UI Historic', 'Noto Sans Inscriptional Pahlavi', Shapour, 'ZH Mono', sans-serif;
direction: rtl;
unicode-bidi: isolate;
}
/* Psalter Pahlavi */
.Phlp {
font-family: 'Noto Sans Psalter Pahlavi', sans-serif;
}
/* Phoenician */
.Phnx {
font-family: 'Segoe UI Historic', 'ALPHABETUM Unicode', 'MPH 2B Damase', Aegean, 'Code2001', 'Free Sans', 'Noto Sans Phoenician', sans-serif;
font-size: 125%;
direction: rtl;
unicode-bidi: isolate;
}
/* Pollard */
.Plrd {
font-family: 'Noto Sans Miao', sans-serif;
}
/* Inscriptional Parthian */
.Prti {
font-family: 'Segoe UI Historic', 'Noto Sans Inscriptional Parthian', 'ZH Mono', sans-serif;
}
/* Rejang */
.Rjng {
font-family: 'Noto Sans Rejang', 'Code2000', sans-serif;
}
/* Hanifi Rohingya */
.Rohg {
font-family: 'Noto Sans Hanifi Rohingya', sans-serif;
}
/* Runic (Germanic) */
.Runr {
font-family: 'Segoe UI Historic', Junicode, 'Free Mono', 'Caslon Roman', 'Segoe UI Symbol', 'Code2000', 'Everson Mono', 'TITUS Cyberbit Basic', 'Noto Sans Runic', sans-serif;
font-size: 130%;
}
/* Samaritan */
.Samr {
font-family: 'Noto Sans Samaritan', Quivira, 'Everson Mono', Unifont, sans-serif;
font-size: 133%;
direction: rtl;
unicode-bidi: isolate;
}
/* South Arabian */
.Sarb {
font-family: 'Segoe UI Historic', Qataban, 'Noto Sans Old South Arabian', sans-serif;
}
/* Saurashtra */
.Saur {
font-family: Pagul, 'Noto Sans Saurashtra', sans-serif;
}
/* SignWriting */
.Sgnw {
font-family: 'Noto Sans SignWriting', sans-serif;
}
/* Shavian */
.Shaw {
font-family: 'Segoe UI Historic', 'Code2001', 'Everson Mono', 'Noto Sans Shavian', sans-serif;
}
/* Sharada */
.Shrd {
font-family: 'Noto Sans Sharada', sans-serif;
font-size: 125%;
}
/* Siddham */
.Sidd {
font-family: 'Noto Sans Siddham', sans-serif;
}
/* Sinhalese */
.Sinh {
font-family: 'Sinhala Sangam MN', KaputaUnicode, KandyUnicode, Dinamina, DinaminaUniWeb, Potha, Madhura, 'Noto Sans Sinhala', sans-serif;
font-size: 125%;
}
/* Sogdian */
.Sogd {
font-family: 'Noto Sans Sogdian', sans-serif;
}
/* Old Sogdian */
.Sogo {
font-family: 'Noto Sans Old Sogdian', sans-serif;
}
/* Sorang Sompeng */
.Sora {
font-family: 'Noto Sans Sora Sompeng', sans-serif;
}
/* Soyombo */
.Soyo {
font-family: 'Noto Sans Soyombo', sans-serif;
}
/* Sundanese */
.Sund {
font-family: 'Sundanese Unicode', 'Noto Sans Sundanese', sans-serif;
}
/* Sunuwar */
.Sunu {
font-family: 'Noto Sans Sunuwar', sans-serif;
}
/* Syloti Nagri */
.Sylo {
font-family: 'Noto Sans Syloti Nagri', sans-serif;
}
/* Syriac */
.Syrc {
font-family: 'Estrangelo Edessa', 'San Francisco', 'Code2000', 'Bitstream Cyberbit', 'Bitstream CyberBase', 'Bitstream Vera', 'TITUS Cyberbit Basic', sans-serif;
font-size: 130%;
direction: rtl;
unicode-bidi: isolate;
}
/* Serto or Western form of Syriac */
.Syrc:lang(tru),
.Syrc:lang(amw) {
font-family: 'Noto Sans Syriac Western', 'Serto Jerusalem', 'Noto Sans Syriac', sans-serif;
}
/* Madnhaya or Eastern form of Syriac */
.Syrc:lang(aii) {
font-family: 'Noto Sans Syriac Eastern', 'Noto Sans Syriac', sans-serif;
}
/* Tagbanwa */
.Tagb {
font-family: Quivira, Tagbanwa, 'Noto Sans Tagbanwa', sans-serif;
font-size: 110%;
}
/* Takri */
.Takr {
font-family: 'Noto Sans Takri', sans-serif;
}
/* Tai Le */
.Tale {
font-family: 'Microsoft Tai Le', 'Tai Le Valentinium', 'MPH 2B Damase', 'Noto Sans Tai Le', sans-serif;
}
/* New Tai Lue */
.Talu {
font-family: 'Noto Sans New Tai Lue', 'Microsoft New Tai Lue', 'Dai Banna SIL Book', 'Husb_New_Tai_Lue_WB', sans-serif;
}
/* Tamil */
.Taml {
font-family: 'Tamil Sangam MN', InaiMathi, Vijaya, Akshar, JanaTamil, 'Code2000', ETTamilNew, 'Lohit Tamil', 'Arial Unicode MS', 'Free Serif', Latha, 'Noto Sans Tamil', 'Noto Sans Tamil Supplement', sans-serif;
font-size: 125%;
}
/* Tangut */
.Tang {
font-family: 'BabelStone Tangut Wenhai', 'Tangut N4694', 'Tangut Yinchuan', 'New Tangut', 'New Tangut Std', 'Tangut TWU', 'Babelstone Tangut Wenhai', sans-serif;
font-size: 120%;
text-orientation: upright;
-webkit-text-orientation: upright
}
/* Tai Viet */
.Tavt {
font-family: 'Tai Heritage Pro', 'Noto Sans Tai Viet', sans-serif;
}
/* Lai Tay */
.Tayo {
-webkit-writing-mode: vertical-rl;
-moz-writing-mode: vertical-rl;
writing-mode: vertical-rl;
layout-flow: vertical-ideographic;
}
/* Telugu */
.Telu {
font-family: 'Telugu Sangam MN', 'Noto Sans Telugu', sans-serif;
font-size: 125%;
}
/* Tifinagh */
.Tfng {
font-family: 'Hapax Berbère', Ebrima, 'Code2000', DejaVu, 'Noto Sans Tifinagh', sans-serif;
font-size: 120%;
}
/* Baybayin */
.Tglg {
font-family: 'Noto Sans Tagalog', 'Tagalog Stylized', 'Baybayin Lopez', 'Tagalog Doctrina 1593', Quivira, sans-serif;
}
.Tglg:lang(bcl) {
font-family: 'Bikol Mintz', 'Noto Sans Tagalog', 'Tagalog Stylized', 'Baybayin Lopez', 'Tagalog Doctrina 1593', Quivira, sans-serif;
}
.Tglg:lang(ceb) {
font-family: 'Bisaya Hervas', 'Noto Sans Tagalog', 'Tagalog Stylized', 'Baybayin Lopez', 'Tagalog Doctrina 1593', Quivira, sans-serif;
}
/* Thaana */
.Thaa {
font-family: 'Noto Sans Thaana', sans-serif;
}
/* Thai */
.Thai {
font-family: 'Leelawadee UI', Leelawadee, 'Arial Unicode MS', 'Code2000', 'Noto Sans Thai', 'Noto Sans Thai Looped', sans-serif;
font-size: 125%;
}
/* Tibetan */
.Tibt {
font-family: 'Noto Serif Tibetan', 'Noto Sans Tibetan', Jomolhari-ID, 'Tibetan Machine Uni', 'Tibetan Machine Web', Jomolhari, 'Microsoft Himalaya', sans-serif;
font-size: 130%;
}
/* Tirhuta */
.Tirh {
font-family: 'Noto Sans Tirhuta', sans-serif;
}
/* Toto */
.Toto {
font-family: 'Noto Serif Toto', serif;
}
/* Ugaritic */
.Ugar {
font-family: 'Oxford Ugaritic', 'Segoe UI Historic', Aegean, 'Code2001', 'Noto Sans Ugaritic', sans-serif;
}
/* Vai */
.Vaii {
font-family: Ebrima, 'Code2000', 'Noto Sans Vai', sans-serif;
font-size: 110%;
}
/* Vithkuqi */
.Vith {
font-family: 'Noto Serif Vithkuqi', 'Noto Sans Vithkuqi', sans-serif;
}
/* Varang Kshiti */
.Wara {
font-family: 'Noto Sans Warang Citi', sans-serif;
}
/* Wancho */
.Wcho {
font-family: 'Noto Sans Wancho', sans-serif;
}
/* Old Persian cuneiform */
.Xpeo {
font-family: 'Segoe UI Historic', Aegean, Xerxes, 'Code2001', 'Noto Sans Old Persian', sans-serif;
}
/* Sumero-Akkadian cuneiform */
.Xsux {
font-family: Akkadian, FreeIdgSerif, CuneiformComposite, 'Segoe UI Historic', sans-serif; /* Neo-Assyrian form */
/* font-family: FreeIdgSerif, Akkadian, CuneiformComposite, 'Segoe UI Historic', sans-serif; */ /* Old Assyrian form */
/* font-family: Akkadian, CuneiformComposite, FreeIdgSerif, 'Segoe UI Historic', sans-serif; /* /* Neo-Sumerian form */
/* font-family: Akkadian, CuneiformComposite, FreeIdgSerif, 'Segoe UI Historic', sans-serif; /* /* Classical Sumerian form */
font-size: 125%;
}
/* Specify Hittite fonts */
.Xsux:lang(hit) {
font-family: UllikummiA, Akkadian, FreeIdgSerif, CuneiformComposite, 'Segoe UI Historic', sans-serif;
}
/* Specify Sumerian fonts */
.Xsux:lang(sux) {
font-family: CuneiformComposite, Akkadian, 'Segoe UI Historic', sans-serif;
}
/* Yezidi */
.Yezi {
font-family: 'Noto Serif Yezidi', serif;
}
/* Yi */
.Yiii {
font-family: 'Noto Sans Yi', sans-serif;
}
/* Zanabazar Square */
.Zanb {
font-family: 'Noto Sans Zanabazar Square', sans-serif;
}
/* Symbols */
.Zsym {
font-family: Symbola, sans-serif;
font-size: 150%;
}
/* Rumi numerals; see <https://en.wikipedia.org/wiki/Rumi_Numeral_Symbols> */
.Rumin, .Ruminumerals {
font-size: 120%;
}
/*
Multilingual writing systems and notations
*/
/* English Phonemic Representation (AHD) */
.enPR {
font-family: 'Arial Unicode MS', 'Lucida Grande', 'Gentium Plus', Gentium, 'Gentium Alternative', 'TITUS Cyberbit Basic', 'Lucida Sans Unicode', sans-serif;
}
/* International Phonetic Alphabet */
.IPA, .Ipach, .IPAchar {
font-family: 'Gentium Plus', Gentium, GentiumAlt, 'DejaVu Sans', 'Segoe UI', 'Lucida Grande', 'Charis SIL', 'Doulos SIL', 'TITUS Cyberbit Basic', 'Code2000', 'Lucida Sans Unicode', sans-serif;
font-size: 110%;
font-variant-ligatures: no-common-ligatures;
}
/* Musical notation */
.Music, .musical {
font-family: Musica, 'Musical Symbols', Euterpe, 'Noto Music', sans-serif;
font-size: 140%;
}
/* Znamenny musical notation */
.Zname {
font-family: 'Mezenets Unicode', Slavonic, Voskresensky, Smolensky, Symbola, 'Noto Znamenny Musical Notation', sans-serif;
}
ow6904ikl8aoe8vsjbhn9i0aobnyckr
88869
88868
2026-08-06T23:08:01Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-LanguagesAndScripts.css]]
88868
css
text/css
/*
Support for scripts. See [[Wiktionary:Scripts]].
*/
/* Default style for mentions outside of "form of" definitions.
See [[Wiktionary:Votes/2007-10/style for mentioned terms]] */
.mention {
font-style: italic;
}
.mention i, i .mention {
font-style: normal;
}
/* The following scripts do not support italic text, but do support bold text.
Note: we repeat the first class selector twice to increase CSS 'specificity'. */
.Aghb.Aghb, .Aghb *,
.Ahom.Ahom, .Ahom *,
.Armi.Armi, .Armi *,
.Armn.Armn, .Armn *,
.Avst.Avst, .Avst *,
.Bali.Bali, .Bali *,
.Bamu.Bamu, .Bamu *,
.Batk.Batk, .Batk *,
.Beng.Beng, .Beng *,
.as-Beng.as-Beng, .as-Beng *,
.Bopo.Bopo, .Bopo *,
.Brai.Brai, .Brai *,
.Bugi.Bugi, .Bugi *,
.Buhd.Buhd, .Buhd *,
.Cakm.Cakm, .Cakm *,
.Cans.Cans, .Cans *,
.Cari.Cari, .Cari *,
.Cham.Cham, .Cham *,
.Cher.Cher, .Cher *,
.Copt.Copt, .Copt *,
.Cprt.Cprt, .Cprt *,
.Cyrl.Cyrl, /* .Cyrl *, */
.Cyrs.Cyrs, .Cyrs *,
.Deva.Deva, .Deva *,
.Dsrt.Dsrt, .Dsrt *,
.Egyp.Egyp, .Egyp *,
.Ethi.Ethi, .Ethi *,
.Geok.Geok, .Geok *,
.Geor.Geor, .Geor *,
.Glag.Glag, .Glag *,
.Goth.Goth, .Goth *,
.Grek.Grek, /* .Grek *, */
.Gujr.Gujr, .Gujr *,
.Guru.Guru, .Guru *,
.Hang.Hang, .Hang *,
.Hani.Hani, .Hani *,
.Hano.Hano, .Hano *,
.Hans.Hans, .Hans *,
.Hant.Hant, .Hant *,
.Hira.Hira, .Hira *,
.Hluw.Hluw, .Hluw *,
.IPA.IPA, .IPA *,
.Ipach.Ipach, .Ipach *,
.IPAchar.IPAchar, .IPAchar *,
.Ital.Ital, .Ital *,
.Java.Java, .Java *,
.Jpan.Jpan, .Jpan *,
.Kali.Kali, .Kali *,
.Kana.Kana, .Kana *,
.Khar.Khar, .Khar *,
.Khmr.Khmr, .Khmr *,
.Knda.Knda, .Knda *,
.Kore.Kore, .Kore *,
.Kthi.Kthi, .Kthi *,
.Lana.Lana, .Lana *,
.Laoo.Laoo, .Laoo *,
.Lepc.Lepc, .Lepc *,
.Limb.Limb, .Limb *,
.Lisu.Lisu, .Lisu *,
.Lyci.Lyci, .Lyci *,
.Lydi.Lydi, .Lydi *,
.Mand.Mand, .Mand *,
.Merc.Merc, .Merc *,
.Mero.Mero, .Mero *,
.Mlym.Mlym, .Mlym *,
.Modi.Modi, .Modi *,
.Mtei.Mtei, .Mtei *,
.Music.Music, .Music *,
.musical.musical, .musical *,
.Narb.Narb, .Narb *,
.Nkoo.Nkoo, .Nkoo *,
.Olck.Olck, .Olck *,
.Orya.Orya, .Orya *,
.Osma.Osma, .Osma *,
.Phag.Phag, .Phag *,
.Phli.Phli, .Phli *,
.Phlv.Phlv, .Phlv *,
.Phnx.Phnx, .Phnx *,
.Plrd.Plrd, .Plrd *,
.Polyt.Polyt, .Polyt *,
.polytonic.polytonic, .polytonic *,
.Prti.Prti, .Prti *,
.Rjng.Rjng, .Rjng *,
.Samr.Samr, .Samr *,
.Sarb.Sarb, .Sarb *,
.Saur.Saur, .Saur *,
.Sgnw.Sgnw, .Sgnw *,
.Shaw.Shaw, .Shaw *,
.Shrd.Shrd, .Shrd *,
.Sinh.Sinh, .Sinh *,
.Sora.Sora, .Sora *,
.Sund.Sund, .Sund *,
.Sylo.Sylo, .Sylo *,
.Tagb.Tagb, .Tagb *,
.Tale.Tale, .Tale *,
.Talu.Talu, .Talu *,
.Taml.Taml, .Taml *,
.Tavt.Tavt, .Tavt *,
.Tayo.Tayo, .Tayo *,
.Telu.Telu, .Telu *,
.Tfng.Tfng, .Tfng *,
.Tglg.Tglg, .Tglg *,
.Thaa.Thaa, .Thaa *,
.Thai.Thai, .Thai *,
.Tibt.Tibt, .Tibt *,
.Ugar.Ugar, .Ugar *,
.Vaii.Vaii, .Vaii *,
.Xpeo.Xpeo, .Xpeo *,
.Yiii.Yiii, .Yiii *,
.Zmth.Zmth, .Zmth *,
.Zname.Zname, .Zname *,
.Zsym.Zsym, .Zsym * {
font-style: normal;
}
/* The following scripts do not support neither bold nor italic text.
Note: we repeat the first class selector twice to increase CSS 'specificity'. */
.Arab.Arab, .Arab *,
.fa-Arab.fa-Arab, .fa-Arab *,
.glk-Arab.glk-Arab, .glk-Arab *,
.kk-Arab.kk-Arab, .kk-Arab *,
.ks-Arab.ks-Arab, .ks-Arab *,
.ku-Arab.ku-Arab, .ku-Arab *,
.mzn-Arab.mzn-Arab, .mzn-Arab *,
.ota-Arab.ota-Arab, .ota-Arab *,
.pa-Arab.pa-Arab, .pa-Arab *,
.ps-Arab.ps-Arab, .ps-Arab *,
.sd-Arab.sd-Arab, .sd-Arab *,
.tt-Arab.tt-Arab, .tt-Arab *,
.ug-Arab.ug-Arab, .ug-Arab *,
.ur-Arab.ur-Arab, .ur-Arab *,
.ms-Arab.ms-Arab, .ms-Arab *,
.Brah.Brah, .Brah *,
.Elba.Elba, .Elba *,
.Hebr.Hebr, .Hebr *,
.Lina.Lina, .Lina *,
.Linb.Linb, .Linb *,
.Mani.Mani, .Mani *,
.Mymr.Mymr, .Mymr *,
.Mong.Mong, .Mong *,
.mnc-Mong.mnc-Mong, .mnc-Mong *,
.sjo-Mong.sjo-Mong, .sjo-Mong *,
.xwo-Mong.xwo-Mong, .xwo-Mong *,
.Ogam.Ogam, .Ogam *,
.Orkh.Orkh, .Orkh *,
.Ougr.Ougr, .Ougr *,
.Runr.Runr, .Runr *,
.Sogd.Sogd, .Sogd *,
.Syrc.Syrc, .Syrc *,
.Tang.Tang, .Tang *,
.Vith.Vith, .Vith *,
.Xsux.Xsux, .Xsux * {
font-style: normal;
font-weight: normal;
}
/* For scripts where bolding is not used or can be hard to see, the
quoted word is highlighted in pink. */
.Arab.e-example b, .Arab.e-quotation b, .Arab.e-normalization b,
.Brah.e-example b, .Brah.e-quotation b, .Brah.e-normalization b,
.Deva.e-example b, .Deva.e-quotation b, .Deva.e-normalization b,
.Elba.e-example b, .Elba.e-quotation b, .Elba.e-normalization b,
.Hebr.e-example b, .Hebr.e-quotation b, .Hebr.e-normalization b,
.Lina.e-example b, .Lina.e-quotation b, .Lina.e-normalization b,
.Linb.e-example b, .Linb.e-quotation b, .Linb.e-normalization b,
.Mani.e-example b, .Mani.e-quotation b, .Mani.e-normalization b,
.Mong.e-example b, .Mong.e-quotation b, .Mong.e-normalization b,
.Ogam.e-example b, .Ogam.e-quotation b, .Ogam.e-normalization b,
.Orkh.e-example b, .Orkh.e-quotation b, .Orkh.e-normalization b,
.Ougr.e-example b, .Ougr.e-quotation b, .Ougr.e-normalization b,
.Runr.e-example b, .Runr.e-quotation b, .Runr.e-normalization b,
.Sogd.e-example b, .Sogd.e-quotation b, .Sogd.e-normalization b,
.Syrc.e-example b, .Syrc.e-quotation b, .Syrc.e-normalization b,
.Ugar.e-example b, .Ugar.e-quotation b, .Ugar.e-normalization b,
.Vith.e-example b, .Vith.e-quotation b, .Vith.e-normalization b,
.Xpeo.e-example b, .Xpeo.e-quotation b, .Xpeo.e-normalization b,
.Xsux.e-example b, .Xsux.e-quotation b, .Xsux.e-normalization b {
background-color: var(--wikt-palette-pink, #ffe0f0);
font-size: inherit;
}
/* Latin (.Latn)
This is the default script, so it has no definitions. */
/* This is needed to overcome the "smartness" of certain web browsers */
.Latn:lang(ja),
.Latn:lang(ko),
.Latn:lang(zh) {
font-family: inherit;
}
/* Latin Fraktur */
.Latf {
font-family: UnifrakturMaguntia, UnifrakturCook, Unifraktur, sans-serif;
font-size: 125%;
}
/* Navajo */
.nv-Latn {
font-family: Calibri, 'Aboriginal Sans', 'DejaVu Sans', 'Arial Unicode MS', sans-serif !important;
}
/* Pitjantjatjara (ḻ ṉ ṟ ṯ and capitals) */
.pjt-Latn {
font-family: Arial, 'Microsoft Sans Serif', Tahoma, sans-serif;
}
/* Latin extended */
/* no styles for .Latnx, .Latinx, .unicode, .Unicode */
/* Adlam */
.Adlm {
font-family: 'Noto Sans Adlam', sans-serif;
}
/* Aghwan */
.Aghb {
font-family: OptimaModoki, 'Noto Sans Caucasian Albanian', sans-serif;
}
/* Ahom */
.Ahom {
font-family: AhomUnicode, 'Noto Serif Ahom', 'Unifont Upper', sans-serif;
}
/* Arabic */
.Arab,
.fa-Arab,
.glk-Arab,
.kk-Arab,
.ks-Arab,
.ku-Arab,
.mzn-Arab,
.ota-Arab,
.pa-Arab,
.ps-Arab,
.sd-Arab,
.tt-Arab,
.ug-Arab,
.ur-Arab,
.ms-Arab {
font-family: 'Noto Naskh Arabic', 'Iranian Sans', Tahoma, 'Microsoft Sans Serif', 'Arial Unicode MS', sans-serif;
font-size: 133%;
direction: rtl;
unicode-bidi: isolate;
}
/* Persian */
.fa-Arab {
font-family: 'Iranian Sans', 'Geeza Pro', Tahoma, sans-serif;
}
/* Kashmiri */
.ks-Arab {
font-family: 'Noto Naskh Arabic', Amiri, Mirza, 'Scheherazade New', Scheherazade, 'Geeza Pro', Tahoma, sans-serif;
}
/* Kurdish */
.ku-Arab {
font-family: 'Noto Naskh Arabic', Tahoma, 'Arial Unicode MS', 'UT Cairo', 'UT Naskh', sans-serif;
}
/* Punjabi Shahmukhi, Urdu */
.pa-Arab,
.ur-Arab {
font-family: 'Noto Nastaliq Urdu', Tahoma, 'Arial Unicode MS', 'UT Cairo', 'UT Naskh', sans-serif;
}
/* Pashto */
.ps-Arab {
font-family: Pokhto, 'Pashto Kror Asiatype', 'Scheherazade New', Scheherazade, sans-serif;
}
/* Sindhi */
.sd-Arab {
font-family: 'MB Sindhi', Tahoma, 'MBSarang Sattar', 'MB Bhitai Sattar', 'MB Lateefi', 'Ayaz Gul', sans-serif;
}
/* Uyghur */
.ug-Arab {
font-family: 'UKIJ Tuz', 'Microsoft Uighur', 'Scheherazade New', Scheherazade, sans-serif;
}
/* Imperial Aramaic */
.Armi {
font-family: 'Segoe UI Historic', 'Aramaic Imperial Yeb', 'Noto Sans Imperial Aramaic', sans-serif;
direction: rtl;
unicode-bidi: isolate;
}
/* Armenian */
.Armn {
font-family: Mshtakan, Arial, 'Segoe UI', Tahoma, 'Arian AMU', 'DejaVu Sans', sans-serif;
}
/* Avestan */
.Avst {
font-family: Avestan, Ahuramzda, 'Noto Sans Avestan', sans-serif;
font-size: 125%;
direction: rtl;
unicode-bidi: isolate;
font-variant-ligatures: none; /* Avestan language */
}
.Avst:lang(pal) { /* Pazend (Middle Persian in Avestan script) */
font-variant-ligatures: normal;
}
/* Balinese */
.Bali {
font-family: 'Noto Sans Balinese', sans-serif;
}
/* Bamum */
.Bamu {
font-family: 'Noto Sans Bamum', sans-serif;
}
/* Bassa */
.Bass {
font-family: 'Noto Sans Bassa Vah', sans-serif;
}
/* Batak */
.Batk {
font-family: Batak-Unicode, 'Noto Sans Batak', sans-serif;
}
/* Bengali, Assamese */
.Beng,
.as-Beng {
font-family: 'Bangla Sangam MN', UniBangla, 'Code2000', Likhan, 'UT Bengali Dhaka', Vrinda, 'Noto Sans Bengali', sans-serif;
font-size: 130%;
}
/* Bhaiksuki */
.Bhks {
font-family: 'Noto Sans Bhaiksuki', sans-serif;
}
/* Zhuyin (Bopomofo) */
.Bopo {
font-family: MOESongUN, DFKai-SB, 'Microsoft Yahei', 'Microsoft Jhenghei', 'Source Han Sans TC', 'Source Han Sans TW', 'Noto Sans CJK TC', sans-serif;
}
/* Brahmi */
.Brah {
font-family: 'Segoe UI Historic', 'Noto Sans Brahmi', sans-serif;
}
/* Braille */
.Brai {
font-size: 150%;
}
/* Buginese */
.Bugi {
font-family: Saweri, 'Noto Sans Buginese', sans-serif;
}
/* Buhid [[Wiktionary:Grease pit/2025/February#Buhid]] */
.Buhd {
font-family: 'Noto Sans Buhid', Quivira, sans-serif;
font-size: 110%;
}
/* Chakma */
.Cakm {
font-family: RibengUni, 'Noto Sans Chakma', sans-serif;
}
/* Unified Canadian Aboriginal Syllabics */
.Cans {
font-family: 'Euphemia UCAS', Euphemia, 'Aboriginal Sans', OskiBlackfoot, 'Code2000', 'Everson Mono Unicode', 'Noto Sans Canadian Aboriginal', sans-serif;
font-size: 110%;
}
/* Carian */
.Cari {
font-family: 'Segoe UI Historic', Aegean, 'Noto Sans Carian', sans-serif;
font-size: 125%;
}
/* Cham */
.Cham {
font-family: 'Code2000', 'JG ChamCambodia', 'Noto Sans Cham', sans-serif;
font-size: 110%;
}
/* Cherokee */
.Cher {
font-family: Digohweli, 'Aboriginal Sans', 'Code2000', Marin, 'Rotinonhsonni Sans', 'Everson Mono Unicode', 'Noto Sans Cherokee', sans-serif;
font-size: 110%;
}
/* Khwarezmian */
.Chrs {
font-family: 'Noto Sans Chorasmian', sans-serif;
}
/* Coptic */
.Copt {
font-family: Quivira, Antinoou, 'New Athena Unicode', Analecta, FreeSerifAvvaShenouda, 'Arial Coptic', 'Sophia Nubian', 'Code2000', 'Noto Sans Coptic', sans-serif;
font-size: 130%;
}
/* Cypro-Minoan */
.Cpmn {
font-family: 'Noto Sans Cypro Minoan', sans-serif;
}
/* Cypriot */
.Cprt {
font-family: 'Segoe UI Historic', Aegean, 'Noto Sans Cypriot', sans-serif;
font-size: 115%;
direction: rtl;
unicode-bidi: isolate;
}
/* Cyrillic */
.Cyrl {
font-family: Helvetica, Geneva, 'Arial Unicode MS', 'Lucida Sans Unicode', sans-serif;
}
/* Old Cyrillic (Old Church Slavonic, Old East Slavic) */
.Cyrs {
font-family: Monomakh, 'Monomakh Unicode', 'Monomakh Unicode TT', Menaion, 'Menaion Unicode', 'Menaion Unicode TT', Ponomar, 'Ponomar Unicode', 'Ponomar Unicode TT', Fedorovsk, 'Fedorovsk Unicode', 'Fedorovsk Unicode TT', Pochaevsk, 'Pochaevsk Unicode', 'Pochaevsk Unicode TT', Triodion, 'Triodion Unicode', 'Triodion Unicode TT', 'Acathist', 'Shafarik', Vilnius, BukyVede, 'Kliment Std', 'RomanCyrillic Std', 'Monomachus', 'Old Standard', 'Old Standard TT', Dilyana, 'Hirmos Ponomar', 'Hirmos Ponomar TT', 'Menaion Medieval', Lazov, 'DejaVu Sans', 'Lucida Grande', 'Arial Unicode MS', 'Lucida Sans Unicode', sans-serif;
font-size: 125%;
}
/* Devanagari */
.Deva {
font-family: 'Tiro Devanagari Sanskrit', 'Sanskrit Text', 'Adobe Devanagari', 'Noto Serif Devanagari', Utsaah, 'Devanagari MT', Raghu, Gargi, JanaSanskrit, JanaHindi, Siddhanta, 'Noto Sans Devanagari', sans-serif;
font-size: 120%;
}
/* Dhives Akuru */
.Diak {
font-family: 'Noto Serif Dives Akuru', serif;
}
/* Dogra */
.Dogr {
font-family: 'Noto Serif Dogra', sans-serif;
}
/* Deseret */
.Dsrt {
font-family: 'Segoe UI Symbol', 'Code2001', 'MPH 2B Damase', 'Everson Mono', sans-serif;
}
/* Duployan */
.Dupl {
font-family: 'Noto Sans Duployan', sans-serif;
}
/* Demotic (Egyptian) */
.Egyd, .Egyd * {
font-style: normal;
direction: rtl;
}
/* Egyptian hieroglyphs */
.Egyp {
font-family: 'NewGardiner', 'Noto Sans Egyptian Hieroglyphs', Abydos, Aegyptus, 'Segoe UI Historic', sans-serif;
font-size: 150%;
}
/* Elbasan */
.Elba {
font-family: 'Noto Sans Elbasan', sans-serif;
}
/* Elymaic */
.Elym {
font-family: 'Noto Sans Elymaic', sans-serif;
}
/* Ethiopic (Ge'ez) */
.Ethi {
font-family: 'Abyssinica SIL', Nyala, 'Code2000', 'Ethiopia Jiret', 'GF Zemen Unicode', 'TITUS Cyberbit Basic', 'Visual Geez Unicode', 'Visual Geez Unicode Agazian', 'Visual Geez Unicode Title', 'Noto Sans Ethiopic', sans-serif;
font-size: 120%;
}
/* Georgian */
.Geor {
font-family: 'DejaVu Sans', 'Arial Unicode MS', Sylfaen, sans-serif;
}
/* Glagolitic */
.Glag {
font-family: Shafarik, Menaion, 'Menaion Unicode', 'Menaion Unicode TT', 'Segoe UI Historic', BukyVede, Dilyana, 'Noto Sans Glagolitic', sans-serif;
font-size: 150%;
}
/* Gunjala Gondi */
.Gong {
font-family: 'Noto Sans Gunjala Gondi', sans-serif;
}
/* Masaram Gondi */
.Gonm {
font-family: 'Noto Sans Masaram Gondi', sans-serif;
}
/* Gothic */
.Goth {
font-family: 'Segoe UI Historic', 'Code2001', Skeirs, 'MPH 2B Damase', 'Noto Sans Gothic', sans-serif;
}
b.Goth,
strong.Goth {
font-size: larger;
}
/* Grantha */
.Gran {
font-family: 'Noto Sans Grantha', sans-serif;
}
/* Greek (.Grek) removed - [[Wiktionary:Grease pit/2025/February#Sans-serif Greek font]] */
/* Albanian, Pontic and Tsakonian in Greek script */
.Grek:lang(sq),
.Grek:lang(pnt),
.Grek:lang(tsd) {
font-family: 'Gentium Plus', 'Gentium', 'Lucida Sans Unicode', sans-serif;
}
/* Polytonic Greek */
.polytonic, .Polyt {
font-family: 'SBL Greek', 'SBL BibLit', 'New Athena Unicode', 'DejaVu Sans', Athena, 'Gentium Plus', Gentium, 'Palatino Linotype', Menaion, Times, 'Arial Unicode MS', 'Lucida Sans Unicode', 'Lucida Grande', sans-serif;
}
/* Messapic */
.Polyt:lang(cms) {
font-family: 'Noto Serif', 'Lucida Sans Unicode', 'Gentium Plus', 'Gentium', 'Noto Sans', sans-serif;
}
/* Gujarati */
.Gujr {
font-family: 'Noto Sans Gujarati', 'Gujarati Sangam MN', 'Gujarati MT', Shruti, 'Lucida Grande', 'Arial Unicode MS', 'Lucida Sans Unicode', 'Code2000', 'TITUS Cyberbit Basic', 'Chrysanthi Unicode', 'Bitstream Cyberbit', 'Bitstream CyberBase', 'Bitstream Vera', 'Visual Geez Unicode', sans-serif;
font-size: 125%;
}
/* Gurmukhi */
.Guru {
font-family: 'Gurmukhi MN', 'Gurmukhi MT', 'UT Punjabi Amritsar', 'Lucida Grande', 'Arial Unicode MS', 'Lucida Sans Unicode', 'Code2000', 'TITUS Cyberbit Basic', 'Chrysanthi Unicode', 'Bitstream Cyberbit', 'Bitstream CyberBase', 'Bitstream Vera', 'Visual Geez Unicode', 'Noto Sans Gurmukhi', sans-serif;
font-size: 125%;
}
/* Chinese (Han)
.Hani = generic
.Hans = simplified
.Hant = traditional
*/
.Hans {
font-family: 'PingFang SC', DengXian, 'Source Han Sans SC', 'Source Han Sans CN', 'Noto Sans CJK SC', 'Microsoft Yahei', SimHei, SimSun, NSimSun, SimSun-ExtB, Song, 'Heiti SC', HanaMinA, HanaMinB, sans-serif;
}
.Hani,
.Hant {
font-family: 'PingFang TC', 'Source Han Sans TC', 'Source Han Sans TW', 'Noto Sans CJK TC', 'Microsoft Jhenghei', MOESongUN, PMingLiU, PMingLiU-ExtB, MingLiU, MingLiU-ExtB, Ming, 'Heiti TC', HanaMinA, HanaMinB, sans-serif;
}
.Hani,
.Hans,
.Hant {
font-size: 120%;
line-height: 1;
}
.Hani:lang(vi),
.Hani:lang(tyz) {
font-family: 'Nom Na Tong', 'HAN NOM A', 'HAN NOM B', Sun-ExtA, Sun-ExtB, Ming-Lt-HKSCS-UNI-H, Ming-Lt-HKSCS-ExtB, HanaMinA, HanaMinB, HanaMin, 'PingFang TC', MingLiU, MingLiU-ExtB, 'MingLiU_HKSCS', 'MingLiU_HKSCS-ExtB', SimSun, SimSun-ExtB, 'Arial Unicode MS', 'TITUS Cyberbit Basic', sans-serif;
/* CJK Unified Ideographs Extension C and Extension D (U+2A700..U+2B734, U+2B740..U+2B81F)
font-family: Sun-ExtB, 'MingLiU_HKSCS-ExtB', Ming-Lt-HKSCS-ExtB, HanaMinB, sans-serif;
**/
}
/* Hanunoo */
.Hano {
font-family: 'Noto Sans Hanunoo', Quivira, 'MPH 2B Damase', sans-serif;
font-size: 110%;
}
/* Hatran */
.Hatr {
font-family: 'Noto Sans Hatran', sans-serif;
direction: rtl;
}
/* Hebrew */
.Hebr {
font-family: 'SBL Hebrew', 'SBL BibLit', 'Taamey David CLM', 'Taamey Frank CLM', Alef, 'Noto Sans Hebrew', Narkisim, Miriam, Kinryu, 'Arial Hebrew', Arial, 'Adobe Hebrew', serif;
font-size: 133%;
direction: rtl;
unicode-bidi: isolate;
}
/* Hiragana: see .Jpan */
/* Anatolian Hieroglyphs */
.Hluw {
font-family: 'Noto Sans Anatolian Hieroglyphs', Anatolian, sans-serif;
font-size: 200%;
}
/* Pahawh Hmong */
.Hmng {
font-family: 'Noto Sans Pahawh Hmong', sans-serif;
}
/* Nyiakeng Puachue Hmong */
.Hmnp {
font-family: 'Noto Serif Nyiakeng Puachue Hmong', serif;
}
/* Old Hungarian */
.Hung {
font-family: 'Noto Sans Old Hungarian', sans-serif;
}
/* Italic */
.Ital {
font-family: 'Segoe UI Historic', Aegean, 'Noto Sans Old Italic', sans-serif;
font-size: 125%;
}
/* Javanese */
.Java {
font-family: 'Tuladha Jejeg', 'Javanese Text', adjisaka, 'Noto Sans Javanese', sans-serif;
}
/* Japanese scripts
.Hira = Hiragana
.Kana = Katakana (used for Ainu language)
.Jpan = Hiragana + Katakana + Kanji
*/
.Hira,
.Jpan,
.Kana {
font-family: 'Hiragino Kaku Gothic Pro', Osaka, 'Yu Gothic', Meiryo, 'Source Han Sans J', 'Source Han Sans JP', 'Noto Sans CJK JP', 'Droid Sans Japanese', 'MS PGothic', 'MS Gothic', 'MS PMincho', 'MS Mincho', HanaMinA, HanaMinB, sans-serif;
font-size: 120%;
line-height: 1;
}
.Jpan ruby rt {
font-family: 'Yu Gothic UI', 'Meiryo UI', 'MS UI Gothic', sans-serif;
font-size: 60%;
}
/* Kayah Li */
.Kali {
font-family: 'Code2000', 'Noto Sans Kayah Li', sans-serif;
}
/* Katakana: see .Jpan */
/* Kharoshthi */
.Khar {
font-family: 'Segoe UI Historic', 'MPH 2B Damase', 'Noto Sans Kharoshthi', sans-serif;
font-size: 110%;
direction: rtl;
unicode-bidi: isolate;
}
/* Khmer */
.Khmr {
font-family: 'Khmer OS', 'Khmer OS Content', 'Leelawadee UI', 'Noto Sans Khmer', 'Code2000', 'Khmer Mondulkiri U OT ls', sans-serif;
font-size: 125%;
}
/* Khojki */
.Khoj {
font-family: 'Noto Sans Khojki', sans-serif;
}
/* Khitan small script */
.Kits {
font-family: 'Noto Serif Khitan Small Script', serif;
}
/* Kannada */
.Knda {
font-family: 'Kannada Sangam MN', JanaKannada, Tunga, Kedage, RaghuKannada, Sampige, 'Arial Unicode MS', 'Code2000', 'Bitstream Cyberbit', 'Bitstream CyberBase', 'Noto Sans Kannada', sans-serif;
font-size: 125%;
}
/* Korean */
.Kore,
.Hang {
font-family: 'Apple SD Gothic Neo', 'Malgun Gothic', Dotum, Gulim, 'NanumBarunGothic YetHangul', NanumBarunGothic, UnDotum, 'Source Han Sans K', 'Source Han Sans KR', 'Noto Sans CJK KR', NanumGothic, 'NanumMyeongjo YetHangul', NanumMyeongjo, Batang, UnBatang, sans-serif;
font-size: 120%;
line-height: 1;
}
/* Kaithi */
.Kthi {
font-family: 'Noto Sans Kaithi', sans-serif;
}
/* Lanna */
.Lana {
font-family: 'Lanna Alif', 'Noto Sans Tai Tham', sans-serif;
}
/* Lao */
.Laoo {
font-family: 'Phetsarath OT', 'Saysettha OT', 'JG Basic Lao Opentype', 'JG Basic2 Lao Opentype', 'JG LaoTimes Opentype', 'Phagnoum Lao Unicode Opentype', 'JG Lao Old Arial Opentype', DokChampa, 'Code2000', 'JG Lao Classic Opentype', 'Alice0 Unicode', 'Alice1 Unicode', 'Alice2 Unicode', 'Alice3 Unicode', 'Alice4 Unicode', 'Alice5 Unicode', 'Noto Sans Lao', 'Noto Sans Lao Looped', sans-serif;
font-size: 125%;
}
/* Lepcha */
.Lepc {
font-family: 'Noto Sans Lepcha', Mingzat, sans-serif;
}
/* Limbu */
.Limb {
font-family: 'Code2000', 'MPH 2B Damase', 'Noto Sans Limbu', sans-serif;
}
/* Linear A */
.Lina {
font-family: Aegean, 'Noto Sans Linear A', sans-serif;
font-size: 125%;
}
/* Linear B */
.Linb {
font-family: Aegean, 'Noto Sans Linear B', sans-serif;
font-size: 125%;
}
/* Lisu */
.Lisu {
font-family: 'Noto Sans Lisu', Quivira, sans-serif;
}
/* Lycian */
.Lyci {
font-family: 'Segoe UI Historic', Aegean, 'Noto Sans Lycian', sans-serif;
font-size: 125%;
}
/* Lydian */
.Lydi {
font-family: 'Segoe UI Historic', Aegean, 'Noto Sans Lydian', sans-serif;
font-size: 125%;
}
/* Mahajani */
.Mahj {
font-family: 'Noto Sans Mahajani', sans-serif;
font-size: 125%;
}
/* Makasar */
.Maka {
font-family: 'Noto Serif Makasar', serif;
}
/* Mandaic */
.Mand {
font-family: 'Noto Sans Mandaic', sans-serif;
direction: rtl;
unicode-bidi: isolate;
}
/* Manichaean */
.Mani {
font-family: 'Noto Sans Manichaean', sans-serif;
font-size: 133%;
direction: rtl;
unicode-bidi: isolate;
}
/* Marchen */
.Marc {
font-family: 'Noto Sans Marchen', sans-serif;
}
/* Medefaidrin */
.Medf {
font-family: 'Noto Sans Medefaidrin', sans-serif;
}
/* Mende */
.Mend {
font-family: 'Noto Sans Mende Kikakui', sans-serif;
}
/* Meroitic cursive */
.Merc {
font-family: 'Segoe UI Historic', Aegyptus, 'Noto Sans Meroitic', sans-serif;
font-size: 150%;
}
/* Meroitic hieroglyphs */
.Mero {
font-family: Aegyptus, 'Noto Sans Meroitic', sans-serif;
font-size: 150%;
}
/* Malayalam */
.Mlym {
font-family: 'Malayalam Sangam MN', Kartika, 'Code2000', 'Lucida Grande', 'Arial Unicode MS', 'Lucida Sans Unicode', 'TITUS Cyberbit Basic', 'Chrysanthi Unicode', 'Bitstream Cyberbit', 'Bitstream CyberBase', 'Bitstream Vera', 'Visual Geez Unicode', 'Noto Sans Malayalam', sans-serif;
font-size: 125%;
}
/* Modi */
.Modi {
font-family: 'Noto Sans Modi', sans-serif;
}
/* Classical Mongolian, Manchu, Xibe, Clear Script */
.Mong,
.mnc-Mong, /* Manchu */
.sjo-Mong, /* Xibe */
.xwo-Mong /* Clear Script, Todo */ {
font-family: 'Mongolian Baiti', 'Noto Sans Mongolian', sans-serif;
font-size: 140%;
-webkit-writing-mode: vertical-lr;
-moz-writing-mode: vertical-lr;
writing-mode: vertical-lr;
}
/* Mro */
.Mroo {
font-family: 'Noto Sans Mro', sans-serif;
}
/* Meitei Mayek */
.Mtei {
font-family: 'Eeyek Unicode', 'Noto Sans Meetei Mayek', sans-serif;
}
/* Multani */
.Mult {
font-family: 'Noto Sans Multani', sans-serif;
}
/* Burmese */
.Mymr {
font-family: TharLon, Padauk, 'Myanmar Text', 'Myanmar3', 'Myanmar2', 'Myanmar1', ParabaikSans, MyMyanmar, 'Noto Sans Myanmar', sans-serif;
font-size: 130%;
}
big.Mymr,
strong.Mymr,
b.Mymr {
font-size: 162%;
}
.Mymr b {
font-size: 130%;
}
/* Nandinagari */
.Nand {
font-family: 'Noto Sans Nandinagari', sans-serif;
}
/* North Arabian */
.Narb {
font-family: 'Noto Sans Old North Arabian', sans-serif;
}
/* Newa */
.Newa {
font-family: 'Noto Sans Newa', sans-serif;
}
/* N'Ko */
.Nkoo {
font-family: "Noto Sans N'Ko", sans-serif;
}
/* Nüshu or Nushu */
.Nshu {
font-family: 'Noto Sans Nushu', 'Noto Traditional Nushu', 'Unicode Nushu', sans-serif;
font-size: 130%;
}
/* Ogham */
.Ogam {
font-family: 'Segoe UI Historic', Beth-Luis-Nion, Pollach, 'Maigh Nuad', 'Craobh Ruadh', 'Everson Mono Ogham', Cog, Crosta, 'TITUS Ogham', 'Ragnarok Ogham', 'Noto Sans Ogham', sans-serif;
font-size: 125%;
}
big.Ogam, strong.Ogam, b.Ogam, b .Ogam {
font-size: 156%;
}
.Ogam b {
font-size: 125%;
}
/* Ol Chiki */
.Olck {
font-family: 'Code2000', 'Noto Sans Ol Chiki', sans-serif;
}
/* Orkhon Runes */
.Orkh {
font-family: 'Segoe UI Historic', Orkun, 'Old Turkic', 'Noto Sans Old Turkic', sans-serif;
}
/* Odia */
.Orya {
font-family: 'Oriya Sangam MN', 'Noto Sans Oriya', sans-serif;
font-size: 125%;
}
/* Osage */
.Osge {
font-family: 'Noto Sans Osage', sans-serif;
}
/* Osmanya */
.Osma {
font-family: 'Noto Sans Osmanya', sans-serif;
}
/* Old Uyghur */
.Ougr {
font-family: 'Noto Serif Old Uyghur', serif;
font-size: 133%;
direction: rtl;
unicode-bidi: isolate;
}
/* Palmyrene */
.Palm {
font-family: 'Noto Sans Palmyrene', sans-serif;
}
/* Pau Cin Hau */
.Pauc {
font-family: 'Noto Sans Pau Cin Hau', sans-serif;
}
/* Old Permic */
.Perm {
font-family: 'Noto Sans Old Permic', sans-serif;
}
/* Phags-pa */
.Phag {
font-family: 'BabelStone Phags-pa Book', 'Microsoft PhagsPa', 'Noto Sans Phags-pa', sans-serif;
-webkit-writing-mode: vertical-lr;
-moz-writing-mode: vertical-lr;
writing-mode: vertical-lr;
layout-flow: vertical-ideographic;
}
/* Inscriptional Pahlavi */
.Phli {
font-family: 'Segoe UI Historic', 'Noto Sans Inscriptional Pahlavi', Shapour, 'ZH Mono', sans-serif;
direction: rtl;
unicode-bidi: isolate;
}
/* Psalter Pahlavi */
.Phlp {
font-family: 'Noto Sans Psalter Pahlavi', sans-serif;
}
/* Phoenician */
.Phnx {
font-family: 'Segoe UI Historic', 'ALPHABETUM Unicode', 'MPH 2B Damase', Aegean, 'Code2001', 'Free Sans', 'Noto Sans Phoenician', sans-serif;
font-size: 125%;
direction: rtl;
unicode-bidi: isolate;
}
/* Pollard */
.Plrd {
font-family: 'Noto Sans Miao', sans-serif;
}
/* Inscriptional Parthian */
.Prti {
font-family: 'Segoe UI Historic', 'Noto Sans Inscriptional Parthian', 'ZH Mono', sans-serif;
}
/* Rejang */
.Rjng {
font-family: 'Noto Sans Rejang', 'Code2000', sans-serif;
}
/* Hanifi Rohingya */
.Rohg {
font-family: 'Noto Sans Hanifi Rohingya', sans-serif;
}
/* Runic (Germanic) */
.Runr {
font-family: 'Segoe UI Historic', Junicode, 'Free Mono', 'Caslon Roman', 'Segoe UI Symbol', 'Code2000', 'Everson Mono', 'TITUS Cyberbit Basic', 'Noto Sans Runic', sans-serif;
font-size: 130%;
}
/* Samaritan */
.Samr {
font-family: 'Noto Sans Samaritan', Quivira, 'Everson Mono', Unifont, sans-serif;
font-size: 133%;
direction: rtl;
unicode-bidi: isolate;
}
/* South Arabian */
.Sarb {
font-family: 'Segoe UI Historic', Qataban, 'Noto Sans Old South Arabian', sans-serif;
}
/* Saurashtra */
.Saur {
font-family: Pagul, 'Noto Sans Saurashtra', sans-serif;
}
/* SignWriting */
.Sgnw {
font-family: 'Noto Sans SignWriting', sans-serif;
}
/* Shavian */
.Shaw {
font-family: 'Segoe UI Historic', 'Code2001', 'Everson Mono', 'Noto Sans Shavian', sans-serif;
}
/* Sharada */
.Shrd {
font-family: 'Noto Sans Sharada', sans-serif;
font-size: 125%;
}
/* Siddham */
.Sidd {
font-family: 'Noto Sans Siddham', sans-serif;
}
/* Sinhalese */
.Sinh {
font-family: 'Sinhala Sangam MN', KaputaUnicode, KandyUnicode, Dinamina, DinaminaUniWeb, Potha, Madhura, 'Noto Sans Sinhala', sans-serif;
font-size: 125%;
}
/* Sogdian */
.Sogd {
font-family: 'Noto Sans Sogdian', sans-serif;
}
/* Old Sogdian */
.Sogo {
font-family: 'Noto Sans Old Sogdian', sans-serif;
}
/* Sorang Sompeng */
.Sora {
font-family: 'Noto Sans Sora Sompeng', sans-serif;
}
/* Soyombo */
.Soyo {
font-family: 'Noto Sans Soyombo', sans-serif;
}
/* Sundanese */
.Sund {
font-family: 'Sundanese Unicode', 'Noto Sans Sundanese', sans-serif;
}
/* Sunuwar */
.Sunu {
font-family: 'Noto Sans Sunuwar', sans-serif;
}
/* Syloti Nagri */
.Sylo {
font-family: 'Noto Sans Syloti Nagri', sans-serif;
}
/* Syriac */
.Syrc {
font-family: 'Estrangelo Edessa', 'San Francisco', 'Code2000', 'Bitstream Cyberbit', 'Bitstream CyberBase', 'Bitstream Vera', 'TITUS Cyberbit Basic', sans-serif;
font-size: 130%;
direction: rtl;
unicode-bidi: isolate;
}
/* Serto or Western form of Syriac */
.Syrc:lang(tru),
.Syrc:lang(amw) {
font-family: 'Noto Sans Syriac Western', 'Serto Jerusalem', 'Noto Sans Syriac', sans-serif;
}
/* Madnhaya or Eastern form of Syriac */
.Syrc:lang(aii) {
font-family: 'Noto Sans Syriac Eastern', 'Noto Sans Syriac', sans-serif;
}
/* Tagbanwa */
.Tagb {
font-family: Quivira, Tagbanwa, 'Noto Sans Tagbanwa', sans-serif;
font-size: 110%;
}
/* Takri */
.Takr {
font-family: 'Noto Sans Takri', sans-serif;
}
/* Tai Le */
.Tale {
font-family: 'Microsoft Tai Le', 'Tai Le Valentinium', 'MPH 2B Damase', 'Noto Sans Tai Le', sans-serif;
}
/* New Tai Lue */
.Talu {
font-family: 'Noto Sans New Tai Lue', 'Microsoft New Tai Lue', 'Dai Banna SIL Book', 'Husb_New_Tai_Lue_WB', sans-serif;
}
/* Tamil */
.Taml {
font-family: 'Tamil Sangam MN', InaiMathi, Vijaya, Akshar, JanaTamil, 'Code2000', ETTamilNew, 'Lohit Tamil', 'Arial Unicode MS', 'Free Serif', Latha, 'Noto Sans Tamil', 'Noto Sans Tamil Supplement', sans-serif;
font-size: 125%;
}
/* Tangut */
.Tang {
font-family: 'BabelStone Tangut Wenhai', 'Tangut N4694', 'Tangut Yinchuan', 'New Tangut', 'New Tangut Std', 'Tangut TWU', 'Babelstone Tangut Wenhai', sans-serif;
font-size: 120%;
text-orientation: upright;
-webkit-text-orientation: upright
}
/* Tai Viet */
.Tavt {
font-family: 'Tai Heritage Pro', 'Noto Sans Tai Viet', sans-serif;
}
/* Lai Tay */
.Tayo {
-webkit-writing-mode: vertical-rl;
-moz-writing-mode: vertical-rl;
writing-mode: vertical-rl;
layout-flow: vertical-ideographic;
}
/* Telugu */
.Telu {
font-family: 'Telugu Sangam MN', 'Noto Sans Telugu', sans-serif;
font-size: 125%;
}
/* Tifinagh */
.Tfng {
font-family: 'Hapax Berbère', Ebrima, 'Code2000', DejaVu, 'Noto Sans Tifinagh', sans-serif;
font-size: 120%;
}
/* Baybayin */
.Tglg {
font-family: 'Noto Sans Tagalog', 'Tagalog Stylized', 'Baybayin Lopez', 'Tagalog Doctrina 1593', Quivira, sans-serif;
}
.Tglg:lang(bcl) {
font-family: 'Bikol Mintz', 'Noto Sans Tagalog', 'Tagalog Stylized', 'Baybayin Lopez', 'Tagalog Doctrina 1593', Quivira, sans-serif;
}
.Tglg:lang(ceb) {
font-family: 'Bisaya Hervas', 'Noto Sans Tagalog', 'Tagalog Stylized', 'Baybayin Lopez', 'Tagalog Doctrina 1593', Quivira, sans-serif;
}
/* Thaana */
.Thaa {
font-family: 'Noto Sans Thaana', sans-serif;
}
/* Thai */
.Thai {
font-family: 'Leelawadee UI', Leelawadee, 'Arial Unicode MS', 'Code2000', 'Noto Sans Thai', 'Noto Sans Thai Looped', sans-serif;
font-size: 125%;
}
/* Tibetan */
.Tibt {
font-family: 'Noto Serif Tibetan', 'Noto Sans Tibetan', Jomolhari-ID, 'Tibetan Machine Uni', 'Tibetan Machine Web', Jomolhari, 'Microsoft Himalaya', sans-serif;
font-size: 130%;
}
/* Tirhuta */
.Tirh {
font-family: 'Noto Sans Tirhuta', sans-serif;
}
/* Toto */
.Toto {
font-family: 'Noto Serif Toto', serif;
}
/* Ugaritic */
.Ugar {
font-family: 'Oxford Ugaritic', 'Segoe UI Historic', Aegean, 'Code2001', 'Noto Sans Ugaritic', sans-serif;
}
/* Vai */
.Vaii {
font-family: Ebrima, 'Code2000', 'Noto Sans Vai', sans-serif;
font-size: 110%;
}
/* Vithkuqi */
.Vith {
font-family: 'Noto Serif Vithkuqi', 'Noto Sans Vithkuqi', sans-serif;
}
/* Varang Kshiti */
.Wara {
font-family: 'Noto Sans Warang Citi', sans-serif;
}
/* Wancho */
.Wcho {
font-family: 'Noto Sans Wancho', sans-serif;
}
/* Old Persian cuneiform */
.Xpeo {
font-family: 'Segoe UI Historic', Aegean, Xerxes, 'Code2001', 'Noto Sans Old Persian', sans-serif;
}
/* Sumero-Akkadian cuneiform */
.Xsux {
font-family: Akkadian, FreeIdgSerif, CuneiformComposite, 'Segoe UI Historic', sans-serif; /* Neo-Assyrian form */
/* font-family: FreeIdgSerif, Akkadian, CuneiformComposite, 'Segoe UI Historic', sans-serif; */ /* Old Assyrian form */
/* font-family: Akkadian, CuneiformComposite, FreeIdgSerif, 'Segoe UI Historic', sans-serif; /* /* Neo-Sumerian form */
/* font-family: Akkadian, CuneiformComposite, FreeIdgSerif, 'Segoe UI Historic', sans-serif; /* /* Classical Sumerian form */
font-size: 125%;
}
/* Specify Hittite fonts */
.Xsux:lang(hit) {
font-family: UllikummiA, Akkadian, FreeIdgSerif, CuneiformComposite, 'Segoe UI Historic', sans-serif;
}
/* Specify Sumerian fonts */
.Xsux:lang(sux) {
font-family: CuneiformComposite, Akkadian, 'Segoe UI Historic', sans-serif;
}
/* Yezidi */
.Yezi {
font-family: 'Noto Serif Yezidi', serif;
}
/* Yi */
.Yiii {
font-family: 'Noto Sans Yi', sans-serif;
}
/* Zanabazar Square */
.Zanb {
font-family: 'Noto Sans Zanabazar Square', sans-serif;
}
/* Symbols */
.Zsym {
font-family: Symbola, sans-serif;
font-size: 150%;
}
/* Rumi numerals; see <https://en.wikipedia.org/wiki/Rumi_Numeral_Symbols> */
.Rumin, .Ruminumerals {
font-size: 120%;
}
/*
Multilingual writing systems and notations
*/
/* English Phonemic Representation (AHD) */
.enPR {
font-family: 'Arial Unicode MS', 'Lucida Grande', 'Gentium Plus', Gentium, 'Gentium Alternative', 'TITUS Cyberbit Basic', 'Lucida Sans Unicode', sans-serif;
}
/* International Phonetic Alphabet */
.IPA, .Ipach, .IPAchar {
font-family: 'Gentium Plus', Gentium, GentiumAlt, 'DejaVu Sans', 'Segoe UI', 'Lucida Grande', 'Charis SIL', 'Doulos SIL', 'TITUS Cyberbit Basic', 'Code2000', 'Lucida Sans Unicode', sans-serif;
font-size: 110%;
font-variant-ligatures: no-common-ligatures;
}
/* Musical notation */
.Music, .musical {
font-family: Musica, 'Musical Symbols', Euterpe, 'Noto Music', sans-serif;
font-size: 140%;
}
/* Znamenny musical notation */
.Zname {
font-family: 'Mezenets Unicode', Slavonic, Voskresensky, Smolensky, Symbola, 'Noto Znamenny Musical Notation', sans-serif;
}
ow6904ikl8aoe8vsjbhn9i0aobnyckr
MediaWiki:Gadget-VectorClassic.css
8
17934
88870
2014-04-30T16:31:14Z
en>Kephir
0
sync with [[w:MediaWiki:Gadget-VectorClassic.css]]
88870
css
text/css
html, body {
font-family: sans-serif;
}
div#content {
color: black;
}
div#content h1,
div#content h2,
div#content #firstHeading,
div#content .mw-editsection {
font-family: inherit;
}
div#content h1,
div#content h2,
div#content h3,
div#content h4,
div#content h5,
div#content h6 {
line-height: inherit;
margin: 0;
padding-top: 0.5em;
padding-bottom: 0.17em;
}
div#content #firstHeading {
font-size: 1.6em;
line-height: 1.2em;
margin-bottom: 0.1em;
padding-bottom: 0;
}
div#content h1 {
font-size: 188%;
margin-bottom: 0.6em;
}
div#content h2 {
font-size: 150%;
margin-bottom: 0.6em;
}
div#content h3 {
font-size: 132%;
}
div#content h4 {
font-size: 116%;
}
div#content h5 {
font-size: 108%;
}
div#content h6 {
font-size: 100%;
}
.mw-body-content,
#bodyContent {
font-size: 0.8125em;
line-height: 1.5em;
}
leq64ddey8crira5cds9ixyluyb5zix
88871
88870
2026-08-06T23:08:10Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-VectorClassic.css]]
88870
css
text/css
html, body {
font-family: sans-serif;
}
div#content {
color: black;
}
div#content h1,
div#content h2,
div#content #firstHeading,
div#content .mw-editsection {
font-family: inherit;
}
div#content h1,
div#content h2,
div#content h3,
div#content h4,
div#content h5,
div#content h6 {
line-height: inherit;
margin: 0;
padding-top: 0.5em;
padding-bottom: 0.17em;
}
div#content #firstHeading {
font-size: 1.6em;
line-height: 1.2em;
margin-bottom: 0.1em;
padding-bottom: 0;
}
div#content h1 {
font-size: 188%;
margin-bottom: 0.6em;
}
div#content h2 {
font-size: 150%;
margin-bottom: 0.6em;
}
div#content h3 {
font-size: 132%;
}
div#content h4 {
font-size: 116%;
}
div#content h5 {
font-size: 108%;
}
div#content h6 {
font-size: 100%;
}
.mw-body-content,
#bodyContent {
font-size: 0.8125em;
line-height: 1.5em;
}
leq64ddey8crira5cds9ixyluyb5zix
MediaWiki:Gadget-CatchMyAttention.css
8
17935
88872
2025-11-09T12:16:04Z
en>Surjection
0
88872
css
text/css
/* <nowiki> */
.attentionseeking {
border: 1px solid var(--wikt-palette-red, #d73333);
padding: 0.5em;
background: var(--wikt-palette-lightred, #fee7e6);
color: var(--color-base, black);
border-radius: 5px;
}
.attentionseeking::before {
content: '⚠️ {{attention|' attr(data-lang) '}}';
font-family: monospace, 'something';
}
.attentionseeking::after {
content: ' ' attr(title);
}
/* </nowiki> */
2hn8q0db31m98ro5jhjsfal6q5ubgwx
88873
88872
2026-08-06T23:08:18Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-CatchMyAttention.css]]
88872
css
text/css
/* <nowiki> */
.attentionseeking {
border: 1px solid var(--wikt-palette-red, #d73333);
padding: 0.5em;
background: var(--wikt-palette-lightred, #fee7e6);
color: var(--color-base, black);
border-radius: 5px;
}
.attentionseeking::before {
content: '⚠️ {{attention|' attr(data-lang) '}}';
font-family: monospace, 'something';
}
.attentionseeking::after {
content: ' ' attr(title);
}
/* </nowiki> */
2hn8q0db31m98ro5jhjsfal6q5ubgwx
MediaWiki:Gadget-HideLogo.css
8
17936
88874
2014-07-31T06:05:54Z
en>Kephir
0
Migrating pure-CSS gadgets from [[WT:PREFS]]
88874
css
text/css
#p-logo {
display: none;}
#column-one {
padding-top: 0;}
#mw-panel {
top:0 !important;}
t0f2pppo06r0eh9xpagcpod6hm7bext
88875
88874
2026-08-06T23:08:35Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-HideLogo.css]]
88874
css
text/css
#p-logo {
display: none;}
#column-one {
padding-top: 0;}
#mw-panel {
top:0 !important;}
t0f2pppo06r0eh9xpagcpod6hm7bext
MediaWiki:Gadget-vectorTabs.js
8
17937
88876
2017-08-19T19:51:01Z
en>Dixtosa
0
88876
javascript
text/javascript
// {{documentation}}
$(function() {
$("#p-cactions").remove().find("li").insertBefore("#ca-watch,#ca-unwatch").find("a").wrap("<span>");
});
bzkksiyyf8g2iaqdoxvrx59s6gdq2l5
88877
88876
2026-08-06T23:08:46Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-vectorTabs.js]]
88876
javascript
text/javascript
// {{documentation}}
$(function() {
$("#p-cactions").remove().find("li").insertBefore("#ca-watch,#ca-unwatch").find("a").wrap("<span>");
});
bzkksiyyf8g2iaqdoxvrx59s6gdq2l5
MediaWiki:Gadget-vectorTabs.css
8
17938
88878
2017-08-15T15:28:59Z
en>Dixtosa
0
88878
css
text/css
/*.menu li{background-color: #F3F3F3;background-image: url(/w/skins/Vector/images/tab-normal-fade.png);background-position: 0% 100%;background-repeat: repeat-x;float: left;line-height: 1.125em !important;}
.menu{display:block !important;clear:none !important;}
.menu ul{position:static !important;margin-top:10px !important;}
#p-cactions h5{display:none;}
.menu li a{background-image: url(/w/skins/Vector/images/tab-break.png);background-position: 100% 100%;background-repeat: no-repeat;}
*/
#p-cactions {
display:none;
}
fsmgox7en3feoembh09cc0xuc4gqqsw
88879
88878
2026-08-06T23:08:55Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-vectorTabs.css]]
88878
css
text/css
/*.menu li{background-color: #F3F3F3;background-image: url(/w/skins/Vector/images/tab-normal-fade.png);background-position: 0% 100%;background-repeat: repeat-x;float: left;line-height: 1.125em !important;}
.menu{display:block !important;clear:none !important;}
.menu ul{position:static !important;margin-top:10px !important;}
#p-cactions h5{display:none;}
.menu li a{background-image: url(/w/skins/Vector/images/tab-break.png);background-position: 100% 100%;background-repeat: no-repeat;}
*/
#p-cactions {
display:none;
}
fsmgox7en3feoembh09cc0xuc4gqqsw
MediaWiki:Gadget-WiktCountryFlags.css
8
17939
88880
2026-08-06T09:42:57Z
en>Surjection
0
manually update Alemannic from 36px to 40px
88880
css
text/css
/*
* flags.css - adds flags to language headers.
* originally created by [[User:Prince Kassad]]
* this version is auto-generated from the list at [[Wiktionary:Language flags list]]
* ([[Special:Permalink/91668618]])
*/
.ns-0 h2 #Translingual::before, .ns-0 h2#Translingual::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6a/Earth_Day_Flag.png/40px-Earth_Day_Flag.png');}
.ns-0 h2 #English::before, .ns-0 h2#English::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0b/English_language.svg/40px-English_language.svg.png');}
.ns-0 h2 #\'Are\'are::before, .ns-0 h2#\'Are\'are::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/78/Flag_of_Guadalcanal.svg/40px-Flag_of_Guadalcanal.svg.png');}
.ns-0 h2 #Aari::before, .ns-0 h2#Aari::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8a/Flag_of_Southern_Ethiopia.svg/40px-Flag_of_Southern_Ethiopia.svg.png');}
.ns-0 h2 #Abau::before, .ns-0 h2#Abau::before, .ns-0 h2 #Krisa::before, .ns-0 h2#Krisa::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/de/Flag_of_Sandaun.svg/40px-Flag_of_Sandaun.svg.png');}
.ns-0 h2 #Abaza::before, .ns-0 h2#Abaza::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/27/Flag_of_Abazinia.svg/40px-Flag_of_Abazinia.svg.png');}
.ns-0 h2 #Abenaki::before, .ns-0 h2#Abenaki::before, .ns-0 h2 #Malecite-Passamaquoddy::before, .ns-0 h2#Malecite-Passamaquoddy::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/19/Wabanaki_Seal.png/40px-Wabanaki_Seal.png');}
.ns-0 h2 #Abkhaz::before, .ns-0 h2#Abkhaz::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7a/Flag_of_the_Republic_of_Abkhazia.svg/40px-Flag_of_the_Republic_of_Abkhazia.svg.png');}
.ns-0 h2 #Acehnese::before, .ns-0 h2#Acehnese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d8/Flag_of_Aceh_Sultanate.svg/40px-Flag_of_Aceh_Sultanate.svg.png');}
.ns-0 h2 #Acholi::before, .ns-0 h2#Acholi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/96/Flag_of_Acholi.svg/40px-Flag_of_Acholi.svg.png');}
.ns-0 h2 #Adzera::before, .ns-0 h2#Adzera::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a9/Flag_of_Morobe.png/40px-Flag_of_Morobe.png');}
.ns-0 h2 #Afar::before, .ns-0 h2#Afar::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/34/Flag_of_Djibouti.svg/40px-Flag_of_Djibouti.svg.png');}
.ns-0 h2 #Afrikaans::before, .ns-0 h2#Afrikaans::before, .ns-0 h2 #Fanagalo::before, .ns-0 h2#Fanagalo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/af/Flag_of_South_Africa.svg/40px-Flag_of_South_Africa.svg.png');}
.ns-0 h2 #Agutaynen::before, .ns-0 h2#Agutaynen::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/79/Flag_of_Agutaya,_Palawan.png/40px-Flag_of_Agutaya,_Palawan.png');}
.ns-0 h2 #Ahom::before, .ns-0 h2#Ahom::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8b/Ahom_insignia_plain.svg/40px-Ahom_insignia_plain.svg.png');}
.ns-0 h2 #Ainu::before, .ns-0 h2#Ainu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9b/Flag_of_Ainu.svg/40px-Flag_of_Ainu.svg.png');}
.ns-0 h2 #Akan::before, .ns-0 h2#Akan::before, .ns-0 h2 #Dagbani::before, .ns-0 h2#Dagbani::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/19/Flag_of_Ghana.svg/40px-Flag_of_Ghana.svg.png');}
.ns-0 h2 #Akha::before, .ns-0 h2#Akha::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5b/Flag_of_the_Akha_National_Development_Party.svg/40px-Flag_of_the_Akha_National_Development_Party.svg.png');}
.ns-0 h2 #Akkala_Sami::before, .ns-0 h2#Akkala_Sami::before, .ns-0 h2 #Inari_Sami::before, .ns-0 h2#Inari_Sami::before, .ns-0 h2 #Kemi_Sami::before, .ns-0 h2#Kemi_Sami::before, .ns-0 h2 #Kildin_Sami::before, .ns-0 h2#Kildin_Sami::before, .ns-0 h2 #Lule_Sami::before, .ns-0 h2#Lule_Sami::before, .ns-0 h2 #Northern_Sami::before, .ns-0 h2#Northern_Sami::before, .ns-0 h2 #Pite_Sami::before, .ns-0 h2#Pite_Sami::before, .ns-0 h2 #Skolt_Sami::before, .ns-0 h2#Skolt_Sami::before, .ns-0 h2 #Southern_Sami::before, .ns-0 h2#Southern_Sami::before, .ns-0 h2 #Ter_Sami::before, .ns-0 h2#Ter_Sami::before, .ns-0 h2 #Ume_Sami::before, .ns-0 h2#Ume_Sami::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1b/Sami_flag.svg/40px-Sami_flag.svg.png');}
.ns-0 h2 #Aklanon::before, .ns-0 h2#Aklanon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/90/PH-AKL_Flag.png/40px-PH-AKL_Flag.png');}
.ns-0 h2 #Albanian::before, .ns-0 h2#Albanian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/36/Flag_of_Albania.svg/40px-Flag_of_Albania.svg.png');}
.ns-0 h2 #Alemannic_German::before, .ns-0 h2#Alemannic_German::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/08/Flag_of_Switzerland_(Pantone).svg/40px-Flag_of_Switzerland_(Pantone).svg.png');}
.ns-0 h2 #Aleut::before, .ns-0 h2#Aleut::before, .ns-0 h2 #Inupiaq::before, .ns-0 h2#Inupiaq::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e6/Flag_of_Alaska.svg/40px-Flag_of_Alaska.svg.png');}
.ns-0 h2 #Algerian_Arabic::before, .ns-0 h2#Algerian_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/77/Flag_of_Algeria.svg/40px-Flag_of_Algeria.svg.png');}
.ns-0 h2 #Ambonese_Malay::before, .ns-0 h2#Ambonese_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c0/Lambang_Ambon.png/28px-Lambang_Ambon.png');}
.ns-0 h2 #American_Sign_Language::before, .ns-0 h2#American_Sign_Language::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a4/Flag_of_the_United_States.svg/40px-Flag_of_the_United_States.svg.png');}
.ns-0 h2 #Amharic::before, .ns-0 h2#Amharic::before, .ns-0 h2 #Argobba::before, .ns-0 h2#Argobba::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/71/Flag_of_Ethiopia.svg/40px-Flag_of_Ethiopia.svg.png');}
.ns-0 h2 #Amis::before, .ns-0 h2#Amis::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/28/Emblem_of_Hualien_County_2010.svg/37px-Emblem_of_Hualien_County_2010.svg.png');}
.ns-0 h2 #Ancient_Greek::before, .ns-0 h2#Ancient_Greek::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/58/Byzantine_imperial_flag,_14th_century,_square.svg/37px-Byzantine_imperial_flag,_14th_century,_square.svg.png');}
.ns-0 h2 #Andalusian_Arabic::before, .ns-0 h2#Andalusian_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3c/Green-White_Flag.svg/40px-Green-White_Flag.svg.png');}
.ns-0 h2 #Angevin::before, .ns-0 h2#Angevin::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/ad/Flag_of_Anjou.svg/40px-Flag_of_Anjou.svg.png');}
.ns-0 h2 #Angika::before, .ns-0 h2#Angika::before, .ns-0 h2 #Magahi::before, .ns-0 h2#Magahi::before, .ns-0 h2 #Maithili::before, .ns-0 h2#Maithili::before, .ns-0 h2 #Santali::before, .ns-0 h2#Santali::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a9/Jharkhand_Rajakiya_Chihna.svg/40px-Jharkhand_Rajakiya_Chihna.svg.png');}
.ns-0 h2 #Angolar::before, .ns-0 h2#Angolar::before, .ns-0 h2 #Sãotomense::before, .ns-0 h2#Sãotomense::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0a/Flag_of_São_Tomé_and_Príncipe.svg/40px-Flag_of_São_Tomé_and_Príncipe.svg.png');}
.ns-0 h2 #Anguthimri::before, .ns-0 h2#Anguthimri::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/04/Flag_of_Queensland.svg/40px-Flag_of_Queensland.svg.png');}
.ns-0 h2 #Annobonese::before, .ns-0 h2#Annobonese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/72/Bandera_Annobon.png/40px-Bandera_Annobon.png');}
.ns-0 h2 #Antigua_and_Barbuda_Creole_English::before, .ns-0 h2#Antigua_and_Barbuda_Creole_English::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/89/Flag_of_Antigua_and_Barbuda.svg/40px-Flag_of_Antigua_and_Barbuda.svg.png');}
.ns-0 h2 #Antillean_Creole::before, .ns-0 h2#Antillean_Creole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9f/Flag_of_Saint_Lucia.svg/40px-Flag_of_Saint_Lucia.svg.png');}
.ns-0 h2 #Ao::before, .ns-0 h2#Ao::before, .ns-0 h2 #Naga_Pidgin::before, .ns-0 h2#Naga_Pidgin::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Unofficial_flag_of_Nagaland.svg/40px-Unofficial_flag_of_Nagaland.svg.png');}
.ns-0 h2 #Arabic::before, .ns-0 h2#Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2b/Flag_of_the_Arab_League.svg/40px-Flag_of_the_Arab_League.svg.png');}
.ns-0 h2 #Aragonese::before, .ns-0 h2#Aragonese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/18/Flag_of_Aragon.svg/40px-Flag_of_Aragon.svg.png');}
.ns-0 h2 #Arapaho::before, .ns-0 h2#Arapaho::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/85/Flag_of_Arapaho_Nation.svg/40px-Flag_of_Arapaho_Nation.svg.png');}
.ns-0 h2 #Armenian::before, .ns-0 h2#Armenian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Flag_of_Armenia.svg/40px-Flag_of_Armenia.svg.png');}
.ns-0 h2 #Aromanian::before, .ns-0 h2#Aromanian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/97/Aromanian_flag.svg/40px-Aromanian_flag.svg.png');}
.ns-0 h2 #Asi::before, .ns-0 h2#Asi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/39/Flag_of_Banton,_Romblon.png/40px-Flag_of_Banton,_Romblon.png');}
.ns-0 h2 #Assamese::before, .ns-0 h2#Assamese::before, .ns-0 h2 #Bodo_\(India\)::before, .ns-0 h2#Bodo_\(India\)::before, .ns-0 h2 #Gujarati::before, .ns-0 h2#Gujarati::before, .ns-0 h2 #Hindi::before, .ns-0 h2#Hindi::before, .ns-0 h2 #Malayalam::before, .ns-0 h2#Malayalam::before, .ns-0 h2 #Marathi::before, .ns-0 h2#Marathi::before, .ns-0 h2 #Telugu::before, .ns-0 h2#Telugu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/41/Flag_of_India.svg/40px-Flag_of_India.svg.png');}
.ns-0 h2 #Assyrian_Neo-Aramaic::before, .ns-0 h2#Assyrian_Neo-Aramaic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/11/Flag_of_the_Assyrians_(gold_and_blue_Assur).svg/40px-Flag_of_the_Assyrians_(gold_and_blue_Assur).svg.png');}
.ns-0 h2 #Asturian::before, .ns-0 h2#Asturian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3e/Flag_of_Asturias.svg/40px-Flag_of_Asturias.svg.png');}
.ns-0 h2 #Atayal::before, .ns-0 h2#Atayal::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d0/Flag_of_New_Taipei_City.svg/40px-Flag_of_New_Taipei_City.svg.png');}
.ns-0 h2 #Atikamekw::before, .ns-0 h2#Atikamekw::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5f/Flag_of_Quebec.svg/40px-Flag_of_Quebec.svg.png');}
.ns-0 h2 #Aukan::before, .ns-0 h2#Aukan::before, .ns-0 h2 #Saramaccan::before, .ns-0 h2#Saramaccan::before, .ns-0 h2 #Sranan_Tongo::before, .ns-0 h2#Sranan_Tongo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/60/Flag_of_Suriname.svg/40px-Flag_of_Suriname.svg.png');}
.ns-0 h2 #Australian_Kriol::before, .ns-0 h2#Australian_Kriol::before, .ns-0 h2 #Torres_Strait_Creole::before, .ns-0 h2#Torres_Strait_Creole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3f/Australian_Aboriginal_Flag.svg/40px-Australian_Aboriginal_Flag.svg.png');}
.ns-0 h2 #Avar::before, .ns-0 h2#Avar::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/80/Flag_of_Avars.svg/40px-Flag_of_Avars.svg.png');}
.ns-0 h2 #Avestan::before, .ns-0 h2#Avestan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f9/Faravahar-Gold.svg/40px-Faravahar-Gold.svg.png');}
.ns-0 h2 #Awadhi::before, .ns-0 h2#Awadhi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f2/Flag_of_Awadh.svg/40px-Flag_of_Awadh.svg.png');}
.ns-0 h2 #Aymara::before, .ns-0 h2#Aymara::before, .ns-0 h2 #Bolivian_Sign_Language::before, .ns-0 h2#Bolivian_Sign_Language::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/de/Flag_of_Bolivia_(state).svg/40px-Flag_of_Bolivia_(state).svg.png');}
.ns-0 h2 #Azerbaijani::before, .ns-0 h2#Azerbaijani::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Flag_of_Azerbaijan.svg/40px-Flag_of_Azerbaijan.svg.png');}
.ns-0 h2 #Baba_Malay::before, .ns-0 h2#Baba_Malay::before, .ns-0 h2 #Singapore_Sign_Language::before, .ns-0 h2#Singapore_Sign_Language::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/48/Flag_of_Singapore.svg/40px-Flag_of_Singapore.svg.png');}
.ns-0 h2 #Bacanese_Malay::before, .ns-0 h2#Bacanese_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/08/Bendera_Sultan_Bacan.svg/40px-Bendera_Sultan_Bacan.svg.png');}
.ns-0 h2 #Badaga::before, .ns-0 h2#Badaga::before, .ns-0 h2 #Tamil::before, .ns-0 h2#Tamil::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7c/Seal_of_Tamil_Nadu.svg/40px-Seal_of_Tamil_Nadu.svg.png');}
.ns-0 h2 #Bagheli::before, .ns-0 h2#Bagheli::before, .ns-0 h2 #Bundeli::before, .ns-0 h2#Bundeli::before, .ns-0 h2 #Korku::before, .ns-0 h2#Korku::before, .ns-0 h2 #Malvi::before, .ns-0 h2#Malvi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/ae/Emblem_of_Madhya_Pradesh.svg/40px-Emblem_of_Madhya_Pradesh.svg.png');}
.ns-0 h2 #Baharna_Arabic::before, .ns-0 h2#Baharna_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Flag_of_Bahrain.svg/40px-Flag_of_Bahrain.svg.png');}
.ns-0 h2 #Bahnar::before, .ns-0 h2#Bahnar::before, .ns-0 h2 #Hrê::before, .ns-0 h2#Hrê::before, .ns-0 h2 #Vietnamese::before, .ns-0 h2#Vietnamese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/21/Flag_of_Vietnam.svg/40px-Flag_of_Vietnam.svg.png');}
.ns-0 h2 #Bajan::before, .ns-0 h2#Bajan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ef/Flag_of_Barbados.svg/40px-Flag_of_Barbados.svg.png');}
.ns-0 h2 #Balinese::before, .ns-0 h2#Balinese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/89/Historical_Flag_of_Bali.png/40px-Historical_Flag_of_Bali.png');}
.ns-0 h2 #Balinese_Malay::before, .ns-0 h2#Balinese_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/10/Coat_of_arms_of_Bali.svg/35px-Coat_of_arms_of_Bali.svg.png');}
.ns-0 h2 #Balti::before, .ns-0 h2#Balti::before, .ns-0 h2 #Burushaski::before, .ns-0 h2#Burushaski::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/62/Flag_of_Hunza.svg/40px-Flag_of_Hunza.svg.png');}
.ns-0 h2 #Baluchi::before, .ns-0 h2#Baluchi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0f/Flag_of_Balochistan.svg/40px-Flag_of_Balochistan.svg.png');}
.ns-0 h2 #Bambara::before, .ns-0 h2#Bambara::before, .ns-0 h2 #Soninke::before, .ns-0 h2#Soninke::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/92/Flag_of_Mali.svg/40px-Flag_of_Mali.svg.png');}
.ns-0 h2 #Banjarese::before, .ns-0 h2#Banjarese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a5/Banjar_Sultanate_Flag.svg/40px-Banjar_Sultanate_Flag.svg.png');}
.ns-0 h2 #Baoule::before, .ns-0 h2#Baoule::before, .ns-0 h2 #Dyula::before, .ns-0 h2#Dyula::before{padding-right:5px;
content:url("//upload.wikimedia.org/wikipedia/commons/thumb/f/fe/Flag_of_Côte_d'Ivoire.svg/40px-Flag_of_Côte_d'Ivoire.svg.png");}
.ns-0 h2 #Bari::before, .ns-0 h2#Bari::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d2/Flag_of_the_Bari_people.svg/40px-Flag_of_the_Bari_people.svg.png');}
.ns-0 h2 #Barikanchi::before, .ns-0 h2#Barikanchi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2b/Flag_of_the_Nigerian_Armed_Forces.svg/40px-Flag_of_the_Nigerian_Armed_Forces.svg.png');}
.ns-0 h2 #Bashkir::before, .ns-0 h2#Bashkir::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3d/Flag_of_Bashkortostan.svg/40px-Flag_of_Bashkortostan.svg.png');}
.ns-0 h2 #Basque::before, .ns-0 h2#Basque::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2d/Flag_of_the_Basque_Country.svg/40px-Flag_of_the_Basque_Country.svg.png');}
.ns-0 h2 #Bavarian::before, .ns-0 h2#Bavarian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/20/Flag_of_Bavaria_(lozengy).svg/40px-Flag_of_Bavaria_(lozengy).svg.png');}
.ns-0 h2 #Beja::before, .ns-0 h2#Beja::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6e/Flag_of_the_Beja_Congress.svg/40px-Flag_of_the_Beja_Congress.svg.png');}
.ns-0 h2 #Belarusian::before, .ns-0 h2#Belarusian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/85/Flag_of_Belarus.svg/40px-Flag_of_Belarus.svg.png');}
.ns-0 h2 #Belizean_Creole::before, .ns-0 h2#Belizean_Creole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e7/Flag_of_Belize.svg/40px-Flag_of_Belize.svg.png');}
.ns-0 h2 #Bella_Coola::before, .ns-0 h2#Bella_Coola::before, .ns-0 h2 #Halkomelem::before, .ns-0 h2#Halkomelem::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b8/Flag_of_British_Columbia.svg/40px-Flag_of_British_Columbia.svg.png');}
.ns-0 h2 #Bemba::before, .ns-0 h2#Bemba::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/06/Flag_of_Zambia.svg/40px-Flag_of_Zambia.svg.png');}
.ns-0 h2 #Bengali::before, .ns-0 h2#Bengali::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/80/Bengali_language.svg/40px-Bengali_language.svg.png');}
.ns-0 h2 #Berau_Malay::before, .ns-0 h2#Berau_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7f/East_Borneo_Sultanate_Flags.png/40px-East_Borneo_Sultanate_Flags.png');}
.ns-0 h2 #Berbice_Creole_Dutch::before, .ns-0 h2#Berbice_Creole_Dutch::before, .ns-0 h2 #Skepi_Creole_Dutch::before, .ns-0 h2#Skepi_Creole_Dutch::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/41/Flag_of_the_Dutch_West_India_Company.svg/40px-Flag_of_the_Dutch_West_India_Company.svg.png');}
.ns-0 h2 #Betawi::before, .ns-0 h2#Betawi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/eb/Coat_of_arms_of_Jakarta.svg/30px-Coat_of_arms_of_Jakarta.svg.png');}
.ns-0 h2 #Bhojpuri::before, .ns-0 h2#Bhojpuri::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Seal_of_Uttar_Pradesh.svg/40px-Seal_of_Uttar_Pradesh.svg.png');}
.ns-0 h2 #Bihari::before, .ns-0 h2#Bihari::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e9/Seal_of_Bihar.svg/40px-Seal_of_Bihar.svg.png');}
.ns-0 h2 #Bima::before, .ns-0 h2#Bima::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6d/Bendera_Kesultanan_Bima.png/40px-Bendera_Kesultanan_Bima.png');}
.ns-0 h2 #Binukid::before, .ns-0 h2#Binukid::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c8/Flag_of_the_Province_of_Bukidnon.svg/40px-Flag_of_the_Province_of_Bukidnon.svg.png');}
.ns-0 h2 #Bislama::before, .ns-0 h2#Bislama::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Flag_of_Vanuatu.svg/40px-Flag_of_Vanuatu.svg.png');}
.ns-0 h2 #Blackfoot::before, .ns-0 h2#Blackfoot::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8e/Flag_of_the_Blackfoot_Confederacy.jpg/40px-Flag_of_the_Blackfoot_Confederacy.jpg');}
.ns-0 h2 #Boon::before, .ns-0 h2#Boon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a6/Flag_of_Jubaland_(Somalia).svg/40px-Flag_of_Jubaland_(Somalia).svg.png');}
.ns-0 h2 #Bourbonnais-Berrichon::before, .ns-0 h2#Bourbonnais-Berrichon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f9/Flag_of_Bourbonnais.svg/40px-Flag_of_Bourbonnais.svg.png');}
.ns-0 h2 #Bourguignon::before, .ns-0 h2#Bourguignon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3c/Flag_of_Bourgogne.svg/40px-Flag_of_Bourgogne.svg.png');}
.ns-0 h2 #Brahui::before, .ns-0 h2#Brahui::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/59/FlagofKalat.svg/40px-FlagofKalat.svg.png');}
.ns-0 h2 #Brazilian_Sign_Language::before, .ns-0 h2#Brazilian_Sign_Language::before, .ns-0 h2 #Old_Tupi::before, .ns-0 h2#Old_Tupi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/05/Flag_of_Brazil.svg/40px-Flag_of_Brazil.svg.png');}
.ns-0 h2 #Breton::before, .ns-0 h2#Breton::before, .ns-0 h2 #Gallo::before, .ns-0 h2#Gallo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7d/Flag_of_Brittany.svg/40px-Flag_of_Brittany.svg.png');}
.ns-0 h2 #Brokkat::before, .ns-0 h2#Brokkat::before, .ns-0 h2 #Bumthangkha::before, .ns-0 h2#Bumthangkha::before, .ns-0 h2 #Chocangaca::before, .ns-0 h2#Chocangaca::before, .ns-0 h2 #Dzala::before, .ns-0 h2#Dzala::before, .ns-0 h2 #Dzongkha::before, .ns-0 h2#Dzongkha::before, .ns-0 h2 #Kurtöp::before, .ns-0 h2#Kurtöp::before, .ns-0 h2 #Tshangla::before, .ns-0 h2#Tshangla::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/91/Flag_of_Bhutan.svg/40px-Flag_of_Bhutan.svg.png');}
.ns-0 h2 #Brooke\'s_Point_Palawano::before, .ns-0 h2#Brooke\'s_Point_Palawano::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a8/Flag_of_Palawan,_Philippines.svg/40px-Flag_of_Palawan,_Philippines.svg.png');}
.ns-0 h2 #Brunei_Malay::before, .ns-0 h2#Brunei_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9c/Flag_of_Brunei.svg/40px-Flag_of_Brunei.svg.png');}
.ns-0 h2 #Bulgarian::before, .ns-0 h2#Bulgarian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9a/Flag_of_Bulgaria.svg/40px-Flag_of_Bulgaria.svg.png');}
.ns-0 h2 #Bunun::before, .ns-0 h2#Bunun::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/89/Flag_of_Nantou_County.svg/40px-Flag_of_Nantou_County.svg.png');}
.ns-0 h2 #Burmese::before, .ns-0 h2#Burmese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8c/Flag_of_Myanmar.svg/40px-Flag_of_Myanmar.svg.png');}
.ns-0 h2 #Buryat::before, .ns-0 h2#Buryat::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/68/Flag_of_Buryatia.svg/40px-Flag_of_Buryatia.svg.png');}
.ns-0 h2 #Butuanon::before, .ns-0 h2#Butuanon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7b/Butuancityflag.jpg/40px-Butuancityflag.jpg');}
.ns-0 h2 #Cahuilla::before, .ns-0 h2#Cahuilla::before, .ns-0 h2 #Northern_Valley_Yokuts::before, .ns-0 h2#Northern_Valley_Yokuts::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/01/Flag_of_California.svg/40px-Flag_of_California.svg.png');}
.ns-0 h2 #Calabrian_Greek::before, .ns-0 h2#Calabrian_Greek::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8b/Flag_of_Calabria.svg/40px-Flag_of_Calabria.svg.png');}
.ns-0 h2 #Caluyanun::before, .ns-0 h2#Caluyanun::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/28/Flag_of_Caluya,_Antique.png/40px-Flag_of_Caluya,_Antique.png');}
.ns-0 h2 #Caló::before, .ns-0 h2#Caló::before, .ns-0 h2 #Romani::before, .ns-0 h2#Romani::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/10/Flag_of_the_Romani_people.svg/40px-Flag_of_the_Romani_people.svg.png');}
.ns-0 h2 #Cameroon_Pidgin::before, .ns-0 h2#Cameroon_Pidgin::before, .ns-0 h2 #Ghomala\'::before, .ns-0 h2#Ghomala\'::before, .ns-0 h2 #Matal::before, .ns-0 h2#Matal::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4f/Flag_of_Cameroon.svg/40px-Flag_of_Cameroon.svg.png');}
.ns-0 h2 #Cantonese::before, .ns-0 h2#Cantonese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5b/Flag_of_Hong_Kong.svg/40px-Flag_of_Hong_Kong.svg.png');}
.ns-0 h2 #Caribbean_Hindustani::before, .ns-0 h2#Caribbean_Hindustani::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/21/Flag_of_Suriname_and_Guyana.png/40px-Flag_of_Suriname_and_Guyana.png');}
.ns-0 h2 #Carolinian::before, .ns-0 h2#Carolinian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e0/Flag_of_the_Northern_Mariana_Islands.svg/40px-Flag_of_the_Northern_Mariana_Islands.svg.png');}
.ns-0 h2 #Carpathian_Rusyn::before, .ns-0 h2#Carpathian_Rusyn::before, .ns-0 h2 #Pannonian_Rusyn::before, .ns-0 h2#Pannonian_Rusyn::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/27/Flag_of_Rusyns_2007.svg/40px-Flag_of_Rusyns_2007.svg.png');}
.ns-0 h2 #Catalan::before, .ns-0 h2#Catalan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/ce/Flag_of_Catalonia.svg/40px-Flag_of_Catalonia.svg.png');}
.ns-0 h2 #Cavineña::before, .ns-0 h2#Cavineña::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/70/Flag_of_Beni.svg/40px-Flag_of_Beni.svg.png');}
.ns-0 h2 #Cebuano::before, .ns-0 h2#Cebuano::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/11/Flag_of_Cebu_(province).svg/40px-Flag_of_Cebu_(province).svg.png');}
.ns-0 h2 #Central_Atlas_Tamazight::before, .ns-0 h2#Central_Atlas_Tamazight::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Berber_flag.svg/40px-Berber_flag.svg.png');}
.ns-0 h2 #Central_Bikol::before, .ns-0 h2#Central_Bikol::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6e/Flag_of_the_Katipuneros_of_Bicol.svg/40px-Flag_of_the_Katipuneros_of_Bicol.svg.png');}
.ns-0 h2 #Central_Dusun::before, .ns-0 h2#Central_Dusun::before, .ns-0 h2 #Sabah_Malay::before, .ns-0 h2#Sabah_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b5/Flag_of_Sabah.svg/40px-Flag_of_Sabah.svg.png');}
.ns-0 h2 #Central_Kurdish::before, .ns-0 h2#Central_Kurdish::before, .ns-0 h2 #Northern_Kurdish::before, .ns-0 h2#Northern_Kurdish::before, .ns-0 h2 #Southern_Kurdish::before, .ns-0 h2#Southern_Kurdish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/35/Flag_of_Kurdistan.svg/40px-Flag_of_Kurdistan.svg.png');}
.ns-0 h2 #Central_Mansi::before, .ns-0 h2#Central_Mansi::before, .ns-0 h2 #Eastern_Khanty::before, .ns-0 h2#Eastern_Khanty::before, .ns-0 h2 #Northern_Khanty::before, .ns-0 h2#Northern_Khanty::before, .ns-0 h2 #Northern_Mansi::before, .ns-0 h2#Northern_Mansi::before, .ns-0 h2 #Southern_Khanty::before, .ns-0 h2#Southern_Khanty::before, .ns-0 h2 #Southern_Mansi::before, .ns-0 h2#Southern_Mansi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/70/Flag_of_Yugra.svg/40px-Flag_of_Yugra.svg.png');}
.ns-0 h2 #Central_Nahuatl::before, .ns-0 h2#Central_Nahuatl::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/26/Flag_of_Tlaxcala.svg/40px-Flag_of_Tlaxcala.svg.png');}
.ns-0 h2 #Chadian_Arabic::before, .ns-0 h2#Chadian_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4b/Flag_of_Chad.svg/40px-Flag_of_Chad.svg.png');}
.ns-0 h2 #Chakma::before, .ns-0 h2#Chakma::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d7/Chakma_Flag.jpg/40px-Chakma_Flag.jpg');}
.ns-0 h2 #Chaldean_Neo-Aramaic::before, .ns-0 h2#Chaldean_Neo-Aramaic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/95/Chaldean_flag.svg/40px-Chaldean_flag.svg.png');}
.ns-0 h2 #Chamorro::before, .ns-0 h2#Chamorro::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/07/Flag_of_Guam.svg/40px-Flag_of_Guam.svg.png');}
.ns-0 h2 #Champenois::before, .ns-0 h2#Champenois::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f1/Flag_of_Champagne-Ardenne.svg/40px-Flag_of_Champagne-Ardenne.svg.png');}
.ns-0 h2 #Chavacano::before, .ns-0 h2#Chavacano::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/16/Flag_of_Zamboanga.jpg/40px-Flag_of_Zamboanga.jpg');}
.ns-0 h2 #Chechen::before, .ns-0 h2#Chechen::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/13/Flag_of_the_Chechen_Republic.svg/40px-Flag_of_the_Chechen_Republic.svg.png');}
.ns-0 h2 #Cherokee::before, .ns-0 h2#Cherokee::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Flag_of_the_Cherokee_Nation.svg/40px-Flag_of_the_Cherokee_Nation.svg.png');}
.ns-0 h2 #Cheyenne::before, .ns-0 h2#Cheyenne::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e7/Flag_of_Northern_Cheyenne.svg/40px-Flag_of_Northern_Cheyenne.svg.png');}
.ns-0 h2 #Chhattisgarhi::before, .ns-0 h2#Chhattisgarhi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/87/Coat_of_arms_of_Chhattisgarh.svg/40px-Coat_of_arms_of_Chhattisgarh.svg.png');}
.ns-0 h2 #Chibcha::before, .ns-0 h2#Chibcha::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8d/Bandera_de_la_confederacion_muisca.png/33px-Bandera_de_la_confederacion_muisca.png');}
.ns-0 h2 #Chichewa::before, .ns-0 h2#Chichewa::before, .ns-0 h2 #Tumbuka::before, .ns-0 h2#Tumbuka::before, .ns-0 h2 #Yao_\(Africa\)::before, .ns-0 h2#Yao_\(Africa\)::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Flag_of_Malawi.svg/40px-Flag_of_Malawi.svg.png');}
.ns-0 h2 #Chickasaw::before, .ns-0 h2#Chickasaw::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/74/Flag_of_the_Chickasaw_Nation.svg/40px-Flag_of_the_Chickasaw_Nation.svg.png');}
.ns-0 h2 #Chimwiini::before, .ns-0 h2#Chimwiini::before, .ns-0 h2 #Maay::before, .ns-0 h2#Maay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8b/Flag_of_South_West_State_of_Somalia.svg/40px-Flag_of_South_West_State_of_Somalia.svg.png');}
.ns-0 h2 #Chinese::before, .ns-0 h2#Chinese::before, .ns-0 h2 #Chinese_Pidgin_English::before, .ns-0 h2#Chinese_Pidgin_English::before, .ns-0 h2 #Mandarin::before, .ns-0 h2#Mandarin::before{padding-right:5px;
content:url("//upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Flag_of_the_People's_Republic_of_China.svg/40px-Flag_of_the_People's_Republic_of_China.svg.png");}
.ns-0 h2 #Chittagonian::before, .ns-0 h2#Chittagonian::before, .ns-0 h2 #Sylheti::before, .ns-0 h2#Sylheti::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f9/Flag_of_Bangladesh.svg/40px-Flag_of_Bangladesh.svg.png');}
.ns-0 h2 #Choctaw::before, .ns-0 h2#Choctaw::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a6/Choctaw_flag.svg/40px-Choctaw_flag.svg.png');}
.ns-0 h2 #Chukchi::before, .ns-0 h2#Chukchi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a0/Flag_of_Chukotka.svg/40px-Flag_of_Chukotka.svg.png');}
.ns-0 h2 #Church_Slavonic::before, .ns-0 h2#Church_Slavonic::before, .ns-0 h2 #Old_Church_Slavonic::before, .ns-0 h2#Old_Church_Slavonic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8e/Bulgarian_Orthodox_Cross_6.svg/33px-Bulgarian_Orthodox_Cross_6.svg.png');}
.ns-0 h2 #Chuukese::before, .ns-0 h2#Chuukese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/55/Flag_of_Chuuk.svg/40px-Flag_of_Chuuk.svg.png');}
.ns-0 h2 #Chuvash::before, .ns-0 h2#Chuvash::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d7/Flag_of_Chuvashia.svg/40px-Flag_of_Chuvashia.svg.png');}
.ns-0 h2 #Cimbrian::before, .ns-0 h2#Cimbrian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/09/Flag_of_the_Province_of_Trento.svg/40px-Flag_of_the_Province_of_Trento.svg.png');}
.ns-0 h2 #Classical_Nahuatl::before, .ns-0 h2#Classical_Nahuatl::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/87/Aztec_Triple_Alliance.png/40px-Aztec_Triple_Alliance.png');}
.ns-0 h2 #Coptic::before, .ns-0 h2#Coptic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/71/Coptic_cross.svg/35px-Coptic_cross.svg.png');}
.ns-0 h2 #Cornish::before, .ns-0 h2#Cornish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b8/Flag_of_Cornwall.svg/40px-Flag_of_Cornwall.svg.png');}
.ns-0 h2 #Corsican::before, .ns-0 h2#Corsican::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7c/Flag_of_Corsica.svg/40px-Flag_of_Corsica.svg.png');}
.ns-0 h2 #Cree::before, .ns-0 h2#Cree::before, .ns-0 h2 #Dogrib::before, .ns-0 h2#Dogrib::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c1/Flag_of_the_Northwest_Territories.svg/40px-Flag_of_the_Northwest_Territories.svg.png');}
.ns-0 h2 #Creek::before, .ns-0 h2#Creek::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d4/State_of_Muskogee_(Florida,_1799-1803).svg/40px-State_of_Muskogee_(Florida,_1799-1803).svg.png');}
.ns-0 h2 #Crimean_Tatar::before, .ns-0 h2#Crimean_Tatar::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/08/Flag_of_the_Crimean_Tatar_people.svg/40px-Flag_of_the_Crimean_Tatar_people.svg.png');}
.ns-0 h2 #Croatian_Sign_Language::before, .ns-0 h2#Croatian_Sign_Language::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1b/Flag_of_Croatia.svg/40px-Flag_of_Croatia.svg.png');}
.ns-0 h2 #Cuban_Sign_Language::before, .ns-0 h2#Cuban_Sign_Language::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/bd/Flag_of_Cuba.svg/40px-Flag_of_Cuba.svg.png');}
.ns-0 h2 #Cubeo::before, .ns-0 h2#Cubeo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c0/Flag_of_Vaupés.svg/40px-Flag_of_Vaupés.svg.png');}
.ns-0 h2 #Cuyunon::before, .ns-0 h2#Cuyunon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Flag_of_Cuyo,_Palawan.png/40px-Flag_of_Cuyo,_Palawan.png');}
.ns-0 h2 #Cypriot_Arabic::before, .ns-0 h2#Cypriot_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d4/Flag_of_Cyprus.svg/40px-Flag_of_Cyprus.svg.png');}
.ns-0 h2 #Czech::before, .ns-0 h2#Czech::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/cb/Flag_of_the_Czech_Republic.svg/40px-Flag_of_the_Czech_Republic.svg.png');}
.ns-0 h2 #Dahalik::before, .ns-0 h2#Dahalik::before, .ns-0 h2 #Tigre::before, .ns-0 h2#Tigre::before, .ns-0 h2 #Tigrinya::before, .ns-0 h2#Tigrinya::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/29/Flag_of_Eritrea.svg/40px-Flag_of_Eritrea.svg.png');}
.ns-0 h2 #Dakota::before, .ns-0 h2#Dakota::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ee/Flag_of_North_Dakota.svg/40px-Flag_of_North_Dakota.svg.png');}
.ns-0 h2 #Dalmatian::before, .ns-0 h2#Dalmatian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Flag_of_Kingdom_of_Dalmatia_(1852-1860).svg/40px-Flag_of_Kingdom_of_Dalmatia_(1852-1860).svg.png');}
.ns-0 h2 #Danish::before, .ns-0 h2#Danish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9c/Flag_of_Denmark.svg/40px-Flag_of_Denmark.svg.png');}
.ns-0 h2 #Dhivehi::before, .ns-0 h2#Dhivehi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0f/Flag_of_Maldives.svg/40px-Flag_of_Maldives.svg.png');}
.ns-0 h2 #Dhofari_Arabic::before, .ns-0 h2#Dhofari_Arabic::before, .ns-0 h2 #Omani_Arabic::before, .ns-0 h2#Omani_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Flag_of_Oman.svg/40px-Flag_of_Oman.svg.png');}
.ns-0 h2 #Dinka::before, .ns-0 h2#Dinka::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1d/Flag_of_Dinka_people.svg/40px-Flag_of_Dinka_people.svg.png');}
.ns-0 h2 #Dutch::before, .ns-0 h2#Dutch::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/20/Flag_of_the_Netherlands.svg/40px-Flag_of_the_Netherlands.svg.png');}
.ns-0 h2 #East_Circassian::before, .ns-0 h2#East_Circassian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d8/Flag_of_Kabardino-Balkaria.svg/40px-Flag_of_Kabardino-Balkaria.svg.png');}
.ns-0 h2 #East_Futuna::before, .ns-0 h2#East_Futuna::before, .ns-0 h2 #Wallisian::before, .ns-0 h2#Wallisian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d2/Flag_of_Wallis_and_Futuna.svg/40px-Flag_of_Wallis_and_Futuna.svg.png');}
.ns-0 h2 #Eastern_Durango_Nahuatl::before, .ns-0 h2#Eastern_Durango_Nahuatl::before, .ns-0 h2 #Western_Durango_Nahuatl::before, .ns-0 h2#Western_Durango_Nahuatl::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4b/Coat_of_arms_of_Durango.svg/35px-Coat_of_arms_of_Durango.svg.png');}
.ns-0 h2 #Eastern_Maninkakan::before, .ns-0 h2#Eastern_Maninkakan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ed/Flag_of_Guinea.svg/40px-Flag_of_Guinea.svg.png');}
.ns-0 h2 #Eastern_Mari::before, .ns-0 h2#Eastern_Mari::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a7/Flag_of_Mari_El.svg/40px-Flag_of_Mari_El.svg.png');}
.ns-0 h2 #Edo::before, .ns-0 h2#Edo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Edo_State_Flag.png/40px-Edo_State_Flag.png');}
.ns-0 h2 #Egyptian::before, .ns-0 h2#Egyptian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1c/Ancient_Egyptian_Culture_Flag.webp/40px-Ancient_Egyptian_Culture_Flag.webp');}
.ns-0 h2 #Egyptian_Arabic::before, .ns-0 h2#Egyptian_Arabic::before, .ns-0 h2 #Nobiin::before, .ns-0 h2#Nobiin::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fe/Flag_of_Egypt.svg/40px-Flag_of_Egypt.svg.png');}
.ns-0 h2 #Elfdalian::before, .ns-0 h2#Elfdalian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/ff/Älvdalen_vapen.svg/33px-Älvdalen_vapen.svg.png');}
.ns-0 h2 #Emilian::before, .ns-0 h2#Emilian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1f/Flag_of_Bologna_2.png/40px-Flag_of_Bologna_2.png');}
.ns-0 h2 #Erzya::before, .ns-0 h2#Erzya::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/28/Erzya_Flag.svg/40px-Erzya_Flag.svg.png');}
.ns-0 h2 #Ese::before, .ns-0 h2#Ese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5d/Flag_of_Flag_Oro_new.png/40px-Flag_of_Flag_Oro_new.png');}
.ns-0 h2 #Esperanto::before, .ns-0 h2#Esperanto::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f5/Flag_of_Esperanto.svg/40px-Flag_of_Esperanto.svg.png');}
.ns-0 h2 #Estonian::before, .ns-0 h2#Estonian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8f/Flag_of_Estonia.svg/40px-Flag_of_Estonia.svg.png');}
.ns-0 h2 #Evenki::before, .ns-0 h2#Evenki::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/72/Flag_of_Evenks.svg/40px-Flag_of_Evenks.svg.png');}
.ns-0 h2 #Ewe::before, .ns-0 h2#Ewe::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b8/Flag_of_the_Ewe_people.svg/40px-Flag_of_the_Ewe_people.svg.png');}
.ns-0 h2 #Extremaduran::before, .ns-0 h2#Extremaduran::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/13/Flag_of_Extremadura,_Spain_(with_coat_of_arms).svg/40px-Flag_of_Extremadura,_Spain_(with_coat_of_arms).svg.png');}
.ns-0 h2 #Fala::before, .ns-0 h2#Fala::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b7/Bandera_de_Valverde_del_Fresno_(Cáceres).svg/40px-Bandera_de_Valverde_del_Fresno_(Cáceres).svg.png');}
.ns-0 h2 #Fang_\(Bantu\)::before, .ns-0 h2#Fang_\(Bantu\)::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/04/Flag_of_Gabon.svg/40px-Flag_of_Gabon.svg.png');}
.ns-0 h2 #Faroese::before, .ns-0 h2#Faroese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3c/Flag_of_the_Faroe_Islands.svg/40px-Flag_of_the_Faroe_Islands.svg.png');}
.ns-0 h2 #Fiji_Hindi::before, .ns-0 h2#Fiji_Hindi::before, .ns-0 h2 #Fijian::before, .ns-0 h2#Fijian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/ba/Flag_of_Fiji.svg/40px-Flag_of_Fiji.svg.png');}
.ns-0 h2 #Fingallian::before, .ns-0 h2#Fingallian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/22/Flag_of_County_Limerick.svg/40px-Flag_of_County_Limerick.svg.png');}
.ns-0 h2 #Finnish::before, .ns-0 h2#Finnish::before, .ns-0 h2 #Finnish_Sign_Language::before, .ns-0 h2#Finnish_Sign_Language::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Flag_of_Finland.svg/40px-Flag_of_Finland.svg.png');}
.ns-0 h2 #Fon::before, .ns-0 h2#Fon::before, .ns-0 h2 #Gun::before, .ns-0 h2#Gun::before, .ns-0 h2 #Ifè::before, .ns-0 h2#Ifè::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0a/Flag_of_Benin.svg/40px-Flag_of_Benin.svg.png');}
.ns-0 h2 #Forest_Nenets::before, .ns-0 h2#Forest_Nenets::before, .ns-0 h2 #Tundra_Nenets::before, .ns-0 h2#Tundra_Nenets::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/15/Flag_of_Nenets_Autonomous_District.svg/40px-Flag_of_Nenets_Autonomous_District.svg.png');}
.ns-0 h2 #Franc-Comtois::before, .ns-0 h2#Franc-Comtois::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/96/Flag_of_Franche-Comté.svg/40px-Flag_of_Franche-Comté.svg.png');}
.ns-0 h2 #Franco-Provençal::before, .ns-0 h2#Franco-Provençal::before{padding-right:5px;
content:url("//upload.wikimedia.org/wikipedia/commons/thumb/9/90/Flag_of_Valle_d'Aosta.svg/40px-Flag_of_Valle_d'Aosta.svg.png");}
.ns-0 h2 #French::before, .ns-0 h2#French::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Flag_of_France.svg/40px-Flag_of_France.svg.png');}
.ns-0 h2 #Friulian::before, .ns-0 h2#Friulian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/87/Bandiere_dal_Friûl.svg/40px-Bandiere_dal_Friûl.svg.png');}
.ns-0 h2 #Fula::before, .ns-0 h2#Fula::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/08/NE-CM-BF-GM_flag.svg/40px-NE-CM-BF-GM_flag.svg.png');}
.ns-0 h2 #Fur::before, .ns-0 h2#Fur::before, .ns-0 h2 #Sudanese_Arabic::before, .ns-0 h2#Sudanese_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/01/Flag_of_Sudan.svg/40px-Flag_of_Sudan.svg.png');}
.ns-0 h2 #Futuna-Aniwa::before, .ns-0 h2#Futuna-Aniwa::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/55/Tafea_Province_Flag.svg/40px-Tafea_Province_Flag.svg.png');}
.ns-0 h2 #Fyam::before, .ns-0 h2#Fyam::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/00/Plateau_State_Flag.jpg/40px-Plateau_State_Flag.jpg');}
.ns-0 h2 #Ga::before, .ns-0 h2#Ga::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/cd/Flag_of_Accra.svg/40px-Flag_of_Accra.svg.png');}
.ns-0 h2 #Gabadi::before, .ns-0 h2#Gabadi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/84/Flag_of_Central_Province,_Papua_New_Guinea.svg/40px-Flag_of_Central_Province,_Papua_New_Guinea.svg.png');}
.ns-0 h2 #Gagauz::before, .ns-0 h2#Gagauz::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/69/Flag_of_Gagauzia.svg/40px-Flag_of_Gagauzia.svg.png');}
.ns-0 h2 #Galician::before, .ns-0 h2#Galician::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/64/Flag_of_Galicia.svg/40px-Flag_of_Galicia.svg.png');}
.ns-0 h2 #Gallo-Italic_of_Basilicata::before, .ns-0 h2#Gallo-Italic_of_Basilicata::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/00/Flag_for_the_Lombards_of_Basilicata.png/40px-Flag_for_the_Lombards_of_Basilicata.png');}
.ns-0 h2 #Gallo-Italic_of_Sicily::before, .ns-0 h2#Gallo-Italic_of_Sicily::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/30/Flag_for_the_Lombards_of_Sicily.png/40px-Flag_for_the_Lombards_of_Sicily.png');}
.ns-0 h2 #Gamilaraay::before, .ns-0 h2#Gamilaraay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/00/Flag_of_New_South_Wales.svg/40px-Flag_of_New_South_Wales.svg.png');}
.ns-0 h2 #Garhwali::before, .ns-0 h2#Garhwali::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7d/Flag_of_the_Princely_State_of_Tehri_Garhwal.svg/40px-Flag_of_the_Princely_State_of_Tehri_Garhwal.svg.png');}
.ns-0 h2 #Garifuna::before, .ns-0 h2#Garifuna::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b1/Flag_of_Garifuna.svg/40px-Flag_of_Garifuna.svg.png');}
.ns-0 h2 #Garo::before, .ns-0 h2#Garo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/81/Garo_hills.png/40px-Garo_hills.png');}
.ns-0 h2 #Ge\'ez::before, .ns-0 h2#Ge\'ez::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/08/Lion_of_Judah_emblem_of_the_Ethiopian_Empire.svg/40px-Lion_of_Judah_emblem_of_the_Ethiopian_Empire.svg.png');}
.ns-0 h2 #Georgian::before, .ns-0 h2#Georgian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0f/Flag_of_Georgia.svg/40px-Flag_of_Georgia.svg.png');}
.ns-0 h2 #German::before, .ns-0 h2#German::before, .ns-0 h2 #German_Sign_Language::before, .ns-0 h2#German_Sign_Language::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/ba/Flag_of_Germany.svg/40px-Flag_of_Germany.svg.png');}
.ns-0 h2 #Ghadames::before, .ns-0 h2#Ghadames::before, .ns-0 h2 #Libyan_Arabic::before, .ns-0 h2#Libyan_Arabic::before, .ns-0 h2 #Nefusa::before, .ns-0 h2#Nefusa::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/05/Flag_of_Libya.svg/40px-Flag_of_Libya.svg.png');}
.ns-0 h2 #Gilbertese::before, .ns-0 h2#Gilbertese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d3/Flag_of_Kiribati.svg/40px-Flag_of_Kiribati.svg.png');}
.ns-0 h2 #Gorontalo::before, .ns-0 h2#Gorontalo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/01/Coat_of_arms_of_Gorontalo.svg/35px-Coat_of_arms_of_Gorontalo.svg.png');}
.ns-0 h2 #Greek::before, .ns-0 h2#Greek::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5c/Flag_of_Greece.svg/40px-Flag_of_Greece.svg.png');}
.ns-0 h2 #Green_Hmong::before, .ns-0 h2#Green_Hmong::before, .ns-0 h2 #White_Hmong::before, .ns-0 h2#White_Hmong::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0f/Hmong_Flag_(UNPO).svg/40px-Hmong_Flag_(UNPO).svg.png');}
.ns-0 h2 #Greenlandic::before, .ns-0 h2#Greenlandic::before, .ns-0 h2 #West_Greenlandic_Pidgin::before, .ns-0 h2#West_Greenlandic_Pidgin::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/09/Flag_of_Greenland.svg/40px-Flag_of_Greenland.svg.png');}
.ns-0 h2 #Grenadian_Creole_English::before, .ns-0 h2#Grenadian_Creole_English::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Flag_of_Grenada.svg/40px-Flag_of_Grenada.svg.png');}
.ns-0 h2 #Guajajára::before, .ns-0 h2#Guajajára::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/45/Bandeira_do_Maranhão.svg/40px-Bandeira_do_Maranhão.svg.png');}
.ns-0 h2 #Guanche::before, .ns-0 h2#Guanche::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b0/Flag_of_the_Canary_Islands.svg/40px-Flag_of_the_Canary_Islands.svg.png');}
.ns-0 h2 #Guerrero_Amuzgo::before, .ns-0 h2#Guerrero_Amuzgo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b8/Flag_of_Guerrero.svg/40px-Flag_of_Guerrero.svg.png');}
.ns-0 h2 #Guianese_Creole::before, .ns-0 h2#Guianese_Creole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/29/Flag_of_French_Guiana.svg/40px-Flag_of_French_Guiana.svg.png');}
.ns-0 h2 #Guinea-Bissau_Creole::before, .ns-0 h2#Guinea-Bissau_Creole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/01/Flag_of_Guinea-Bissau.svg/40px-Flag_of_Guinea-Bissau.svg.png');}
.ns-0 h2 #Gulf_Arabic::before, .ns-0 h2#Gulf_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/cb/Flag_of_the_United_Arab_Emirates.svg/40px-Flag_of_the_United_Arab_Emirates.svg.png');}
.ns-0 h2 #Gullah::before, .ns-0 h2#Gullah::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/ff/Black_American_Heritage_flag.svg/40px-Black_American_Heritage_flag.svg.png');}
.ns-0 h2 #Gurindji::before, .ns-0 h2#Gurindji::before, .ns-0 h2 #Warlpiri::before, .ns-0 h2#Warlpiri::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b7/Flag_of_the_Northern_Territory.svg/40px-Flag_of_the_Northern_Territory.svg.png');}
.ns-0 h2 #Gutnish::before, .ns-0 h2#Gutnish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c4/Gotlands_län_vapenflagga.svg/37px-Gotlands_län_vapenflagga.svg.png');}
.ns-0 h2 #Guyanese_Creole_English::before, .ns-0 h2#Guyanese_Creole_English::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/99/Flag_of_Guyana.svg/40px-Flag_of_Guyana.svg.png');}
.ns-0 h2 #Hadza::before, .ns-0 h2#Hadza::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/38/Flag_of_Tanzania.svg/40px-Flag_of_Tanzania.svg.png');}
.ns-0 h2 #Haida::before, .ns-0 h2#Haida::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8f/CHN_Crest.png/33px-CHN_Crest.png');}
.ns-0 h2 #Haitian_Creole::before, .ns-0 h2#Haitian_Creole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/56/Flag_of_Haiti.svg/40px-Flag_of_Haiti.svg.png');}
.ns-0 h2 #Hakka::before, .ns-0 h2#Hakka::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/84/Flag_of_Hakka_Party.svg/40px-Flag_of_Hakka_Party.svg.png');}
.ns-0 h2 #Halia::before, .ns-0 h2#Halia::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e4/Flag_of_Bougainville.svg/40px-Flag_of_Bougainville.svg.png');}
.ns-0 h2 #Harari::before, .ns-0 h2#Harari::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6f/Harari_Flag.svg/40px-Harari_Flag.svg.png');}
.ns-0 h2 #Haryanvi::before, .ns-0 h2#Haryanvi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e6/Flag_of_Haryana.svg/40px-Flag_of_Haryana.svg.png');}
.ns-0 h2 #Hassaniya_Arabic::before, .ns-0 h2#Hassaniya_Arabic::before, .ns-0 h2 #Zenaga::before, .ns-0 h2#Zenaga::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/43/Flag_of_Mauritania.svg/40px-Flag_of_Mauritania.svg.png');}
.ns-0 h2 #Hausa::before, .ns-0 h2#Hausa::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5d/Flag_of_the_Hausa_people.svg/40px-Flag_of_the_Hausa_people.svg.png');}
.ns-0 h2 #Hawaiian::before, .ns-0 h2#Hawaiian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ef/Flag_of_Hawaii.svg/40px-Flag_of_Hawaii.svg.png');}
.ns-0 h2 #Hebrew::before, .ns-0 h2#Hebrew::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d4/Flag_of_Israel.svg/40px-Flag_of_Israel.svg.png');}
.ns-0 h2 #Herero::before, .ns-0 h2#Herero::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3b/Namibia,_Botswana_and_Angola.gif/40px-Namibia,_Botswana_and_Angola.gif');}
.ns-0 h2 #Higaonon::before, .ns-0 h2#Higaonon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Ph_seal_misamis_oriental.2.png/40px-Ph_seal_misamis_oriental.2.png');}
.ns-0 h2 #Hijazi_Arabic::before, .ns-0 h2#Hijazi_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8f/Flag_of_Hejaz_(1920).svg/40px-Flag_of_Hejaz_(1920).svg.png');}
.ns-0 h2 #Hiligaynon::before, .ns-0 h2#Hiligaynon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0e/Flag_of_the_Province_of_Iloilo.svg/40px-Flag_of_the_Province_of_Iloilo.svg.png');}
.ns-0 h2 #Hiri_Motu::before, .ns-0 h2#Hiri_Motu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Flag_of_Port_Moresby.png/40px-Flag_of_Port_Moresby.png');}
.ns-0 h2 #Hokkien::before, .ns-0 h2#Hokkien::before, .ns-0 h2 #Min_Nan::before, .ns-0 h2#Min_Nan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/72/Flag_of_the_Republic_of_China.svg/40px-Flag_of_the_Republic_of_China.svg.png');}
.ns-0 h2 #Hungarian::before, .ns-0 h2#Hungarian::before, .ns-0 h2 #Hungarian_Sign_Language::before, .ns-0 h2#Hungarian_Sign_Language::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c1/Flag_of_Hungary.svg/40px-Flag_of_Hungary.svg.png');}
.ns-0 h2 #Hunsrik::before, .ns-0 h2#Hunsrik::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/63/Bandeira_do_Rio_Grande_do_Sul.svg/40px-Bandeira_do_Rio_Grande_do_Sul.svg.png');}
.ns-0 h2 #Iban::before, .ns-0 h2#Iban::before, .ns-0 h2 #Sarawak_Malay::before, .ns-0 h2#Sarawak_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7e/Flag_of_Sarawak.svg/40px-Flag_of_Sarawak.svg.png');}
.ns-0 h2 #Ibanag::before, .ns-0 h2#Ibanag::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/62/Flag_of_Cagayan.svg/40px-Flag_of_Cagayan.svg.png');}
.ns-0 h2 #Icelandic::before, .ns-0 h2#Icelandic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/ce/Flag_of_Iceland.svg/40px-Flag_of_Iceland.svg.png');}
.ns-0 h2 #Ido::before, .ns-0 h2#Ido::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f1/Flag_of_Ido.svg/40px-Flag_of_Ido.svg.png');}
.ns-0 h2 #Igala::before, .ns-0 h2#Igala::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0e/Kogi_State_Flag.svg/40px-Kogi_State_Flag.svg.png');}
.ns-0 h2 #Igbo::before, .ns-0 h2#Igbo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/82/Flag_of_Biafra.svg/40px-Flag_of_Biafra.svg.png');}
.ns-0 h2 #Ilocano::before, .ns-0 h2#Ilocano::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5d/PH-ILS_Flag.png/40px-PH-ILS_Flag.png');}
.ns-0 h2 #Indo-Portuguese::before, .ns-0 h2#Indo-Portuguese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/72/Flag_of_Portugal_(1640).svg/40px-Flag_of_Portugal_(1640).svg.png');}
.ns-0 h2 #Indonesian::before, .ns-0 h2#Indonesian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9f/Flag_of_Indonesia.svg/40px-Flag_of_Indonesia.svg.png');}
.ns-0 h2 #Ingrian::before, .ns-0 h2#Ingrian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/27/Flag_igora.svg/40px-Flag_igora.svg.png');}
.ns-0 h2 #Ingush::before, .ns-0 h2#Ingush::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/00/Flag_of_Ingushetia.svg/40px-Flag_of_Ingushetia.svg.png');}
.ns-0 h2 #Interlingua::before, .ns-0 h2#Interlingua::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9b/Bandiera_de_Interlingua.svg/40px-Bandiera_de_Interlingua.svg.png');}
.ns-0 h2 #Interlingue::before, .ns-0 h2#Interlingue::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5b/Flag_of_Interlingue.svg/40px-Flag_of_Interlingue.svg.png');}
.ns-0 h2 #Inuktitut::before, .ns-0 h2#Inuktitut::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/90/Flag_of_Nunavut.svg/40px-Flag_of_Nunavut.svg.png');}
.ns-0 h2 #Iranun::before, .ns-0 h2#Iranun::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b8/Seal_of_Maguindanao_del_Norte.png/40px-Seal_of_Maguindanao_del_Norte.png');}
.ns-0 h2 #Iraqi_Arabic::before, .ns-0 h2#Iraqi_Arabic::before, .ns-0 h2 #North_Mesopotamian_Arabic::before, .ns-0 h2#North_Mesopotamian_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f6/Flag_of_Iraq.svg/40px-Flag_of_Iraq.svg.png');}
.ns-0 h2 #Iraqw::before, .ns-0 h2#Iraqw::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Iraqw_Flag.png/40px-Iraqw_Flag.png');}
.ns-0 h2 #Irish::before, .ns-0 h2#Irish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/45/Flag_of_Ireland.svg/40px-Flag_of_Ireland.svg.png');}
.ns-0 h2 #Islander_Creole_English::before, .ns-0 h2#Islander_Creole_English::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/36/Flag_of_San_Andrés_y_Providencia.svg/40px-Flag_of_San_Andrés_y_Providencia.svg.png');}
.ns-0 h2 #Istriot::before, .ns-0 h2#Istriot::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/99/HRV_Rovinj_flag.svg/40px-HRV_Rovinj_flag.svg.png');}
.ns-0 h2 #Italian::before, .ns-0 h2#Italian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/03/Flag_of_Italy.svg/40px-Flag_of_Italy.svg.png');}
.ns-0 h2 #Italiot_Greek::before, .ns-0 h2#Italiot_Greek::before, .ns-0 h2 #Tarantino::before, .ns-0 h2#Tarantino::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b8/Flag_of_Apulia.svg/40px-Flag_of_Apulia.svg.png');}
.ns-0 h2 #Itsekiri::before, .ns-0 h2#Itsekiri::before, .ns-0 h2 #Olukumi::before, .ns-0 h2#Olukumi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/aa/Flag_of_Delta_State.gif/40px-Flag_of_Delta_State.gif');}
.ns-0 h2 #Jah_Hut::before, .ns-0 h2#Jah_Hut::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/aa/Flag_of_Pahang.svg/40px-Flag_of_Pahang.svg.png');}
.ns-0 h2 #Jamaican_Creole::before, .ns-0 h2#Jamaican_Creole::before, .ns-0 h2 #Maroon_Spirit_Language::before, .ns-0 h2#Maroon_Spirit_Language::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0a/Flag_of_Jamaica.svg/40px-Flag_of_Jamaica.svg.png');}
.ns-0 h2 #Jamtish::before, .ns-0 h2#Jamtish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Jämtlands_vapen.svg/35px-Jämtlands_vapen.svg.png');}
.ns-0 h2 #Japanese::before, .ns-0 h2#Japanese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9e/Flag_of_Japan.svg/40px-Flag_of_Japan.svg.png');}
.ns-0 h2 #Jaqaru::before, .ns-0 h2#Jaqaru::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/84/Bandera_Provincia_Yauyos.jpg/40px-Bandera_Provincia_Yauyos.jpg');}
.ns-0 h2 #Jaunsari::before, .ns-0 h2#Jaunsari::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/99/Seal_of_Uttarakhand.svg/35px-Seal_of_Uttarakhand.svg.png');}
.ns-0 h2 #Javanese::before, .ns-0 h2#Javanese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Flag_of_the_Mataram_Sultanate.svg/40px-Flag_of_the_Mataram_Sultanate.svg.png');}
.ns-0 h2 #Jeju::before, .ns-0 h2#Jeju::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b9/Flag_of_Jeju_Province.svg/40px-Flag_of_Jeju_Province.svg.png');}
.ns-0 h2 #Jersey_Dutch::before, .ns-0 h2#Jersey_Dutch::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/92/Flag_of_New_Jersey.svg/40px-Flag_of_New_Jersey.svg.png');}
.ns-0 h2 #Jingpho::before, .ns-0 h2#Jingpho::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/47/Kachin_People_Flag.svg/40px-Kachin_People_Flag.svg.png');}
.ns-0 h2 #Juba_Arabic::before, .ns-0 h2#Juba_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7a/Flag_of_South_Sudan.svg/40px-Flag_of_South_Sudan.svg.png');}
.ns-0 h2 #Judeo-Italian::before, .ns-0 h2#Judeo-Italian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b1/Flag_of_Italian_Jews.svg/40px-Flag_of_Italian_Jews.svg.png');}
.ns-0 h2 #K\'iche\'::before, .ns-0 h2#K\'iche\'::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1e/El_Quiché_Flag_(GUATEMALA).png/40px-El_Quiché_Flag_(GUATEMALA).png');}
.ns-0 h2 #Kabiye::before, .ns-0 h2#Kabiye::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7a/Flag_of_Togo_(3-2).svg/40px-Flag_of_Togo_(3-2).svg.png');}
.ns-0 h2 #Kabuverdianu::before, .ns-0 h2#Kabuverdianu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/65/Flag_of_Cape_Verde_(10-17).svg/40px-Flag_of_Cape_Verde_(10-17).svg.png');}
.ns-0 h2 #Kabyle::before, .ns-0 h2#Kabyle::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/71/Flag_of_the_Republic_of_Kabylia.svg/40px-Flag_of_the_Republic_of_Kabylia.svg.png');}
.ns-0 h2 #Kaingang::before, .ns-0 h2#Kaingang::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/93/Bandeira_do_Paraná.svg/40px-Bandeira_do_Paraná.svg.png');}
.ns-0 h2 #Kaitag::before, .ns-0 h2#Kaitag::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Flag_of_Dagestan.svg/40px-Flag_of_Dagestan.svg.png');}
.ns-0 h2 #Kalasha::before, .ns-0 h2#Kalasha::before, .ns-0 h2 #Khowar::before, .ns-0 h2#Khowar::before, .ns-0 h2 #Palula::before, .ns-0 h2#Palula::before, .ns-0 h2 #Ushojo::before, .ns-0 h2#Ushojo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/54/Flag_of_Khyber_Pakhtunkhwa.svg/40px-Flag_of_Khyber_Pakhtunkhwa.svg.png');}
.ns-0 h2 #Kalmyk::before, .ns-0 h2#Kalmyk::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9f/Flag_of_Kalmykia.svg/40px-Flag_of_Kalmykia.svg.png');}
.ns-0 h2 #Kamayurá::before, .ns-0 h2#Kamayurá::before, .ns-0 h2 #Xavante::before, .ns-0 h2#Xavante::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0b/Bandeira_de_Mato_Grosso.svg/40px-Bandeira_de_Mato_Grosso.svg.png');}
.ns-0 h2 #Kankanaey::before, .ns-0 h2#Kankanaey::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/02/Flag_of_Mountain_Province.png/40px-Flag_of_Mountain_Province.png');}
.ns-0 h2 #Kannada::before, .ns-0 h2#Kannada::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0b/Flag_of_Karnataka,_India.svg/40px-Flag_of_Karnataka,_India.svg.png');}
.ns-0 h2 #Kanuri::before, .ns-0 h2#Kanuri::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/95/Flag_of_the_Kanuri_people.svg/40px-Flag_of_the_Kanuri_people.svg.png');}
.ns-0 h2 #Kapampangan::before, .ns-0 h2#Kapampangan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/46/Pampanga_Flag.png/40px-Pampanga_Flag.png');}
.ns-0 h2 #Karachay-Balkar::before, .ns-0 h2#Karachay-Balkar::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/59/Flag_of_Karachay-Cherkessia.svg/40px-Flag_of_Karachay-Cherkessia.svg.png');}
.ns-0 h2 #Karakalpak::before, .ns-0 h2#Karakalpak::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/16/Flag_of_Karakalpakstan.svg/40px-Flag_of_Karakalpakstan.svg.png');}
.ns-0 h2 #Karakhanid::before, .ns-0 h2#Karakhanid::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f4/Flag_of_the_Kara-Khanids.svg/40px-Flag_of_the_Kara-Khanids.svg.png');}
.ns-0 h2 #Karelian::before, .ns-0 h2#Karelian::before, .ns-0 h2 #Livvi::before, .ns-0 h2#Livvi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/78/Itäkarjalaisten_lippu.svg/40px-Itäkarjalaisten_lippu.svg.png');}
.ns-0 h2 #Karipúna_Creole_French::before, .ns-0 h2#Karipúna_Creole_French::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0c/Bandeira_do_Amapá.svg/40px-Bandeira_do_Amapá.svg.png');}
.ns-0 h2 #Kashmiri::before, .ns-0 h2#Kashmiri::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/32/Flag_of_Jammu_and_Kashmir_(1936-1953).svg/40px-Flag_of_Jammu_and_Kashmir_(1936-1953).svg.png');}
.ns-0 h2 #Kashubian::before, .ns-0 h2#Kashubian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fa/POL_Kaszuby_flag.svg/40px-POL_Kaszuby_flag.svg.png');}
.ns-0 h2 #Kazakh::before, .ns-0 h2#Kazakh::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d3/Flag_of_Kazakhstan.svg/40px-Flag_of_Kazakhstan.svg.png');}
.ns-0 h2 #Kedah_Malay::before, .ns-0 h2#Kedah_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/67/Flag_of_Kedah_(18th_century_-_1821).svg/40px-Flag_of_Kedah_(18th_century_-_1821).svg.png');}
.ns-0 h2 #Kelantan_Peranakan_Hokkien::before, .ns-0 h2#Kelantan_Peranakan_Hokkien::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/61/Flag_of_Kelantan.svg/40px-Flag_of_Kelantan.svg.png');}
.ns-0 h2 #Ket::before, .ns-0 h2#Ket::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2d/Flag_of_Krasnoyarsk_Krai.svg/40px-Flag_of_Krasnoyarsk_Krai.svg.png');}
.ns-0 h2 #Khakas::before, .ns-0 h2#Khakas::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Flag_of_Khakassia.svg/40px-Flag_of_Khakassia.svg.png');}
.ns-0 h2 #Khasi::before, .ns-0 h2#Khasi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7c/Seng-Khasi.png/40px-Seng-Khasi.png');}
.ns-0 h2 #Khmer::before, .ns-0 h2#Khmer::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/83/Flag_of_Cambodia.svg/40px-Flag_of_Cambodia.svg.png');}
.ns-0 h2 #Khoekhoe::before, .ns-0 h2#Khoekhoe::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/00/Flag_of_Namibia.svg/40px-Flag_of_Namibia.svg.png');}
.ns-0 h2 #Kikuyu::before, .ns-0 h2#Kikuyu::before, .ns-0 h2 #Maasai::before, .ns-0 h2#Maasai::before, .ns-0 h2 #Swahili::before, .ns-0 h2#Swahili::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/49/Flag_of_Kenya.svg/40px-Flag_of_Kenya.svg.png');}
.ns-0 h2 #Kimbundu::before, .ns-0 h2#Kimbundu::before, .ns-0 h2 #Umbundu::before, .ns-0 h2#Umbundu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9d/Flag_of_Angola.svg/40px-Flag_of_Angola.svg.png');}
.ns-0 h2 #Kiowa::before, .ns-0 h2#Kiowa::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6e/Flag_of_Oklahoma.svg/40px-Flag_of_Oklahoma.svg.png');}
.ns-0 h2 #Kituba::before, .ns-0 h2#Kituba::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/92/Flag_of_the_Republic_of_the_Congo.svg/40px-Flag_of_the_Republic_of_the_Congo.svg.png');}
.ns-0 h2 #Klingon::before, .ns-0 h2#Klingon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/02/Klingon_Empire_Flag.svg/40px-Klingon_Empire_Flag.svg.png');}
.ns-0 h2 #Kokborok::before, .ns-0 h2#Kokborok::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Flag_of_the_National_Liberation_Front_of_Tripura.svg/40px-Flag_of_the_National_Liberation_Front_of_Tripura.svg.png');}
.ns-0 h2 #Komi-Permyak::before, .ns-0 h2#Komi-Permyak::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/02/Flag_of_Permyakia.svg/40px-Flag_of_Permyakia.svg.png');}
.ns-0 h2 #Komi-Zyrian::before, .ns-0 h2#Komi-Zyrian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/54/Flag_of_Komi.svg/40px-Flag_of_Komi.svg.png');}
.ns-0 h2 #Kongo::before, .ns-0 h2#Kongo::before, .ns-0 h2 #Lingala::before, .ns-0 h2#Lingala::before, .ns-0 h2 #Luba-Kasai::before, .ns-0 h2#Luba-Kasai::before, .ns-0 h2 #Luba-Katanga::before, .ns-0 h2#Luba-Katanga::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6f/Flag_of_the_Democratic_Republic_of_the_Congo.svg/40px-Flag_of_the_Democratic_Republic_of_the_Congo.svg.png');}
.ns-0 h2 #Konzo::before, .ns-0 h2#Konzo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/09/Flag_of_Rwenzururu.svg/40px-Flag_of_Rwenzururu.svg.png');}
.ns-0 h2 #Korean::before, .ns-0 h2#Korean::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8a/Unification_flag_of_Korea.svg/40px-Unification_flag_of_Korea.svg.png');}
.ns-0 h2 #Korlai_Creole_Portuguese::before, .ns-0 h2#Korlai_Creole_Portuguese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d3/Seal_of_Maharashtra.svg/35px-Seal_of_Maharashtra.svg.png');}
.ns-0 h2 #Koryak::before, .ns-0 h2#Koryak::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Flag_of_Koryakia.svg/40px-Flag_of_Koryakia.svg.png');}
.ns-0 h2 #Kosraean::before, .ns-0 h2#Kosraean::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/23/Flag_of_Kosrae.svg/40px-Flag_of_Kosrae.svg.png');}
.ns-0 h2 #Krio::before, .ns-0 h2#Krio::before, .ns-0 h2 #Mende_\(Sierra_Leone\)::before, .ns-0 h2#Mende_\(Sierra_Leone\)::before, .ns-0 h2 #Temne::before, .ns-0 h2#Temne::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/17/Flag_of_Sierra_Leone.svg/40px-Flag_of_Sierra_Leone.svg.png');}
.ns-0 h2 #Kristang::before, .ns-0 h2#Kristang::before, .ns-0 h2 #Malaccan_Creole_Malay::before, .ns-0 h2#Malaccan_Creole_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/09/Flag_of_Malacca.svg/40px-Flag_of_Malacca.svg.png');}
.ns-0 h2 #Kumaoni::before, .ns-0 h2#Kumaoni::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a1/Flag_of_the_Kumaon_Kingdom.svg/40px-Flag_of_the_Kumaon_Kingdom.svg.png');}
.ns-0 h2 #Kumyk::before, .ns-0 h2#Kumyk::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1c/Flag_of_Kumyks.svg/40px-Flag_of_Kumyks.svg.png');}
.ns-0 h2 #Kuna::before, .ns-0 h2#Kuna::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3d/Bandera_de_la_Comarca_Guna_Yala.svg/40px-Bandera_de_la_Comarca_Guna_Yala.svg.png');}
.ns-0 h2 #Kunigami::before, .ns-0 h2#Kunigami::before, .ns-0 h2 #Miyako::before, .ns-0 h2#Miyako::before, .ns-0 h2 #Okinawan::before, .ns-0 h2#Okinawan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Flag_of_Okinawa_Prefecture.svg/40px-Flag_of_Okinawa_Prefecture.svg.png');}
.ns-0 h2 #Kupang_Malay::before, .ns-0 h2#Kupang_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c6/Flag_of_Kupang_City.png/40px-Flag_of_Kupang_City.png');}
.ns-0 h2 #Kven::before, .ns-0 h2#Kven::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/66/Flag_of_the_Kven_people.svg/40px-Flag_of_the_Kven_people.svg.png');}
.ns-0 h2 #Kwanyama::before, .ns-0 h2#Kwanyama::before, .ns-0 h2 #Ndonga::before, .ns-0 h2#Ndonga::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/46/Flag_of_Ovamboland.svg/40px-Flag_of_Ovamboland.svg.png');}
.ns-0 h2 #Kyrgyz::before, .ns-0 h2#Kyrgyz::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c7/Flag_of_Kyrgyzstan.svg/40px-Flag_of_Kyrgyzstan.svg.png');}
.ns-0 h2 #Laboya::before, .ns-0 h2#Laboya::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e9/Coat_of_Arms_of_East_Nusa_Tenggara_NEW.png/35px-Coat_of_Arms_of_East_Nusa_Tenggara_NEW.png');}
.ns-0 h2 #Ladakhi::before, .ns-0 h2#Ladakhi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5e/Flag_of_the_Ladakh_Nationalist_Movement.svg/40px-Flag_of_the_Ladakh_Nationalist_Movement.svg.png');}
.ns-0 h2 #Ladin::before, .ns-0 h2#Ladin::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/62/Flag_of_Ladinia.svg/40px-Flag_of_Ladinia.svg.png');}
.ns-0 h2 #Ladino::before, .ns-0 h2#Ladino::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3d/Spanish_Jewish_flag.png/40px-Spanish_Jewish_flag.png');}
.ns-0 h2 #Lahu::before, .ns-0 h2#Lahu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/17/Lahu_flag.svg/40px-Lahu_flag.svg.png');}
.ns-0 h2 #Lai::before, .ns-0 h2#Lai::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/af/Emblem_of_Lai_Autonomous_Council.png/40px-Emblem_of_Lai_Autonomous_Council.png');}
.ns-0 h2 #Lakota::before, .ns-0 h2#Lakota::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/77/Pine_Ridge_Flag.svg/40px-Pine_Ridge_Flag.svg.png');}
.ns-0 h2 #Lao::before, .ns-0 h2#Lao::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/56/Flag_of_Laos.svg/40px-Flag_of_Laos.svg.png');}
.ns-0 h2 #Latgalian::before, .ns-0 h2#Latgalian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7d/Official_flag_of_Latgale.svg/40px-Official_flag_of_Latgale.svg.png');}
.ns-0 h2 #Latin::before, .ns-0 h2#Latin::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a6/Flag_of_Rome.svg/40px-Flag_of_Rome.svg.png');}
.ns-0 h2 #Latvian::before, .ns-0 h2#Latvian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/84/Flag_of_Latvia.svg/40px-Flag_of_Latvia.svg.png');}
.ns-0 h2 #Leonese::before, .ns-0 h2#Leonese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/75/Bandera_del_País_Leonés.svg/40px-Bandera_del_País_Leonés.svg.png');}
.ns-0 h2 #Lepcha::before, .ns-0 h2#Lepcha::before, .ns-0 h2 #Sherpa::before, .ns-0 h2#Sherpa::before, .ns-0 h2 #Thulung::before, .ns-0 h2#Thulung::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c0/Seal_of_Sikkim.svg/40px-Seal_of_Sikkim.svg.png');}
.ns-0 h2 #Lezgi::before, .ns-0 h2#Lezgi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3f/Lezgian_flag.svg/40px-Lezgian_flag.svg.png');}
.ns-0 h2 #Ligurian::before, .ns-0 h2#Ligurian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/88/Flag_of_Liguria.svg/40px-Flag_of_Liguria.svg.png');}
.ns-0 h2 #Limbu::before, .ns-0 h2#Limbu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a2/Flag_of_Limbuwan.svg/40px-Flag_of_Limbuwan.svg.png');}
.ns-0 h2 #Limburgish::before, .ns-0 h2#Limburgish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Flag_of_Limburg_(Netherlands).svg/40px-Flag_of_Limburg_(Netherlands).svg.png');}
.ns-0 h2 #Limos_Kalinga::before, .ns-0 h2#Limos_Kalinga::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a9/Ph_seal_kalinga.png/40px-Ph_seal_kalinga.png');}
.ns-0 h2 #Lindu::before, .ns-0 h2#Lindu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/46/Coat_of_arms_of_Central_Sulawesi.svg/30px-Coat_of_arms_of_Central_Sulawesi.svg.png');}
.ns-0 h2 #Lithuanian::before, .ns-0 h2#Lithuanian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/11/Flag_of_Lithuania.svg/40px-Flag_of_Lithuania.svg.png');}
.ns-0 h2 #Livonian::before, .ns-0 h2#Livonian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/42/Flag_of_the_Livonians.svg/40px-Flag_of_the_Livonians.svg.png');}
.ns-0 h2 #Lojban::before, .ns-0 h2#Lojban::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/48/Lojban_logo.svg/40px-Lojban_logo.svg.png');}
.ns-0 h2 #Lombard::before, .ns-0 h2#Lombard::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Flag_of_Lombardy.svg/40px-Flag_of_Lombardy.svg.png');}
.ns-0 h2 #Louisiana_Creole::before, .ns-0 h2#Louisiana_Creole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e0/Flag_of_Louisiana.svg/40px-Flag_of_Louisiana.svg.png');}
.ns-0 h2 #Lovono::before, .ns-0 h2#Lovono::before, .ns-0 h2 #Äiwoo::before, .ns-0 h2#Äiwoo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b3/Temotu_province_flag.svg/40px-Temotu_province_flag.svg.png');}
.ns-0 h2 #Lower_Sorbian::before, .ns-0 h2#Lower_Sorbian::before, .ns-0 h2 #Upper_Sorbian::before, .ns-0 h2#Upper_Sorbian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c0/Flag_of_Sorbs.svg/40px-Flag_of_Sorbs.svg.png');}
.ns-0 h2 #Lozi::before, .ns-0 h2#Lozi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/62/Flag_of_Barotseland.svg/40px-Flag_of_Barotseland.svg.png');}
.ns-0 h2 #Ludian::before, .ns-0 h2#Ludian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6c/Ludic_flag.svg/40px-Ludic_flag.svg.png');}
.ns-0 h2 #Luganda::before, .ns-0 h2#Luganda::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4e/Flag_of_Uganda.svg/40px-Flag_of_Uganda.svg.png');}
.ns-0 h2 #Lushootseed::before, .ns-0 h2#Lushootseed::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/54/Flag_of_Washington.svg/40px-Flag_of_Washington.svg.png');}
.ns-0 h2 #Lutuv::before, .ns-0 h2#Lutuv::before, .ns-0 h2 #Zou::before, .ns-0 h2#Zou::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/16/Flag_of_Chin_State.svg/40px-Flag_of_Chin_State.svg.png');}
.ns-0 h2 #Luxembourgish::before, .ns-0 h2#Luxembourgish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/da/Flag_of_Luxembourg.svg/40px-Flag_of_Luxembourg.svg.png');}
.ns-0 h2 #Macanese::before, .ns-0 h2#Macanese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/63/Flag_of_Macau.svg/40px-Flag_of_Macau.svg.png');}
.ns-0 h2 #Macedonian::before, .ns-0 h2#Macedonian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/79/Flag_of_North_Macedonia.svg/40px-Flag_of_North_Macedonia.svg.png');}
.ns-0 h2 #Maguindanao::before, .ns-0 h2#Maguindanao::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/87/Flag_of_Maguindanao.svg/40px-Flag_of_Maguindanao.svg.png');}
.ns-0 h2 #Makasar::before, .ns-0 h2#Makasar::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Flag_of_the_Sultanate_of_Gowa.svg/40px-Flag_of_the_Sultanate_of_Gowa.svg.png');}
.ns-0 h2 #Malagasy::before, .ns-0 h2#Malagasy::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Flag_of_Madagascar.svg/40px-Flag_of_Madagascar.svg.png');}
.ns-0 h2 #Malay::before, .ns-0 h2#Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/01/Flag_of_Malaysia_and_Indonesia.svg/40px-Flag_of_Malaysia_and_Indonesia.svg.png');}
.ns-0 h2 #Maltese::before, .ns-0 h2#Maltese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/73/Flag_of_Malta.svg/40px-Flag_of_Malta.svg.png');}
.ns-0 h2 #Manado_Malay::before, .ns-0 h2#Manado_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ef/Lambang_Kota_Manado.png/30px-Lambang_Kota_Manado.png');}
.ns-0 h2 #Manchu::before, .ns-0 h2#Manchu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/86/Flag_of_China_(1862–1889).svg/40px-Flag_of_China_(1862–1889).svg.png');}
.ns-0 h2 #Mandinka::before, .ns-0 h2#Mandinka::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/77/Flag_of_The_Gambia.svg/40px-Flag_of_The_Gambia.svg.png');}
.ns-0 h2 #Manipuri::before, .ns-0 h2#Manipuri::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4e/Flag_of_Manipur.svg/40px-Flag_of_Manipur.svg.png');}
.ns-0 h2 #Mansaka::before, .ns-0 h2#Mansaka::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/94/Davao_De_Oro_Flag.jpg/40px-Davao_De_Oro_Flag.jpg');}
.ns-0 h2 #Manx::before, .ns-0 h2#Manx::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Flag_of_the_Isle_of_Man.svg/40px-Flag_of_the_Isle_of_Man.svg.png');}
.ns-0 h2 #Maore_Comorian::before, .ns-0 h2#Maore_Comorian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/bf/Coat_of_Arms_of_Mayotte.svg/40px-Coat_of_Arms_of_Mayotte.svg.png');}
.ns-0 h2 #Mapudungun::before, .ns-0 h2#Mapudungun::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/20/Flag_of_the_Mapuches_(1992).svg/40px-Flag_of_the_Mapuches_(1992).svg.png');}
.ns-0 h2 #Maranao::before, .ns-0 h2#Maranao::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b8/PH-LAS_Flag.png/40px-PH-LAS_Flag.png');}
.ns-0 h2 #Margi::before, .ns-0 h2#Margi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1d/Borno_State_Flag.gif/40px-Borno_State_Flag.gif');}
.ns-0 h2 #Marshallese::before, .ns-0 h2#Marshallese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2e/Flag_of_the_Marshall_Islands.svg/40px-Flag_of_the_Marshall_Islands.svg.png');}
.ns-0 h2 #Masalit::before, .ns-0 h2#Masalit::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b0/Flag_of_the_Masalit.svg/40px-Flag_of_the_Masalit.svg.png');}
.ns-0 h2 #Masbatenyo::before, .ns-0 h2#Masbatenyo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/dd/PH-MAS_Flag.png/40px-PH-MAS_Flag.png');}
.ns-0 h2 #Mauritian_Creole::before, .ns-0 h2#Mauritian_Creole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/77/Flag_of_Mauritius.svg/40px-Flag_of_Mauritius.svg.png');}
.ns-0 h2 #Maybrat::before, .ns-0 h2#Maybrat::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/43/Flag_of_Southwest_Papua.svg/40px-Flag_of_Southwest_Papua.svg.png');}
.ns-0 h2 #Mbya_Guarani::before, .ns-0 h2#Mbya_Guarani::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/24/Flag_of_Uruguay,_Brazil_and_Argentina.png/40px-Flag_of_Uruguay,_Brazil_and_Argentina.png');}
.ns-0 h2 #Media_Lengua::before, .ns-0 h2#Media_Lengua::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/dc/Bandera_Provincia_Imbabura.svg/40px-Bandera_Provincia_Imbabura.svg.png');}
.ns-0 h2 #Megleno-Romanian::before, .ns-0 h2#Megleno-Romanian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1a/Flag_of_Greek_Macedonia.svg/40px-Flag_of_Greek_Macedonia.svg.png');}
.ns-0 h2 #Mehri::before, .ns-0 h2#Mehri::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d8/Flag_of_the_Mahra_Sultanate.svg/40px-Flag_of_the_Mahra_Sultanate.svg.png');}
.ns-0 h2 #Meriam::before, .ns-0 h2#Meriam::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/37/Flag_of_Murray_Island_(Mer).svg/40px-Flag_of_Murray_Island_(Mer).svg.png');}
.ns-0 h2 #Meru::before, .ns-0 h2#Meru::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5c/Flag_of_Meru_County.png/40px-Flag_of_Meru_County.png');}
.ns-0 h2 #Meänkieli::before, .ns-0 h2#Meänkieli::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/94/Flag_of_Tornedalians_(2007).svg/40px-Flag_of_Tornedalians_(2007).svg.png');}
.ns-0 h2 #Mi\'kmaq::before, .ns-0 h2#Mi\'kmaq::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3f/Mikmaq_State_Flag.svg/40px-Mikmaq_State_Flag.svg.png');}
.ns-0 h2 #Michif::before, .ns-0 h2#Michif::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f4/Metis_Blue.svg/40px-Metis_Blue.svg.png');}
.ns-0 h2 #Michoacán_Nahuatl::before, .ns-0 h2#Michoacán_Nahuatl::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Coat_of_arms_of_Michoacan.svg/32px-Coat_of_arms_of_Michoacan.svg.png');}
.ns-0 h2 #Middle_Dutch::before, .ns-0 h2#Middle_Dutch::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3c/Flag_of_the_prince-bishopric_of_Utrecht.svg/36px-Flag_of_the_prince-bishopric_of_Utrecht.svg.png');}
.ns-0 h2 #Middle_English::before, .ns-0 h2#Middle_English::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/be/Flag_of_England.svg/40px-Flag_of_England.svg.png');}
.ns-0 h2 #Middle_French::before, .ns-0 h2#Middle_French::before, .ns-0 h2 #Old_French::before, .ns-0 h2#Old_French::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/82/Royal_flag_of_France.svg/40px-Royal_flag_of_France.svg.png');}
.ns-0 h2 #Middle_Irish::before, .ns-0 h2#Middle_Irish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8d/Flag_of_Leinster.svg/40px-Flag_of_Leinster.svg.png');}
.ns-0 h2 #Middle_Korean::before, .ns-0 h2#Middle_Korean::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/54/Royal_flag_of_Goryeo_(Bong-gi).svg/40px-Royal_flag_of_Goryeo_(Bong-gi).svg.png');}
.ns-0 h2 #Middle_Mongol::before, .ns-0 h2#Middle_Mongol::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Flag_of_the_Mongol_Empire_3.svg/40px-Flag_of_the_Mongol_Empire_3.svg.png');}
.ns-0 h2 #Middle_Norwegian::before, .ns-0 h2#Middle_Norwegian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/36/Royal_Banner_of_Norway_(14th_Century).svg/34px-Royal_Banner_of_Norway_(14th_Century).svg.png');}
.ns-0 h2 #Middle_Persian::before, .ns-0 h2#Middle_Persian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8f/Derafsh_kaviani.svg/38px-Derafsh_kaviani.svg.png');}
.ns-0 h2 #Middle_Scots::before, .ns-0 h2#Middle_Scots::before, .ns-0 h2 #Scottish_Gaelic::before, .ns-0 h2#Scottish_Gaelic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c5/Royal_Banner_of_Scotland.svg/40px-Royal_Banner_of_Scotland.svg.png');}
.ns-0 h2 #Mikasuki::before, .ns-0 h2#Mikasuki::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/34/Flag_of_the_Miccosukee_Tribe_of_Indians_of_Florida.svg/40px-Flag_of_the_Miccosukee_Tribe_of_Indians_of_Florida.svg.png');}
.ns-0 h2 #Minangkabau::before, .ns-0 h2#Minangkabau::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2a/Flag_of_Minang.svg/40px-Flag_of_Minang.svg.png');}
.ns-0 h2 #Mingrelian::before, .ns-0 h2#Mingrelian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fb/Flag_of_The_Principality_of_Mingrelia_(Portolan_1560).svg/40px-Flag_of_The_Principality_of_Mingrelia_(Portolan_1560).svg.png');}
.ns-0 h2 #Mirandese::before, .ns-0 h2#Mirandese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/41/Bandeira-de-Miranda-do-Douro.png/36px-Bandeira-de-Miranda-do-Douro.png');}
.ns-0 h2 #Miskito::before, .ns-0 h2#Miskito::before, .ns-0 h2 #Nicaraguan_Creole::before, .ns-0 h2#Nicaraguan_Creole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/19/Flag_of_Nicaragua.svg/40px-Flag_of_Nicaragua.svg.png');}
.ns-0 h2 #Mohawk::before, .ns-0 h2#Mohawk::before, .ns-0 h2 #Oneida::before, .ns-0 h2#Oneida::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/77/Flag_of_the_Iroquois_Confederacy.svg/40px-Flag_of_the_Iroquois_Confederacy.svg.png');}
.ns-0 h2 #Mokilese::before, .ns-0 h2#Mokilese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/ba/Flag_of_Mwoakilloa.svg/40px-Flag_of_Mwoakilloa.svg.png');}
.ns-0 h2 #Moksha::before, .ns-0 h2#Moksha::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e1/Flag_of_the_Moksha_people.svg/40px-Flag_of_the_Moksha_people.svg.png');}
.ns-0 h2 #Mon::before, .ns-0 h2#Mon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/03/Flag_of_Mon_State_(2018).svg/40px-Flag_of_Mon_State_(2018).svg.png');}
.ns-0 h2 #Mongolian::before, .ns-0 h2#Mongolian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4c/Flag_of_Mongolia.svg/40px-Flag_of_Mongolia.svg.png');}
.ns-0 h2 #Mongondow::before, .ns-0 h2#Mongondow::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d0/Lambang_Kabupaten_Bolaang_Mongondow.png/35px-Lambang_Kabupaten_Bolaang_Mongondow.png');}
.ns-0 h2 #Moore::before, .ns-0 h2#Moore::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/31/Flag_of_Burkina_Faso.svg/40px-Flag_of_Burkina_Faso.svg.png');}
.ns-0 h2 #Morelos_Nahuatl::before, .ns-0 h2#Morelos_Nahuatl::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/01/Coat_of_arms_of_Morelos.svg/32px-Coat_of_arms_of_Morelos.svg.png');}
.ns-0 h2 #Moroccan_Amazigh::before, .ns-0 h2#Moroccan_Amazigh::before, .ns-0 h2 #Moroccan_Arabic::before, .ns-0 h2#Moroccan_Arabic::before, .ns-0 h2 #Tashelhit::before, .ns-0 h2#Tashelhit::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Flag_of_Morocco.svg/40px-Flag_of_Morocco.svg.png');}
.ns-0 h2 #Muong::before, .ns-0 h2#Muong::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/14/Flag_of_Muong_Autonomous_Region.svg/40px-Flag_of_Muong_Autonomous_Region.svg.png');}
.ns-0 h2 #Mwali_Comorian::before, .ns-0 h2#Mwali_Comorian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c2/Flag_of_Mohéli_(official).svg/40px-Flag_of_Mohéli_(official).svg.png');}
.ns-0 h2 #Mwani::before, .ns-0 h2#Mwani::before, .ns-0 h2 #Ronga::before, .ns-0 h2#Ronga::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d0/Flag_of_Mozambique.svg/40px-Flag_of_Mozambique.svg.png');}
.ns-0 h2 #Mòcheno::before, .ns-0 h2#Mòcheno::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/51/Flag_of_Comunità_Alta_Valsugana_e_Bersntol.png/40px-Flag_of_Comunità_Alta_Valsugana_e_Bersntol.png');}
.ns-0 h2 #Māori::before, .ns-0 h2#Māori::before, .ns-0 h2 #New_Zealand_Sign_Language::before, .ns-0 h2#New_Zealand_Sign_Language::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3e/Flag_of_New_Zealand.svg/40px-Flag_of_New_Zealand.svg.png');}
.ns-0 h2 #Najdi_Arabic::before, .ns-0 h2#Najdi_Arabic::before{padding-right:5px;
content:url("//upload.wikimedia.org/wikipedia/commons/thumb/1/17/Flag_of_the_Emirate_of_Ha'il.svg/40px-Flag_of_the_Emirate_of_Ha'il.svg.png");}
.ns-0 h2 #Nauruan::before, .ns-0 h2#Nauruan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/30/Flag_of_Nauru.svg/40px-Flag_of_Nauru.svg.png');}
.ns-0 h2 #Navajo::before, .ns-0 h2#Navajo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/ce/Official_design_of_the_Navajo_Nation_flag_as_it_was_adopted_on_May_21,_1968.svg/40px-Official_design_of_the_Navajo_Nation_flag_as_it_was_adopted_on_May_21,_1968.svg.png');}
.ns-0 h2 #Ndzwani_Comorian::before, .ns-0 h2#Ndzwani_Comorian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a6/Flag_of_Anjouan_(official).svg/40px-Flag_of_Anjouan_(official).svg.png');}
.ns-0 h2 #Neapolitan::before, .ns-0 h2#Neapolitan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c5/Flag_of_Campania.svg/40px-Flag_of_Campania.svg.png');}
.ns-0 h2 #Negeri_Sembilan_Malay::before, .ns-0 h2#Negeri_Sembilan_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/db/Flag_of_Negeri_Sembilan.svg/40px-Flag_of_Negeri_Sembilan.svg.png');}
.ns-0 h2 #Nepali::before, .ns-0 h2#Nepali::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9b/Flag_of_Nepal.svg/20px-Flag_of_Nepal.svg.png');}
.ns-0 h2 #Newar::before, .ns-0 h2#Newar::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Flag_of_Kathmandu,_Nepal.svg/40px-Flag_of_Kathmandu,_Nepal.svg.png');}
.ns-0 h2 #Ngazidja_Comorian::before, .ns-0 h2#Ngazidja_Comorian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/94/Flag_of_the_Comoros.svg/40px-Flag_of_the_Comoros.svg.png');}
.ns-0 h2 #Nheengatu::before, .ns-0 h2#Nheengatu::before, .ns-0 h2 #Trió::before, .ns-0 h2#Trió::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/02/Bandeira_do_Pará.svg/40px-Bandeira_do_Pará.svg.png');}
.ns-0 h2 #Nias::before, .ns-0 h2#Nias::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c8/Coat_of_arms_of_North_Sumatra.svg/30px-Coat_of_arms_of_North_Sumatra.svg.png');}
.ns-0 h2 #Nigerian_Pidgin::before, .ns-0 h2#Nigerian_Pidgin::before, .ns-0 h2 #Nigerian_Sign_Language::before, .ns-0 h2#Nigerian_Sign_Language::before, .ns-0 h2 #Yoruba::before, .ns-0 h2#Yoruba::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/79/Flag_of_Nigeria.svg/40px-Flag_of_Nigeria.svg.png');}
.ns-0 h2 #Niuean::before, .ns-0 h2#Niuean::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/01/Flag_of_Niue.svg/40px-Flag_of_Niue.svg.png');}
.ns-0 h2 #Nivkh::before, .ns-0 h2#Nivkh::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/57/Flag_of_Sakhalin_Oblast.svg/40px-Flag_of_Sakhalin_Oblast.svg.png');}
.ns-0 h2 #Norman::before, .ns-0 h2#Norman::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/41/Flag_of_Normandie.svg/40px-Flag_of_Normandie.svg.png');}
.ns-0 h2 #North_Frisian::before, .ns-0 h2#North_Frisian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b2/Nordfriesischeflagge.svg/40px-Nordfriesischeflagge.svg.png');}
.ns-0 h2 #North_Moluccan_Malay::before, .ns-0 h2#North_Moluccan_Malay::before, .ns-0 h2 #West_Makian::before, .ns-0 h2#West_Makian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/73/Coat_of_arms_of_North_Maluku.svg/35px-Coat_of_arms_of_North_Maluku.svg.png');}
.ns-0 h2 #Northern_Altai::before, .ns-0 h2#Northern_Altai::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4b/Flag_of_Altai_Krai.svg/40px-Flag_of_Altai_Krai.svg.png');}
.ns-0 h2 #Northern_Catanduanes_Bikol::before, .ns-0 h2#Northern_Catanduanes_Bikol::before, .ns-0 h2 #Southern_Catanduanes_Bikol::before, .ns-0 h2#Southern_Catanduanes_Bikol::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/61/PH-CAT_Flag.png/40px-PH-CAT_Flag.png');}
.ns-0 h2 #Northern_Ndebele::before, .ns-0 h2#Northern_Ndebele::before, .ns-0 h2 #Shona::before, .ns-0 h2#Shona::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6a/Flag_of_Zimbabwe.svg/40px-Flag_of_Zimbabwe.svg.png');}
.ns-0 h2 #Northern_Sotho::before, .ns-0 h2#Northern_Sotho::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/52/Flag_of_Lebowa.svg/40px-Flag_of_Lebowa.svg.png');}
.ns-0 h2 #Northern_Thai::before, .ns-0 h2#Northern_Thai::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d4/Seal_of_Lanna_Kingdom.png/40px-Seal_of_Lanna_Kingdom.png');}
.ns-0 h2 #Norwegian::before, .ns-0 h2#Norwegian::before, .ns-0 h2 #Norwegian_Bokmål::before, .ns-0 h2#Norwegian_Bokmål::before, .ns-0 h2 #Norwegian_Nynorsk::before, .ns-0 h2#Norwegian_Nynorsk::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d9/Flag_of_Norway.svg/40px-Flag_of_Norway.svg.png');}
.ns-0 h2 #Novial::before, .ns-0 h2#Novial::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ed/Flag_of_Novial.svg/40px-Flag_of_Novial.svg.png');}
.ns-0 h2 #Nubi::before, .ns-0 h2#Nubi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c2/Kenia-Uganda-Flagge.png/40px-Kenia-Uganda-Flagge.png');}
.ns-0 h2 #Nuosu::before, .ns-0 h2#Nuosu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9d/Proposed_Yi_flag.svg/40px-Proposed_Yi_flag.svg.png');}
.ns-0 h2 #Nupe::before, .ns-0 h2#Nupe::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a7/Niger_state_flag.png/40px-Niger_state_flag.png');}
.ns-0 h2 #Nyankole::before, .ns-0 h2#Nyankole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3c/Flag_of_Ankole.svg/40px-Flag_of_Ankole.svg.png');}
.ns-0 h2 #Nyoro::before, .ns-0 h2#Nyoro::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2a/Bunyoro-Kitara_Kingdom_Flag.png/40px-Bunyoro-Kitara_Kingdom_Flag.png');}
.ns-0 h2 #Nùng::before, .ns-0 h2#Nùng::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/75/Flag_of_the_Nùng_Autonomous_Territory.svg/40px-Flag_of_the_Nùng_Autonomous_Territory.svg.png');}
.ns-0 h2 #O\'odham::before, .ns-0 h2#O\'odham::before{padding-right:5px;
content:url("//upload.wikimedia.org/wikipedia/commons/thumb/0/05/O'odham_unofficial_flag.svg/40px-O'odham_unofficial_flag.svg.png");}
.ns-0 h2 #Occitan::before, .ns-0 h2#Occitan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6d/Flag_of_Occitania_(with_star).svg/40px-Flag_of_Occitania_(with_star).svg.png');}
.ns-0 h2 #Odia::before, .ns-0 h2#Odia::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5d/Flag_of_Mayurbhanj.png/40px-Flag_of_Mayurbhanj.png');}
.ns-0 h2 #Ojibwe::before, .ns-0 h2#Ojibwe::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/94/Flag_of_the_Peguis_First_Nation.svg/40px-Flag_of_the_Peguis_First_Nation.svg.png');}
.ns-0 h2 #Old_Armenian::before, .ns-0 h2#Old_Armenian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b3/Standard_of_the_Artaxiad_dynasty.svg/40px-Standard_of_the_Artaxiad_dynasty.svg.png');}
.ns-0 h2 #Old_Catalan::before, .ns-0 h2#Old_Catalan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/58/Bandera_del_Principat_de_Catalunya_(1200_dC).svg/40px-Bandera_del_Principat_de_Catalunya_(1200_dC).svg.png');}
.ns-0 h2 #Old_Czech::before, .ns-0 h2#Old_Czech::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/41/Banner_of_Přemyslid_family.svg/40px-Banner_of_Přemyslid_family.svg.png');}
.ns-0 h2 #Old_English::before, .ns-0 h2#Old_English::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e0/Flag_of_Wessex.svg/40px-Flag_of_Wessex.svg.png');}
.ns-0 h2 #Old_Galician-Portuguese::before, .ns-0 h2#Old_Galician-Portuguese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/25/PortugueseFlag1143.svg/40px-PortugueseFlag1143.svg.png');}
.ns-0 h2 #Old_Georgian::before, .ns-0 h2#Old_Georgian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Kartli_-_drosha_jvari.svg/40px-Kartli_-_drosha_jvari.svg.png');}
.ns-0 h2 #Old_High_German::before, .ns-0 h2#Old_High_German::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Flag_of_East_Francia_(843–962).png/40px-Flag_of_East_Francia_(843–962).png');}
.ns-0 h2 #Old_Irish::before, .ns-0 h2#Old_Irish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/42/Triskele-Symbol1.svg/40px-Triskele-Symbol1.svg.png');}
.ns-0 h2 #Old_Javanese::before, .ns-0 h2#Old_Javanese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/10/Naval_flag_of_Majapahit_Kingdom.svg/40px-Naval_flag_of_Majapahit_Kingdom.svg.png');}
.ns-0 h2 #Old_Korean::before, .ns-0 h2#Old_Korean::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/80/Military_Banner_of_Silla.svg/40px-Military_Banner_of_Silla.svg.png');}
.ns-0 h2 #Old_Leonese::before, .ns-0 h2#Old_Leonese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/73/Royal_Banner_of_Leon_(Variant).svg/40px-Royal_Banner_of_Leon_(Variant).svg.png');}
.ns-0 h2 #Old_Navarro-Aragonese::before, .ns-0 h2#Old_Navarro-Aragonese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/45/Navarro-Aragonese_flag.png/40px-Navarro-Aragonese_flag.png');}
.ns-0 h2 #Old_Norse::before, .ns-0 h2#Old_Norse::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/10/Raven_Banner.svg/40px-Raven_Banner.svg.png');}
.ns-0 h2 #Old_Novgorodian::before, .ns-0 h2#Old_Novgorodian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/73/Banner_of_the_Novgorod_Republic_(c._1385).svg/40px-Banner_of_the_Novgorod_Republic_(c._1385).svg.png');}
.ns-0 h2 #Old_Nubian::before, .ns-0 h2#Old_Nubian::before{padding-right:5px;
content:url("//upload.wikimedia.org/wikipedia/commons/thumb/4/47/The_flag_of_the_'Kingdom_of_Dongola'_(Makuria)_in_the_%22Book_of_all_kingdoms%22_(C._1350).svg/40px-The_flag_of_the_'Kingdom_of_Dongola'_(Makuria)_in_the_%22Book_of_all_kingdoms%22_(C._1350).svg.png");}
.ns-0 h2 #Old_Occitan::before, .ns-0 h2#Old_Occitan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6f/Flag_of_Provence.svg/40px-Flag_of_Provence.svg.png');}
.ns-0 h2 #Old_Polish::before, .ns-0 h2#Old_Polish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/12/Flag_of_the_Kingdom_of_Poland.svg/40px-Flag_of_the_Kingdom_of_Poland.svg.png');}
.ns-0 h2 #Old_Ruthenian::before, .ns-0 h2#Old_Ruthenian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e1/Royal_banner_of_the_Grand_Duchy_of_Lithuania.svg/40px-Royal_banner_of_the_Grand_Duchy_of_Lithuania.svg.png');}
.ns-0 h2 #Old_Saxon::before, .ns-0 h2#Old_Saxon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3b/Flag_of_Old_Saxony.svg/40px-Flag_of_Old_Saxony.svg.png');}
.ns-0 h2 #Old_Spanish::before, .ns-0 h2#Old_Spanish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/71/Flag_of_Castile.svg/40px-Flag_of_Castile.svg.png');}
.ns-0 h2 #Old_Swedish::before, .ns-0 h2#Old_Swedish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/25/Royal_Banner_of_Sweden_(14th_Century).svg/30px-Royal_Banner_of_Sweden_(14th_Century).svg.png');}
.ns-0 h2 #Old_Welsh::before, .ns-0 h2#Old_Welsh::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/66/Flag_of_Gwynedd_(3-2).svg/40px-Flag_of_Gwynedd_(3-2).svg.png');}
.ns-0 h2 #Orizaba_Nahuatl::before, .ns-0 h2#Orizaba_Nahuatl::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/57/Escudo_de_Armas_de_la_Ciudad_de_Orizaba.svg/35px-Escudo_de_Armas_de_la_Ciudad_de_Orizaba.svg.png');}
.ns-0 h2 #Oromo::before, .ns-0 h2#Oromo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3c/Flag_of_the_Oromia_Region.svg/40px-Flag_of_the_Oromia_Region.svg.png');}
.ns-0 h2 #Osage::before, .ns-0 h2#Osage::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3e/Flag_of_the_Osage_Nation.svg/40px-Flag_of_the_Osage_Nation.svg.png');}
.ns-0 h2 #Ossetian::before, .ns-0 h2#Ossetian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1c/Flag_of_North_Ossetia.svg/40px-Flag_of_North_Ossetia.svg.png');}
.ns-0 h2 #Ottoman_Turkish::before, .ns-0 h2#Ottoman_Turkish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/06/Flag_of_the_Ottoman_Empire_(Thicker_Crescent).svg/40px-Flag_of_the_Ottoman_Empire_(Thicker_Crescent).svg.png');}
.ns-0 h2 #Pa\'o_Karen::before, .ns-0 h2#Pa\'o_Karen::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/28/Flag_of_the_Pa-O_National_Organisation.svg/40px-Flag_of_the_Pa-O_National_Organisation.svg.png');}
.ns-0 h2 #Pahari-Potwari::before, .ns-0 h2#Pahari-Potwari::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Flag_of_Azad_Kashmir.svg/40px-Flag_of_Azad_Kashmir.svg.png');}
.ns-0 h2 #Palauan::before, .ns-0 h2#Palauan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/48/Flag_of_Palau.svg/40px-Flag_of_Palau.svg.png');}
.ns-0 h2 #Palenquero::before, .ns-0 h2#Palenquero::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/21/Flag_of_Colombia.svg/40px-Flag_of_Colombia.svg.png');}
.ns-0 h2 #Pali::before, .ns-0 h2#Pali::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f1/Buddhism_dham_jak.svg/35px-Buddhism_dham_jak.svg.png');}
.ns-0 h2 #Panamanian_Sign_Language::before, .ns-0 h2#Panamanian_Sign_Language::before, .ns-0 h2 #San_Miguel_Creole_French::before, .ns-0 h2#San_Miguel_Creole_French::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/ab/Flag_of_Panama.svg/40px-Flag_of_Panama.svg.png');}
.ns-0 h2 #Pangasinan::before, .ns-0 h2#Pangasinan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a1/Flag_of_Pangasinan.svg/40px-Flag_of_Pangasinan.svg.png');}
.ns-0 h2 #Papiamentu::before, .ns-0 h2#Papiamentu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/ae/Flag_of_the_Netherlands_Antilles_(1986–2010).svg/40px-Flag_of_the_Netherlands_Antilles_(1986–2010).svg.png');}
.ns-0 h2 #Papuan_Malay::before, .ns-0 h2#Papuan_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Kerajaan_Kaimana_Papua.gif/40px-Kerajaan_Kaimana_Papua.gif');}
.ns-0 h2 #Paraguayan_Guarani::before, .ns-0 h2#Paraguayan_Guarani::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/27/Flag_of_Paraguay.svg/40px-Flag_of_Paraguay.svg.png');}
.ns-0 h2 #Parauk::before, .ns-0 h2#Parauk::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/48/Flag_of_Wa_State.svg/40px-Flag_of_Wa_State.svg.png');}
.ns-0 h2 #Pashto::before, .ns-0 h2#Pashto::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d4/Pashto_Banner_flag_(Neutral).png/40px-Pashto_Banner_flag_(Neutral).png');}
.ns-0 h2 #Pennsylvania_German::before, .ns-0 h2#Pennsylvania_German::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f7/Flag_of_Pennsylvania.svg/40px-Flag_of_Pennsylvania.svg.png');}
.ns-0 h2 #Penrhyn::before, .ns-0 h2#Penrhyn::before, .ns-0 h2 #Pukapukan::before, .ns-0 h2#Pukapukan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/35/Flag_of_the_Cook_Islands.svg/40px-Flag_of_the_Cook_Islands.svg.png');}
.ns-0 h2 #Persian::before, .ns-0 h2#Persian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/ca/Flag_of_Iran.svg/40px-Flag_of_Iran.svg.png');}
.ns-0 h2 #Phuthi::before, .ns-0 h2#Phuthi::before, .ns-0 h2 #Sotho::before, .ns-0 h2#Sotho::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4a/Flag_of_Lesotho.svg/40px-Flag_of_Lesotho.svg.png');}
.ns-0 h2 #Picard::before, .ns-0 h2#Picard::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/50/Picardy_flag.png/40px-Picardy_flag.png');}
.ns-0 h2 #Pichinglis::before, .ns-0 h2#Pichinglis::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/31/Flag_of_Equatorial_Guinea.svg/40px-Flag_of_Equatorial_Guinea.svg.png');}
.ns-0 h2 #Piedmontese::before, .ns-0 h2#Piedmontese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b9/Flag_of_Piedmont.svg/40px-Flag_of_Piedmont.svg.png');}
.ns-0 h2 #Pijin::before, .ns-0 h2#Pijin::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/74/Flag_of_the_Solomon_Islands.svg/40px-Flag_of_the_Solomon_Islands.svg.png');}
.ns-0 h2 #Pitcairn-Norfolk::before, .ns-0 h2#Pitcairn-Norfolk::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/48/Flag_of_Norfolk_Island.svg/40px-Flag_of_Norfolk_Island.svg.png');}
.ns-0 h2 #Pitjantjatjara::before, .ns-0 h2#Pitjantjatjara::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fd/Flag_of_South_Australia.svg/40px-Flag_of_South_Australia.svg.png');}
.ns-0 h2 #Pnar::before, .ns-0 h2#Pnar::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/87/Emblem_of_Jaintia_Hills_Autonomous_District_Council.png/40px-Emblem_of_Jaintia_Hills_Autonomous_District_Council.png');}
.ns-0 h2 #Pohnpeian::before, .ns-0 h2#Pohnpeian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d6/Flag_of_Pohnpei_(1977-1992).svg/40px-Flag_of_Pohnpei_(1977-1992).svg.png');}
.ns-0 h2 #Polabian::before, .ns-0 h2#Polabian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/36/Banner_of_Wends.png/40px-Banner_of_Wends.png');}
.ns-0 h2 #Polish::before, .ns-0 h2#Polish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/12/Flag_of_Poland.svg/40px-Flag_of_Poland.svg.png');}
.ns-0 h2 #Pontic_Greek::before, .ns-0 h2#Pontic_Greek::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/bf/Flag_of_Pontus.svg/40px-Flag_of_Pontus.svg.png');}
.ns-0 h2 #Portuguese::before, .ns-0 h2#Portuguese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/31/Flag_of_Portuguese_language_(PT-BR).svg/40px-Flag_of_Portuguese_language_(PT-BR).svg.png');}
.ns-0 h2 #Principense::before, .ns-0 h2#Principense::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/21/Flag_of_Príncipe_Autonomous_Region.svg/40px-Flag_of_Príncipe_Autonomous_Region.svg.png');}
.ns-0 h2 #Puerto_Rican_Sign_Language::before, .ns-0 h2#Puerto_Rican_Sign_Language::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/28/Flag_of_Puerto_Rico.svg/40px-Flag_of_Puerto_Rico.svg.png');}
.ns-0 h2 #Punjabi::before, .ns-0 h2#Punjabi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/36/Flag_of_Punjab_(Paired).png/40px-Flag_of_Punjab_(Paired).png');}
.ns-0 h2 #Quechua::before, .ns-0 h2#Quechua::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8f/Quechuas_flag.svg/40px-Quechuas_flag.svg.png');}
.ns-0 h2 #Rakhine::before, .ns-0 h2#Rakhine::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fd/Flag_of_Rakhine.svg/40px-Flag_of_Rakhine.svg.png');}
.ns-0 h2 #Rapa_Nui::before, .ns-0 h2#Rapa_Nui::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d8/Flag_of_Rapa_Nui,_Chile.svg/40px-Flag_of_Rapa_Nui,_Chile.svg.png');}
.ns-0 h2 #Ratagnon::before, .ns-0 h2#Ratagnon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Province_of_Occidental_Mindoro_seal.svg/40px-Province_of_Occidental_Mindoro_seal.svg.png');}
.ns-0 h2 #Rennellese::before, .ns-0 h2#Rennellese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/83/Flag_of_Rennell_and_Bellona_Province.svg/40px-Flag_of_Rennell_and_Bellona_Province.svg.png');}
.ns-0 h2 #Rohingya::before, .ns-0 h2#Rohingya::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/83/Rohingya_flag.svg/40px-Rohingya_flag.svg.png');}
.ns-0 h2 #Romagnol::before, .ns-0 h2#Romagnol::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b1/Flag_of_San_Marino.svg/40px-Flag_of_San_Marino.svg.png');}
.ns-0 h2 #Romanian::before, .ns-0 h2#Romanian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/73/Flag_of_Romania.svg/40px-Flag_of_Romania.svg.png');}
.ns-0 h2 #Romansh::before, .ns-0 h2#Romansh::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0f/Flag_of_Canton_of_Graubünden.svg/40px-Flag_of_Canton_of_Graubünden.svg.png');}
.ns-0 h2 #Rotuman::before, .ns-0 h2#Rotuman::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/bb/Flag_of_Rotuma_(1987-1988).svg/40px-Flag_of_Rotuma_(1987-1988).svg.png');}
.ns-0 h2 #Russian::before, .ns-0 h2#Russian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f3/Flag_of_Russia.svg/40px-Flag_of_Russia.svg.png');}
.ns-0 h2 #Rutul::before, .ns-0 h2#Rutul::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/da/Flag_of_the_Rutul_People.svg/40px-Flag_of_the_Rutul_People.svg.png');}
.ns-0 h2 #Rwanda-Rundi::before, .ns-0 h2#Rwanda-Rundi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/81/Flag_of_Rwanda_and_Burundi.svg/40px-Flag_of_Rwanda_and_Burundi.svg.png');}
.ns-0 h2 #Réunion_Creole_French::before, .ns-0 h2#Réunion_Creole_French::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8e/Proposed_flag_of_Réunion_(VAR).svg/40px-Proposed_flag_of_Réunion_(VAR).svg.png');}
.ns-0 h2 #S\'gaw_Karen::before, .ns-0 h2#S\'gaw_Karen::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Flag_of_the_Karen_National_Union.svg/40px-Flag_of_the_Karen_National_Union.svg.png');}
.ns-0 h2 #Saho::before, .ns-0 h2#Saho::before{padding-right:5px;
content:url("//upload.wikimedia.org/wikipedia/commons/thumb/c/c4/Flag_of_the_Saho_People's_Democratic_Movement.svg/40px-Flag_of_the_Saho_People's_Democratic_Movement.svg.png");}
.ns-0 h2 #Sambali::before, .ns-0 h2#Sambali::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/81/Seal_of_Zambales.svg/40px-Seal_of_Zambales.svg.png');}
.ns-0 h2 #Samoan::before, .ns-0 h2#Samoan::before, .ns-0 h2 #Samoan_Plantation_Pidgin::before, .ns-0 h2#Samoan_Plantation_Pidgin::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/31/Flag_of_Samoa.svg/40px-Flag_of_Samoa.svg.png');}
.ns-0 h2 #Samogitian::before, .ns-0 h2#Samogitian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c4/Flag_of_Žemaitija.svg/40px-Flag_of_Žemaitija.svg.png');}
.ns-0 h2 #Sanaviron::before, .ns-0 h2#Sanaviron::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/aa/Bandera_de_la_Ciudad_de_Córdoba.svg/40px-Bandera_de_la_Ciudad_de_Córdoba.svg.png');}
.ns-0 h2 #Sango::before, .ns-0 h2#Sango::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6f/Flag_of_the_Central_African_Republic.svg/40px-Flag_of_the_Central_African_Republic.svg.png');}
.ns-0 h2 #Sanskrit::before, .ns-0 h2#Sanskrit::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8e/Red_om_symbol.png/35px-Red_om_symbol.png');}
.ns-0 h2 #Sardinian::before, .ns-0 h2#Sardinian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4e/Flag_of_Sardinia,_Italy.svg/40px-Flag_of_Sardinia,_Italy.svg.png');}
.ns-0 h2 #Sassarese::before, .ns-0 h2#Sassarese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fe/Flag_of_Sassari.svg/40px-Flag_of_Sassari.svg.png');}
.ns-0 h2 #Saterland_Frisian::before, .ns-0 h2#Saterland_Frisian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2f/DEU_Saterland_Flag.svg/40px-DEU_Saterland_Flag.svg.png');}
.ns-0 h2 #Scanian::before, .ns-0 h2#Scanian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1e/Banner_of_Arms_of_Scania.svg/35px-Banner_of_Arms_of_Scania.svg.png');}
.ns-0 h2 #Scots::before, .ns-0 h2#Scots::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/10/Flag_of_Scotland.svg/40px-Flag_of_Scotland.svg.png');}
.ns-0 h2 #Sedang::before, .ns-0 h2#Sedang::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/19/Flag_of_Sedang.svg/36px-Flag_of_Sedang.svg.png');}
.ns-0 h2 #Selk\'nam::before, .ns-0 h2#Selk\'nam::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/94/Bandera_de_la_Provincia_de_Tierra_del_Fuego.svg/40px-Bandera_de_la_Provincia_de_Tierra_del_Fuego.svg.png');}
.ns-0 h2 #Serbo-Croatian::before, .ns-0 h2#Serbo-Croatian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fb/HR-BA-ME-RS_flag.svg/40px-HR-BA-ME-RS_flag.svg.png');}
.ns-0 h2 #Serer::before, .ns-0 h2#Serer::before, .ns-0 h2 #Wolof::before, .ns-0 h2#Wolof::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fd/Flag_of_Senegal.svg/40px-Flag_of_Senegal.svg.png');}
.ns-0 h2 #Seri::before, .ns-0 h2#Seri::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4c/Bandera_de_la_Nacion_Comcaac.svg/40px-Bandera_de_la_Nacion_Comcaac.svg.png');}
.ns-0 h2 #Seychellois_Creole::before, .ns-0 h2#Seychellois_Creole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fc/Flag_of_Seychelles.svg/40px-Flag_of_Seychelles.svg.png');}
.ns-0 h2 #Shan::before, .ns-0 h2#Shan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/68/Flag_of_Shan_State.svg/40px-Flag_of_Shan_State.svg.png');}
.ns-0 h2 #Shihhi_Arabic::before, .ns-0 h2#Shihhi_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5d/Flag_of_Sharjah_and_Ras_Al_Khaimah.svg/40px-Flag_of_Sharjah_and_Ras_Al_Khaimah.svg.png');}
.ns-0 h2 #Sicilian::before, .ns-0 h2#Sicilian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7a/Flag_of_Sicily.svg/40px-Flag_of_Sicily.svg.png');}
.ns-0 h2 #Sidamo::before, .ns-0 h2#Sidamo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/59/Flag_of_Sidama.svg/40px-Flag_of_Sidama.svg.png');}
.ns-0 h2 #Sikkimese::before, .ns-0 h2#Sikkimese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6b/Flag_of_Sikkim_(1967-1975).svg/40px-Flag_of_Sikkim_(1967-1975).svg.png');}
.ns-0 h2 #Silesian::before, .ns-0 h2#Silesian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c1/Flag_of_Upper_Silesia.svg/40px-Flag_of_Upper_Silesia.svg.png');}
.ns-0 h2 #Sindhi::before, .ns-0 h2#Sindhi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Flag_of_Sindh.svg/40px-Flag_of_Sindh.svg.png');}
.ns-0 h2 #Sinhalese::before, .ns-0 h2#Sinhalese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/11/Flag_of_Sri_Lanka.svg/40px-Flag_of_Sri_Lanka.svg.png');}
.ns-0 h2 #Slavomolisano::before, .ns-0 h2#Slavomolisano::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/84/Flag_of_Molise.svg/40px-Flag_of_Molise.svg.png');}
.ns-0 h2 #Slovak::before, .ns-0 h2#Slovak::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e6/Flag_of_Slovakia.svg/40px-Flag_of_Slovakia.svg.png');}
.ns-0 h2 #Slovene::before, .ns-0 h2#Slovene::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f0/Flag_of_Slovenia.svg/40px-Flag_of_Slovenia.svg.png');}
.ns-0 h2 #Slovincian::before, .ns-0 h2#Slovincian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/08/Wappen_Pommern.svg/35px-Wappen_Pommern.svg.png');}
.ns-0 h2 #Soga::before, .ns-0 h2#Soga::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d8/Flag_of_Busoga_(royal_standard).png/40px-Flag_of_Busoga_(royal_standard).png');}
.ns-0 h2 #Solombala_English::before, .ns-0 h2#Solombala_English::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/20/Coat_of_Arms_of_Arkhangelsk.svg/32px-Coat_of_Arms_of_Arkhangelsk.svg.png');}
.ns-0 h2 #Somali::before, .ns-0 h2#Somali::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a0/Flag_of_Somalia.svg/40px-Flag_of_Somalia.svg.png');}
.ns-0 h2 #South_Levantine_Arabic::before, .ns-0 h2#South_Levantine_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/00/Flag_of_Palestine.svg/40px-Flag_of_Palestine.svg.png');}
.ns-0 h2 #Southern_Altai::before, .ns-0 h2#Southern_Altai::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/ff/Flag_of_Altai_Republic.svg/40px-Flag_of_Altai_Republic.svg.png');}
.ns-0 h2 #Southern_Ndebele::before, .ns-0 h2#Southern_Ndebele::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/93/Flag_of_Mpumalanga_Province.svg/40px-Flag_of_Mpumalanga_Province.svg.png');}
.ns-0 h2 #Spanish::before, .ns-0 h2#Spanish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/49/Flag_of_Spanish_language_(ES-MX).svg/40px-Flag_of_Spanish_language_(ES-MX).svg.png');}
.ns-0 h2 #Sri_Lankan_Creole_Malay::before, .ns-0 h2#Sri_Lankan_Creole_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b3/Flag_of_the_Southern_Province_(Sri_Lanka).PNG/40px-Flag_of_the_Southern_Province_(Sri_Lanka).PNG');}
.ns-0 h2 #Sudovian::before, .ns-0 h2#Sudovian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/54/Flag_of_Suvalkija.svg/40px-Flag_of_Suvalkija.svg.png');}
.ns-0 h2 #Sumerian::before, .ns-0 h2#Sumerian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3d/Cuneiform_sumer_dingir.svg/28px-Cuneiform_sumer_dingir.svg.png');}
.ns-0 h2 #Sundanese::before, .ns-0 h2#Sundanese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/41/Flag_of_West_Java_(vectorised).svg/40px-Flag_of_West_Java_(vectorised).svg.png');}
.ns-0 h2 #Surigaonon::before, .ns-0 h2#Surigaonon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e8/Flag_of_Surigao_del_Norte.svg/40px-Flag_of_Surigao_del_Norte.svg.png');}
.ns-0 h2 #Swabian::before, .ns-0 h2#Swabian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/14/Flagge_Königreich_Württemberg.svg/40px-Flagge_Königreich_Württemberg.svg.png');}
.ns-0 h2 #Swazi::before, .ns-0 h2#Swazi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fb/Flag_of_Eswatini.svg/40px-Flag_of_Eswatini.svg.png');}
.ns-0 h2 #Swedish::before, .ns-0 h2#Swedish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4c/Flag_of_Sweden.svg/40px-Flag_of_Sweden.svg.png');}
.ns-0 h2 #Tabasaran::before, .ns-0 h2#Tabasaran::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d0/Ethnic_flag_of_Tabasarans.svg/40px-Ethnic_flag_of_Tabasarans.svg.png');}
.ns-0 h2 #Tabasco_Nahuatl::before, .ns-0 h2#Tabasco_Nahuatl::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b0/Coat_of_arms_of_Tabasco_(México).svg/32px-Coat_of_arms_of_Tabasco_(México).svg.png');}
.ns-0 h2 #Tachawit::before, .ns-0 h2#Tachawit::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6f/Flag_of_Chaouia.svg/40px-Flag_of_Chaouia.svg.png');}
.ns-0 h2 #Tagalog::before, .ns-0 h2#Tagalog::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/99/Flag_of_the_Philippines.svg/40px-Flag_of_the_Philippines.svg.png');}
.ns-0 h2 #Tahitian::before, .ns-0 h2#Tahitian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/db/Flag_of_French_Polynesia.svg/40px-Flag_of_French_Polynesia.svg.png');}
.ns-0 h2 #Tai_Dam::before, .ns-0 h2#Tai_Dam::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/93/Flag_of_the_Tai_Dam_People.svg/40px-Flag_of_the_Tai_Dam_People.svg.png');}
.ns-0 h2 #Tai_Dón::before, .ns-0 h2#Tai_Dón::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/86/Flag_of_Tay_Khao.png/40px-Flag_of_Tay_Khao.png');}
.ns-0 h2 #Taimyr_Pidgin_Russian::before, .ns-0 h2#Taimyr_Pidgin_Russian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Flag_of_the_Siberian_Federal_District.png/40px-Flag_of_the_Siberian_Federal_District.png');}
.ns-0 h2 #Tajik::before, .ns-0 h2#Tajik::before, .ns-0 h2 #Tajiki_Arabic::before, .ns-0 h2#Tajiki_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d0/Flag_of_Tajikistan.svg/40px-Flag_of_Tajikistan.svg.png');}
.ns-0 h2 #Talysh::before, .ns-0 h2#Talysh::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/de/Flag_of_the_Talysh_National_Movement.svg/40px-Flag_of_the_Talysh_National_Movement.svg.png');}
.ns-0 h2 #Tarifit::before, .ns-0 h2#Tarifit::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/61/Flag_of_the_Republic_of_the_Rif.svg/40px-Flag_of_the_Republic_of_the_Rif.svg.png');}
.ns-0 h2 #Tarjumo::before, .ns-0 h2#Tarjumo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/49/Kanem_flag_from_dulcerta_1339-pt.svg/40px-Kanem_flag_from_dulcerta_1339-pt.svg.png');}
.ns-0 h2 #Tatar::before, .ns-0 h2#Tatar::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/28/Flag_of_Tatarstan.svg/40px-Flag_of_Tatarstan.svg.png');}
.ns-0 h2 #Tausug::before, .ns-0 h2#Tausug::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/da/Late_19th_Century_Flag_of_Sulu.svg/40px-Late_19th_Century_Flag_of_Sulu.svg.png');}
.ns-0 h2 #Tayo::before, .ns-0 h2#Tayo::before, .ns-0 h2 #Xârâcùù::before, .ns-0 h2#Xârâcùù::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/66/Flag_of_FLNKS.svg/40px-Flag_of_FLNKS.svg.png');}
.ns-0 h2 #Tboli::before, .ns-0 h2#Tboli::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7f/South_Cotabato_Flag.png/40px-South_Cotabato_Flag.png');}
.ns-0 h2 #Temascaltepec_Nahuatl::before, .ns-0 h2#Temascaltepec_Nahuatl::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4b/TEMASCALTEPEC.svg/35px-TEMASCALTEPEC.svg.png');}
.ns-0 h2 #Terengganu_Malay::before, .ns-0 h2#Terengganu_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6b/Flag_of_Terengganu.svg/40px-Flag_of_Terengganu.svg.png');}
.ns-0 h2 #Ternate::before, .ns-0 h2#Ternate::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/61/Bendera_Ternate_-_Almulk_Buldan_Ternate.svg/40px-Bendera_Ternate_-_Almulk_Buldan_Ternate.svg.png');}
.ns-0 h2 #Tetum::before, .ns-0 h2#Tetum::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/26/Flag_of_East_Timor.svg/40px-Flag_of_East_Timor.svg.png');}
.ns-0 h2 #Thai::before, .ns-0 h2#Thai::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a9/Flag_of_Thailand.svg/40px-Flag_of_Thailand.svg.png');}
.ns-0 h2 #Tibetan::before, .ns-0 h2#Tibetan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3c/Flag_of_Tibet.svg/40px-Flag_of_Tibet.svg.png');}
.ns-0 h2 #Ticuna::before, .ns-0 h2#Ticuna::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6b/Bandeira_do_Amazonas.svg/40px-Bandeira_do_Amazonas.svg.png');}
.ns-0 h2 #Tlingit::before, .ns-0 h2#Tlingit::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/94/Flag_of_the_Organized_Village_of_Kake.svg/40px-Flag_of_the_Organized_Village_of_Kake.svg.png');}
.ns-0 h2 #Toba_Batak::before, .ns-0 h2#Toba_Batak::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5e/Flag_of_Batak_WRB.svg/40px-Flag_of_Batak_WRB.svg.png');}
.ns-0 h2 #Tobagonian_Creole_English::before, .ns-0 h2#Tobagonian_Creole_English::before, .ns-0 h2 #Trinidadian_Creole_English::before, .ns-0 h2#Trinidadian_Creole_English::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/64/Flag_of_Trinidad_and_Tobago.svg/40px-Flag_of_Trinidad_and_Tobago.svg.png');}
.ns-0 h2 #Tok_Pisin::before, .ns-0 h2#Tok_Pisin::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e3/Flag_of_Papua_New_Guinea.svg/40px-Flag_of_Papua_New_Guinea.svg.png');}
.ns-0 h2 #Tokelauan::before, .ns-0 h2#Tokelauan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8e/Flag_of_Tokelau.svg/40px-Flag_of_Tokelau.svg.png');}
.ns-0 h2 #Tongan::before, .ns-0 h2#Tongan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9a/Flag_of_Tonga.svg/40px-Flag_of_Tonga.svg.png');}
.ns-0 h2 #Tooro::before, .ns-0 h2#Tooro::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c7/Flag_of_Toro.gif/40px-Flag_of_Toro.gif');}
.ns-0 h2 #Transylvanian_Saxon::before, .ns-0 h2#Transylvanian_Saxon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8c/Flag_of_Transylvania_Saxons.svg/40px-Flag_of_Transylvania_Saxons.svg.png');}
.ns-0 h2 #Tsonga::before, .ns-0 h2#Tsonga::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/70/Flag_of_Tsonga_language.svg/40px-Flag_of_Tsonga_language.svg.png');}
.ns-0 h2 #Tsou::before, .ns-0 h2#Tsou::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3e/Flag_of_Chiayi_County.svg/40px-Flag_of_Chiayi_County.svg.png');}
.ns-0 h2 #Tswana::before, .ns-0 h2#Tswana::before, .ns-0 h2 #ǃXóõ::before, .ns-0 h2#ǃXóõ::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Flag_of_Botswana.svg/40px-Flag_of_Botswana.svg.png');}
.ns-0 h2 #Tuareg::before, .ns-0 h2#Tuareg::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/54/Touareg_People_Flag.svg/40px-Touareg_People_Flag.svg.png');}
.ns-0 h2 #Tunisian_Arabic::before, .ns-0 h2#Tunisian_Arabic::before, .ns-0 h2 #Tunisian_Berber::before, .ns-0 h2#Tunisian_Berber::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/ce/Flag_of_Tunisia.svg/40px-Flag_of_Tunisia.svg.png');}
.ns-0 h2 #Turkana::before, .ns-0 h2#Turkana::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e8/Flag_of_Turkana_County.gif/40px-Flag_of_Turkana_County.gif');}
.ns-0 h2 #Turkish::before, .ns-0 h2#Turkish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b4/Flag_of_Turkey.svg/40px-Flag_of_Turkey.svg.png');}
.ns-0 h2 #Turkmen::before, .ns-0 h2#Turkmen::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1b/Flag_of_Turkmenistan.svg/40px-Flag_of_Turkmenistan.svg.png');}
.ns-0 h2 #Tuvaluan::before, .ns-0 h2#Tuvaluan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/38/Flag_of_Tuvalu.svg/40px-Flag_of_Tuvalu.svg.png');}
.ns-0 h2 #Tuvan::before, .ns-0 h2#Tuvan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/77/Flag_of_Tuva.svg/40px-Flag_of_Tuva.svg.png');}
.ns-0 h2 #Tzotzil::before, .ns-0 h2#Tzotzil::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3e/Flag_of_the_Mayan_People.svg/40px-Flag_of_the_Mayan_People.svg.png');}
.ns-0 h2 #Tày::before, .ns-0 h2#Tày::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3d/Flag_of_the_Tay_people_(1947-1954).svg/40px-Flag_of_the_Tay_people_(1947-1954).svg.png');}
.ns-0 h2 #Tübatulabal::before, .ns-0 h2#Tübatulabal::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/45/Seal_of_Kern_County,_California.png/40px-Seal_of_Kern_County,_California.png');}
.ns-0 h2 #Udmurt::before, .ns-0 h2#Udmurt::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c7/Flag_of_Udmurtia.svg/40px-Flag_of_Udmurtia.svg.png');}
.ns-0 h2 #Ukrainian::before, .ns-0 h2#Ukrainian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/49/Flag_of_Ukraine.svg/40px-Flag_of_Ukraine.svg.png');}
.ns-0 h2 #Umbrian::before, .ns-0 h2#Umbrian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/cc/Flag_of_Umbria.svg/40px-Flag_of_Umbria.svg.png');}
.ns-0 h2 #Unserdeutsch::before, .ns-0 h2#Unserdeutsch::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/10/Flag_of_East_New_Britain.svg/40px-Flag_of_East_New_Britain.svg.png');}
.ns-0 h2 #Urdu::before, .ns-0 h2#Urdu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/32/Flag_of_Pakistan.svg/40px-Flag_of_Pakistan.svg.png');}
.ns-0 h2 #Uyghur::before, .ns-0 h2#Uyghur::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Kokbayraq_flag.svg/40px-Kokbayraq_flag.svg.png');}
.ns-0 h2 #Uzbek::before, .ns-0 h2#Uzbek::before, .ns-0 h2 #Uzbeki_Arabic::before, .ns-0 h2#Uzbeki_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/84/Flag_of_Uzbekistan.svg/40px-Flag_of_Uzbekistan.svg.png');}
.ns-0 h2 #Vai::before, .ns-0 h2#Vai::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b8/Flag_of_Liberia.svg/40px-Flag_of_Liberia.svg.png');}
.ns-0 h2 #Veddah::before, .ns-0 h2#Veddah::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1c/Flag_of_the_Uva_Province_(Sri_Lanka).svg/40px-Flag_of_the_Uva_Province_(Sri_Lanka).svg.png');}
.ns-0 h2 #Venda::before, .ns-0 h2#Venda::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/21/Flag_of_Venda_(1973–1994).svg/40px-Flag_of_Venda_(1973–1994).svg.png');}
.ns-0 h2 #Venetan::before, .ns-0 h2#Venetan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d5/Flag_of_Veneto.svg/40px-Flag_of_Veneto.svg.png');}
.ns-0 h2 #Veps::before, .ns-0 h2#Veps::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/45/Flag_of_Vepsia.svg/40px-Flag_of_Vepsia.svg.png');}
.ns-0 h2 #Vilamovian::before, .ns-0 h2#Vilamovian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3d/POL_Wilamowice_COA.svg/35px-POL_Wilamowice_COA.svg.png');}
.ns-0 h2 #Vincentian_Creole_English::before, .ns-0 h2#Vincentian_Creole_English::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6d/Flag_of_Saint_Vincent_and_the_Grenadines.svg/40px-Flag_of_Saint_Vincent_and_the_Grenadines.svg.png');}
.ns-0 h2 #Virgin_Islands_Creole::before, .ns-0 h2#Virgin_Islands_Creole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f8/Flag_of_the_United_States_Virgin_Islands.svg/40px-Flag_of_the_United_States_Virgin_Islands.svg.png');}
.ns-0 h2 #Volapük::before, .ns-0 h2#Volapük::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e3/Volapuk_symbol.svg/40px-Volapuk_symbol.svg.png');}
.ns-0 h2 #Volga_German::before, .ns-0 h2#Volga_German::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/55/Flag_of_Volga_Germans_(version).svg/40px-Flag_of_Volga_Germans_(version).svg.png');}
.ns-0 h2 #Votic::before, .ns-0 h2#Votic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Votic_Flag.svg/40px-Votic_Flag.svg.png');}
.ns-0 h2 #Võro::before, .ns-0 h2#Võro::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c1/Võrumaa_lipp.svg/40px-Võrumaa_lipp.svg.png');}
.ns-0 h2 #Walloon::before, .ns-0 h2#Walloon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/42/Flag_of_Wallonia.svg/40px-Flag_of_Wallonia.svg.png');}
.ns-0 h2 #Wapishana::before, .ns-0 h2#Wapishana::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/98/Bandeira_de_Roraima.svg/40px-Bandeira_de_Roraima.svg.png');}
.ns-0 h2 #Waray-Waray::before, .ns-0 h2#Waray-Waray::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f2/Biliran_Provincial_Flag.png/40px-Biliran_Provincial_Flag.png');}
.ns-0 h2 #Wayuu::before, .ns-0 h2#Wayuu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/13/Panteera_Wayuu_Aumentado.png/40px-Panteera_Wayuu_Aumentado.png');}
.ns-0 h2 #Welsh::before, .ns-0 h2#Welsh::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/dc/Flag_of_Wales.svg/40px-Flag_of_Wales.svg.png');}
.ns-0 h2 #West_Circassian::before, .ns-0 h2#West_Circassian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/16/Flag_of_Adygea.svg/40px-Flag_of_Adygea.svg.png');}
.ns-0 h2 #West_Flemish::before, .ns-0 h2#West_Flemish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c2/Flag_of_West_Flanders.svg/40px-Flag_of_West_Flanders.svg.png');}
.ns-0 h2 #West_Frisian::before, .ns-0 h2#West_Frisian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/ca/Frisian_flag.svg/40px-Frisian_flag.svg.png');}
.ns-0 h2 #West_Miraya_Bikol::before, .ns-0 h2#West_Miraya_Bikol::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c2/Albay_Flag.png/40px-Albay_Flag.png');}
.ns-0 h2 #Western_Apache::before, .ns-0 h2#Western_Apache::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8b/Coat_of_arms_of_Sonora.svg/35px-Coat_of_arms_of_Sonora.svg.png');}
.ns-0 h2 #Western_Cham::before, .ns-0 h2#Western_Cham::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/45/Bandera_Front_Alliberament_Cham.svg/40px-Bandera_Front_Alliberament_Cham.svg.png');}
.ns-0 h2 #Western_Mari::before, .ns-0 h2#Western_Mari::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/24/Flag_of_Gornomariysky_rayon.svg/40px-Flag_of_Gornomariysky_rayon.svg.png');}
.ns-0 h2 #Wichí_Lhamtés_Güisnay::before, .ns-0 h2#Wichí_Lhamtés_Güisnay::before, .ns-0 h2 #Wichí_Lhamtés_Nocten::before, .ns-0 h2#Wichí_Lhamtés_Nocten::before, .ns-0 h2 #Wichí_Lhamtés_Vejoz::before, .ns-0 h2#Wichí_Lhamtés_Vejoz::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/66/Bandera_del_pueblo_Wichi.svg/40px-Bandera_del_pueblo_Wichi.svg.png');}
.ns-0 h2 #Woiwurrung::before, .ns-0 h2#Woiwurrung::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/08/Flag_of_Victoria_(Australia).svg/40px-Flag_of_Victoria_(Australia).svg.png');}
.ns-0 h2 #Wolio::before, .ns-0 h2#Wolio::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/55/Lambang_Kota_Bau-Bau.png/35px-Lambang_Kota_Bau-Bau.png');}
.ns-0 h2 #Xerénte::before, .ns-0 h2#Xerénte::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/ff/Bandeira_do_Tocantins.svg/40px-Bandeira_do_Tocantins.svg.png');}
.ns-0 h2 #Xhosa::before, .ns-0 h2#Xhosa::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/cc/Flag_of_Ciskei.svg/40px-Flag_of_Ciskei.svg.png');}
.ns-0 h2 #Xokleng::before, .ns-0 h2#Xokleng::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1a/Bandeira_de_Santa_Catarina.svg/40px-Bandeira_de_Santa_Catarina.svg.png');}
.ns-0 h2 #Yakan::before, .ns-0 h2#Yakan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/87/Flag_of_Basilan.svg/40px-Flag_of_Basilan.svg.png');}
.ns-0 h2 #Yakut::before, .ns-0 h2#Yakut::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/eb/Flag_of_Sakha.svg/40px-Flag_of_Sakha.svg.png');}
.ns-0 h2 #Yapese::before, .ns-0 h2#Yapese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Flag_of_Yap.svg/40px-Flag_of_Yap.svg.png');}
.ns-0 h2 #Yaqui::before, .ns-0 h2#Yaqui::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/41/Flag_of_the_Pascua_Yaqui_Tribe_of_Arizona.svg/40px-Flag_of_the_Pascua_Yaqui_Tribe_of_Arizona.svg.png');}
.ns-0 h2 #Ye\'kwana::before, .ns-0 h2#Ye\'kwana::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/06/Flag_of_Venezuela.svg/40px-Flag_of_Venezuela.svg.png');}
.ns-0 h2 #Yemeni_Arabic::before, .ns-0 h2#Yemeni_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/89/Flag_of_Yemen.svg/40px-Flag_of_Yemen.svg.png');}
.ns-0 h2 #Yiddish::before, .ns-0 h2#Yiddish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d6/Proposed_Yiddish_flag.svg/40px-Proposed_Yiddish_flag.svg.png');}
.ns-0 h2 #Yilan_Creole::before, .ns-0 h2#Yilan_Creole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b4/Flag_of_Yilan_County.svg/40px-Flag_of_Yilan_County.svg.png');}
.ns-0 h2 #Yogad::before, .ns-0 h2#Yogad::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/bf/Flag_of_Echague,_Isabela_(2022-present).png/40px-Flag_of_Echague,_Isabela_(2022-present).png');}
.ns-0 h2 #Yola::before, .ns-0 h2#Yola::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/40/Flag_of_Yola.svg/40px-Flag_of_Yola.svg.png');}
.ns-0 h2 #Yonaguni::before, .ns-0 h2#Yonaguni::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/45/Flag_of_Yonaguni,_Okinawa.svg/40px-Flag_of_Yonaguni,_Okinawa.svg.png');}
.ns-0 h2 #Yucatec_Maya::before, .ns-0 h2#Yucatec_Maya::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/26/Flag_of_the_Republic_of_Yucatan.svg/40px-Flag_of_the_Republic_of_Yucatan.svg.png');}
.ns-0 h2 #Yuracare::before, .ns-0 h2#Yuracare::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f0/Flag_of_Cochabamba.svg/40px-Flag_of_Cochabamba.svg.png');}
.ns-0 h2 #Zaniza_Zapotec::before, .ns-0 h2#Zaniza_Zapotec::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b2/Flag_of_Oaxaca.svg/40px-Flag_of_Oaxaca.svg.png');}
.ns-0 h2 #Zarma::before, .ns-0 h2#Zarma::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f4/Flag_of_Niger.svg/40px-Flag_of_Niger.svg.png');}
.ns-0 h2 #Zazaki::before, .ns-0 h2#Zazaki::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/89/Flag_of_Zazaistan.svg/40px-Flag_of_Zazaistan.svg.png');}
.ns-0 h2 #Zealandic::before, .ns-0 h2#Zealandic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/54/Flag_of_Zeeland.svg/40px-Flag_of_Zeeland.svg.png');}
.ns-0 h2 #Zhuang::before, .ns-0 h2#Zhuang::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/64/Flag_of_the_Zhuang_people.svg/40px-Flag_of_the_Zhuang_people.svg.png');}
.ns-0 h2 #Zulu::before, .ns-0 h2#Zulu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/60/Flag_of_the_KwaZulu-Natal_Province.png/40px-Flag_of_the_KwaZulu-Natal_Province.png');}
jck1btrdumjdqvveo7urrvsepfplb4z
88881
88880
2026-08-06T23:09:06Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-WiktCountryFlags.css]]
88880
css
text/css
/*
* flags.css - adds flags to language headers.
* originally created by [[User:Prince Kassad]]
* this version is auto-generated from the list at [[Wiktionary:Language flags list]]
* ([[Special:Permalink/91668618]])
*/
.ns-0 h2 #Translingual::before, .ns-0 h2#Translingual::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6a/Earth_Day_Flag.png/40px-Earth_Day_Flag.png');}
.ns-0 h2 #English::before, .ns-0 h2#English::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0b/English_language.svg/40px-English_language.svg.png');}
.ns-0 h2 #\'Are\'are::before, .ns-0 h2#\'Are\'are::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/78/Flag_of_Guadalcanal.svg/40px-Flag_of_Guadalcanal.svg.png');}
.ns-0 h2 #Aari::before, .ns-0 h2#Aari::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8a/Flag_of_Southern_Ethiopia.svg/40px-Flag_of_Southern_Ethiopia.svg.png');}
.ns-0 h2 #Abau::before, .ns-0 h2#Abau::before, .ns-0 h2 #Krisa::before, .ns-0 h2#Krisa::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/de/Flag_of_Sandaun.svg/40px-Flag_of_Sandaun.svg.png');}
.ns-0 h2 #Abaza::before, .ns-0 h2#Abaza::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/27/Flag_of_Abazinia.svg/40px-Flag_of_Abazinia.svg.png');}
.ns-0 h2 #Abenaki::before, .ns-0 h2#Abenaki::before, .ns-0 h2 #Malecite-Passamaquoddy::before, .ns-0 h2#Malecite-Passamaquoddy::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/19/Wabanaki_Seal.png/40px-Wabanaki_Seal.png');}
.ns-0 h2 #Abkhaz::before, .ns-0 h2#Abkhaz::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7a/Flag_of_the_Republic_of_Abkhazia.svg/40px-Flag_of_the_Republic_of_Abkhazia.svg.png');}
.ns-0 h2 #Acehnese::before, .ns-0 h2#Acehnese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d8/Flag_of_Aceh_Sultanate.svg/40px-Flag_of_Aceh_Sultanate.svg.png');}
.ns-0 h2 #Acholi::before, .ns-0 h2#Acholi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/96/Flag_of_Acholi.svg/40px-Flag_of_Acholi.svg.png');}
.ns-0 h2 #Adzera::before, .ns-0 h2#Adzera::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a9/Flag_of_Morobe.png/40px-Flag_of_Morobe.png');}
.ns-0 h2 #Afar::before, .ns-0 h2#Afar::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/34/Flag_of_Djibouti.svg/40px-Flag_of_Djibouti.svg.png');}
.ns-0 h2 #Afrikaans::before, .ns-0 h2#Afrikaans::before, .ns-0 h2 #Fanagalo::before, .ns-0 h2#Fanagalo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/af/Flag_of_South_Africa.svg/40px-Flag_of_South_Africa.svg.png');}
.ns-0 h2 #Agutaynen::before, .ns-0 h2#Agutaynen::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/79/Flag_of_Agutaya,_Palawan.png/40px-Flag_of_Agutaya,_Palawan.png');}
.ns-0 h2 #Ahom::before, .ns-0 h2#Ahom::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8b/Ahom_insignia_plain.svg/40px-Ahom_insignia_plain.svg.png');}
.ns-0 h2 #Ainu::before, .ns-0 h2#Ainu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9b/Flag_of_Ainu.svg/40px-Flag_of_Ainu.svg.png');}
.ns-0 h2 #Akan::before, .ns-0 h2#Akan::before, .ns-0 h2 #Dagbani::before, .ns-0 h2#Dagbani::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/19/Flag_of_Ghana.svg/40px-Flag_of_Ghana.svg.png');}
.ns-0 h2 #Akha::before, .ns-0 h2#Akha::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5b/Flag_of_the_Akha_National_Development_Party.svg/40px-Flag_of_the_Akha_National_Development_Party.svg.png');}
.ns-0 h2 #Akkala_Sami::before, .ns-0 h2#Akkala_Sami::before, .ns-0 h2 #Inari_Sami::before, .ns-0 h2#Inari_Sami::before, .ns-0 h2 #Kemi_Sami::before, .ns-0 h2#Kemi_Sami::before, .ns-0 h2 #Kildin_Sami::before, .ns-0 h2#Kildin_Sami::before, .ns-0 h2 #Lule_Sami::before, .ns-0 h2#Lule_Sami::before, .ns-0 h2 #Northern_Sami::before, .ns-0 h2#Northern_Sami::before, .ns-0 h2 #Pite_Sami::before, .ns-0 h2#Pite_Sami::before, .ns-0 h2 #Skolt_Sami::before, .ns-0 h2#Skolt_Sami::before, .ns-0 h2 #Southern_Sami::before, .ns-0 h2#Southern_Sami::before, .ns-0 h2 #Ter_Sami::before, .ns-0 h2#Ter_Sami::before, .ns-0 h2 #Ume_Sami::before, .ns-0 h2#Ume_Sami::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1b/Sami_flag.svg/40px-Sami_flag.svg.png');}
.ns-0 h2 #Aklanon::before, .ns-0 h2#Aklanon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/90/PH-AKL_Flag.png/40px-PH-AKL_Flag.png');}
.ns-0 h2 #Albanian::before, .ns-0 h2#Albanian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/36/Flag_of_Albania.svg/40px-Flag_of_Albania.svg.png');}
.ns-0 h2 #Alemannic_German::before, .ns-0 h2#Alemannic_German::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/08/Flag_of_Switzerland_(Pantone).svg/40px-Flag_of_Switzerland_(Pantone).svg.png');}
.ns-0 h2 #Aleut::before, .ns-0 h2#Aleut::before, .ns-0 h2 #Inupiaq::before, .ns-0 h2#Inupiaq::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e6/Flag_of_Alaska.svg/40px-Flag_of_Alaska.svg.png');}
.ns-0 h2 #Algerian_Arabic::before, .ns-0 h2#Algerian_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/77/Flag_of_Algeria.svg/40px-Flag_of_Algeria.svg.png');}
.ns-0 h2 #Ambonese_Malay::before, .ns-0 h2#Ambonese_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c0/Lambang_Ambon.png/28px-Lambang_Ambon.png');}
.ns-0 h2 #American_Sign_Language::before, .ns-0 h2#American_Sign_Language::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a4/Flag_of_the_United_States.svg/40px-Flag_of_the_United_States.svg.png');}
.ns-0 h2 #Amharic::before, .ns-0 h2#Amharic::before, .ns-0 h2 #Argobba::before, .ns-0 h2#Argobba::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/71/Flag_of_Ethiopia.svg/40px-Flag_of_Ethiopia.svg.png');}
.ns-0 h2 #Amis::before, .ns-0 h2#Amis::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/28/Emblem_of_Hualien_County_2010.svg/37px-Emblem_of_Hualien_County_2010.svg.png');}
.ns-0 h2 #Ancient_Greek::before, .ns-0 h2#Ancient_Greek::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/58/Byzantine_imperial_flag,_14th_century,_square.svg/37px-Byzantine_imperial_flag,_14th_century,_square.svg.png');}
.ns-0 h2 #Andalusian_Arabic::before, .ns-0 h2#Andalusian_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3c/Green-White_Flag.svg/40px-Green-White_Flag.svg.png');}
.ns-0 h2 #Angevin::before, .ns-0 h2#Angevin::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/ad/Flag_of_Anjou.svg/40px-Flag_of_Anjou.svg.png');}
.ns-0 h2 #Angika::before, .ns-0 h2#Angika::before, .ns-0 h2 #Magahi::before, .ns-0 h2#Magahi::before, .ns-0 h2 #Maithili::before, .ns-0 h2#Maithili::before, .ns-0 h2 #Santali::before, .ns-0 h2#Santali::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a9/Jharkhand_Rajakiya_Chihna.svg/40px-Jharkhand_Rajakiya_Chihna.svg.png');}
.ns-0 h2 #Angolar::before, .ns-0 h2#Angolar::before, .ns-0 h2 #Sãotomense::before, .ns-0 h2#Sãotomense::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0a/Flag_of_São_Tomé_and_Príncipe.svg/40px-Flag_of_São_Tomé_and_Príncipe.svg.png');}
.ns-0 h2 #Anguthimri::before, .ns-0 h2#Anguthimri::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/04/Flag_of_Queensland.svg/40px-Flag_of_Queensland.svg.png');}
.ns-0 h2 #Annobonese::before, .ns-0 h2#Annobonese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/72/Bandera_Annobon.png/40px-Bandera_Annobon.png');}
.ns-0 h2 #Antigua_and_Barbuda_Creole_English::before, .ns-0 h2#Antigua_and_Barbuda_Creole_English::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/89/Flag_of_Antigua_and_Barbuda.svg/40px-Flag_of_Antigua_and_Barbuda.svg.png');}
.ns-0 h2 #Antillean_Creole::before, .ns-0 h2#Antillean_Creole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9f/Flag_of_Saint_Lucia.svg/40px-Flag_of_Saint_Lucia.svg.png');}
.ns-0 h2 #Ao::before, .ns-0 h2#Ao::before, .ns-0 h2 #Naga_Pidgin::before, .ns-0 h2#Naga_Pidgin::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Unofficial_flag_of_Nagaland.svg/40px-Unofficial_flag_of_Nagaland.svg.png');}
.ns-0 h2 #Arabic::before, .ns-0 h2#Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2b/Flag_of_the_Arab_League.svg/40px-Flag_of_the_Arab_League.svg.png');}
.ns-0 h2 #Aragonese::before, .ns-0 h2#Aragonese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/18/Flag_of_Aragon.svg/40px-Flag_of_Aragon.svg.png');}
.ns-0 h2 #Arapaho::before, .ns-0 h2#Arapaho::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/85/Flag_of_Arapaho_Nation.svg/40px-Flag_of_Arapaho_Nation.svg.png');}
.ns-0 h2 #Armenian::before, .ns-0 h2#Armenian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Flag_of_Armenia.svg/40px-Flag_of_Armenia.svg.png');}
.ns-0 h2 #Aromanian::before, .ns-0 h2#Aromanian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/97/Aromanian_flag.svg/40px-Aromanian_flag.svg.png');}
.ns-0 h2 #Asi::before, .ns-0 h2#Asi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/39/Flag_of_Banton,_Romblon.png/40px-Flag_of_Banton,_Romblon.png');}
.ns-0 h2 #Assamese::before, .ns-0 h2#Assamese::before, .ns-0 h2 #Bodo_\(India\)::before, .ns-0 h2#Bodo_\(India\)::before, .ns-0 h2 #Gujarati::before, .ns-0 h2#Gujarati::before, .ns-0 h2 #Hindi::before, .ns-0 h2#Hindi::before, .ns-0 h2 #Malayalam::before, .ns-0 h2#Malayalam::before, .ns-0 h2 #Marathi::before, .ns-0 h2#Marathi::before, .ns-0 h2 #Telugu::before, .ns-0 h2#Telugu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/41/Flag_of_India.svg/40px-Flag_of_India.svg.png');}
.ns-0 h2 #Assyrian_Neo-Aramaic::before, .ns-0 h2#Assyrian_Neo-Aramaic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/11/Flag_of_the_Assyrians_(gold_and_blue_Assur).svg/40px-Flag_of_the_Assyrians_(gold_and_blue_Assur).svg.png');}
.ns-0 h2 #Asturian::before, .ns-0 h2#Asturian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3e/Flag_of_Asturias.svg/40px-Flag_of_Asturias.svg.png');}
.ns-0 h2 #Atayal::before, .ns-0 h2#Atayal::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d0/Flag_of_New_Taipei_City.svg/40px-Flag_of_New_Taipei_City.svg.png');}
.ns-0 h2 #Atikamekw::before, .ns-0 h2#Atikamekw::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5f/Flag_of_Quebec.svg/40px-Flag_of_Quebec.svg.png');}
.ns-0 h2 #Aukan::before, .ns-0 h2#Aukan::before, .ns-0 h2 #Saramaccan::before, .ns-0 h2#Saramaccan::before, .ns-0 h2 #Sranan_Tongo::before, .ns-0 h2#Sranan_Tongo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/60/Flag_of_Suriname.svg/40px-Flag_of_Suriname.svg.png');}
.ns-0 h2 #Australian_Kriol::before, .ns-0 h2#Australian_Kriol::before, .ns-0 h2 #Torres_Strait_Creole::before, .ns-0 h2#Torres_Strait_Creole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3f/Australian_Aboriginal_Flag.svg/40px-Australian_Aboriginal_Flag.svg.png');}
.ns-0 h2 #Avar::before, .ns-0 h2#Avar::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/80/Flag_of_Avars.svg/40px-Flag_of_Avars.svg.png');}
.ns-0 h2 #Avestan::before, .ns-0 h2#Avestan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f9/Faravahar-Gold.svg/40px-Faravahar-Gold.svg.png');}
.ns-0 h2 #Awadhi::before, .ns-0 h2#Awadhi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f2/Flag_of_Awadh.svg/40px-Flag_of_Awadh.svg.png');}
.ns-0 h2 #Aymara::before, .ns-0 h2#Aymara::before, .ns-0 h2 #Bolivian_Sign_Language::before, .ns-0 h2#Bolivian_Sign_Language::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/de/Flag_of_Bolivia_(state).svg/40px-Flag_of_Bolivia_(state).svg.png');}
.ns-0 h2 #Azerbaijani::before, .ns-0 h2#Azerbaijani::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Flag_of_Azerbaijan.svg/40px-Flag_of_Azerbaijan.svg.png');}
.ns-0 h2 #Baba_Malay::before, .ns-0 h2#Baba_Malay::before, .ns-0 h2 #Singapore_Sign_Language::before, .ns-0 h2#Singapore_Sign_Language::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/48/Flag_of_Singapore.svg/40px-Flag_of_Singapore.svg.png');}
.ns-0 h2 #Bacanese_Malay::before, .ns-0 h2#Bacanese_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/08/Bendera_Sultan_Bacan.svg/40px-Bendera_Sultan_Bacan.svg.png');}
.ns-0 h2 #Badaga::before, .ns-0 h2#Badaga::before, .ns-0 h2 #Tamil::before, .ns-0 h2#Tamil::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7c/Seal_of_Tamil_Nadu.svg/40px-Seal_of_Tamil_Nadu.svg.png');}
.ns-0 h2 #Bagheli::before, .ns-0 h2#Bagheli::before, .ns-0 h2 #Bundeli::before, .ns-0 h2#Bundeli::before, .ns-0 h2 #Korku::before, .ns-0 h2#Korku::before, .ns-0 h2 #Malvi::before, .ns-0 h2#Malvi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/ae/Emblem_of_Madhya_Pradesh.svg/40px-Emblem_of_Madhya_Pradesh.svg.png');}
.ns-0 h2 #Baharna_Arabic::before, .ns-0 h2#Baharna_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Flag_of_Bahrain.svg/40px-Flag_of_Bahrain.svg.png');}
.ns-0 h2 #Bahnar::before, .ns-0 h2#Bahnar::before, .ns-0 h2 #Hrê::before, .ns-0 h2#Hrê::before, .ns-0 h2 #Vietnamese::before, .ns-0 h2#Vietnamese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/21/Flag_of_Vietnam.svg/40px-Flag_of_Vietnam.svg.png');}
.ns-0 h2 #Bajan::before, .ns-0 h2#Bajan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ef/Flag_of_Barbados.svg/40px-Flag_of_Barbados.svg.png');}
.ns-0 h2 #Balinese::before, .ns-0 h2#Balinese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/89/Historical_Flag_of_Bali.png/40px-Historical_Flag_of_Bali.png');}
.ns-0 h2 #Balinese_Malay::before, .ns-0 h2#Balinese_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/10/Coat_of_arms_of_Bali.svg/35px-Coat_of_arms_of_Bali.svg.png');}
.ns-0 h2 #Balti::before, .ns-0 h2#Balti::before, .ns-0 h2 #Burushaski::before, .ns-0 h2#Burushaski::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/62/Flag_of_Hunza.svg/40px-Flag_of_Hunza.svg.png');}
.ns-0 h2 #Baluchi::before, .ns-0 h2#Baluchi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0f/Flag_of_Balochistan.svg/40px-Flag_of_Balochistan.svg.png');}
.ns-0 h2 #Bambara::before, .ns-0 h2#Bambara::before, .ns-0 h2 #Soninke::before, .ns-0 h2#Soninke::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/92/Flag_of_Mali.svg/40px-Flag_of_Mali.svg.png');}
.ns-0 h2 #Banjarese::before, .ns-0 h2#Banjarese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a5/Banjar_Sultanate_Flag.svg/40px-Banjar_Sultanate_Flag.svg.png');}
.ns-0 h2 #Baoule::before, .ns-0 h2#Baoule::before, .ns-0 h2 #Dyula::before, .ns-0 h2#Dyula::before{padding-right:5px;
content:url("//upload.wikimedia.org/wikipedia/commons/thumb/f/fe/Flag_of_Côte_d'Ivoire.svg/40px-Flag_of_Côte_d'Ivoire.svg.png");}
.ns-0 h2 #Bari::before, .ns-0 h2#Bari::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d2/Flag_of_the_Bari_people.svg/40px-Flag_of_the_Bari_people.svg.png');}
.ns-0 h2 #Barikanchi::before, .ns-0 h2#Barikanchi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2b/Flag_of_the_Nigerian_Armed_Forces.svg/40px-Flag_of_the_Nigerian_Armed_Forces.svg.png');}
.ns-0 h2 #Bashkir::before, .ns-0 h2#Bashkir::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3d/Flag_of_Bashkortostan.svg/40px-Flag_of_Bashkortostan.svg.png');}
.ns-0 h2 #Basque::before, .ns-0 h2#Basque::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2d/Flag_of_the_Basque_Country.svg/40px-Flag_of_the_Basque_Country.svg.png');}
.ns-0 h2 #Bavarian::before, .ns-0 h2#Bavarian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/20/Flag_of_Bavaria_(lozengy).svg/40px-Flag_of_Bavaria_(lozengy).svg.png');}
.ns-0 h2 #Beja::before, .ns-0 h2#Beja::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6e/Flag_of_the_Beja_Congress.svg/40px-Flag_of_the_Beja_Congress.svg.png');}
.ns-0 h2 #Belarusian::before, .ns-0 h2#Belarusian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/85/Flag_of_Belarus.svg/40px-Flag_of_Belarus.svg.png');}
.ns-0 h2 #Belizean_Creole::before, .ns-0 h2#Belizean_Creole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e7/Flag_of_Belize.svg/40px-Flag_of_Belize.svg.png');}
.ns-0 h2 #Bella_Coola::before, .ns-0 h2#Bella_Coola::before, .ns-0 h2 #Halkomelem::before, .ns-0 h2#Halkomelem::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b8/Flag_of_British_Columbia.svg/40px-Flag_of_British_Columbia.svg.png');}
.ns-0 h2 #Bemba::before, .ns-0 h2#Bemba::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/06/Flag_of_Zambia.svg/40px-Flag_of_Zambia.svg.png');}
.ns-0 h2 #Bengali::before, .ns-0 h2#Bengali::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/80/Bengali_language.svg/40px-Bengali_language.svg.png');}
.ns-0 h2 #Berau_Malay::before, .ns-0 h2#Berau_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7f/East_Borneo_Sultanate_Flags.png/40px-East_Borneo_Sultanate_Flags.png');}
.ns-0 h2 #Berbice_Creole_Dutch::before, .ns-0 h2#Berbice_Creole_Dutch::before, .ns-0 h2 #Skepi_Creole_Dutch::before, .ns-0 h2#Skepi_Creole_Dutch::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/41/Flag_of_the_Dutch_West_India_Company.svg/40px-Flag_of_the_Dutch_West_India_Company.svg.png');}
.ns-0 h2 #Betawi::before, .ns-0 h2#Betawi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/eb/Coat_of_arms_of_Jakarta.svg/30px-Coat_of_arms_of_Jakarta.svg.png');}
.ns-0 h2 #Bhojpuri::before, .ns-0 h2#Bhojpuri::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Seal_of_Uttar_Pradesh.svg/40px-Seal_of_Uttar_Pradesh.svg.png');}
.ns-0 h2 #Bihari::before, .ns-0 h2#Bihari::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e9/Seal_of_Bihar.svg/40px-Seal_of_Bihar.svg.png');}
.ns-0 h2 #Bima::before, .ns-0 h2#Bima::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6d/Bendera_Kesultanan_Bima.png/40px-Bendera_Kesultanan_Bima.png');}
.ns-0 h2 #Binukid::before, .ns-0 h2#Binukid::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c8/Flag_of_the_Province_of_Bukidnon.svg/40px-Flag_of_the_Province_of_Bukidnon.svg.png');}
.ns-0 h2 #Bislama::before, .ns-0 h2#Bislama::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Flag_of_Vanuatu.svg/40px-Flag_of_Vanuatu.svg.png');}
.ns-0 h2 #Blackfoot::before, .ns-0 h2#Blackfoot::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8e/Flag_of_the_Blackfoot_Confederacy.jpg/40px-Flag_of_the_Blackfoot_Confederacy.jpg');}
.ns-0 h2 #Boon::before, .ns-0 h2#Boon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a6/Flag_of_Jubaland_(Somalia).svg/40px-Flag_of_Jubaland_(Somalia).svg.png');}
.ns-0 h2 #Bourbonnais-Berrichon::before, .ns-0 h2#Bourbonnais-Berrichon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f9/Flag_of_Bourbonnais.svg/40px-Flag_of_Bourbonnais.svg.png');}
.ns-0 h2 #Bourguignon::before, .ns-0 h2#Bourguignon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3c/Flag_of_Bourgogne.svg/40px-Flag_of_Bourgogne.svg.png');}
.ns-0 h2 #Brahui::before, .ns-0 h2#Brahui::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/59/FlagofKalat.svg/40px-FlagofKalat.svg.png');}
.ns-0 h2 #Brazilian_Sign_Language::before, .ns-0 h2#Brazilian_Sign_Language::before, .ns-0 h2 #Old_Tupi::before, .ns-0 h2#Old_Tupi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/05/Flag_of_Brazil.svg/40px-Flag_of_Brazil.svg.png');}
.ns-0 h2 #Breton::before, .ns-0 h2#Breton::before, .ns-0 h2 #Gallo::before, .ns-0 h2#Gallo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7d/Flag_of_Brittany.svg/40px-Flag_of_Brittany.svg.png');}
.ns-0 h2 #Brokkat::before, .ns-0 h2#Brokkat::before, .ns-0 h2 #Bumthangkha::before, .ns-0 h2#Bumthangkha::before, .ns-0 h2 #Chocangaca::before, .ns-0 h2#Chocangaca::before, .ns-0 h2 #Dzala::before, .ns-0 h2#Dzala::before, .ns-0 h2 #Dzongkha::before, .ns-0 h2#Dzongkha::before, .ns-0 h2 #Kurtöp::before, .ns-0 h2#Kurtöp::before, .ns-0 h2 #Tshangla::before, .ns-0 h2#Tshangla::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/91/Flag_of_Bhutan.svg/40px-Flag_of_Bhutan.svg.png');}
.ns-0 h2 #Brooke\'s_Point_Palawano::before, .ns-0 h2#Brooke\'s_Point_Palawano::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a8/Flag_of_Palawan,_Philippines.svg/40px-Flag_of_Palawan,_Philippines.svg.png');}
.ns-0 h2 #Brunei_Malay::before, .ns-0 h2#Brunei_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9c/Flag_of_Brunei.svg/40px-Flag_of_Brunei.svg.png');}
.ns-0 h2 #Bulgarian::before, .ns-0 h2#Bulgarian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9a/Flag_of_Bulgaria.svg/40px-Flag_of_Bulgaria.svg.png');}
.ns-0 h2 #Bunun::before, .ns-0 h2#Bunun::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/89/Flag_of_Nantou_County.svg/40px-Flag_of_Nantou_County.svg.png');}
.ns-0 h2 #Burmese::before, .ns-0 h2#Burmese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8c/Flag_of_Myanmar.svg/40px-Flag_of_Myanmar.svg.png');}
.ns-0 h2 #Buryat::before, .ns-0 h2#Buryat::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/68/Flag_of_Buryatia.svg/40px-Flag_of_Buryatia.svg.png');}
.ns-0 h2 #Butuanon::before, .ns-0 h2#Butuanon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7b/Butuancityflag.jpg/40px-Butuancityflag.jpg');}
.ns-0 h2 #Cahuilla::before, .ns-0 h2#Cahuilla::before, .ns-0 h2 #Northern_Valley_Yokuts::before, .ns-0 h2#Northern_Valley_Yokuts::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/01/Flag_of_California.svg/40px-Flag_of_California.svg.png');}
.ns-0 h2 #Calabrian_Greek::before, .ns-0 h2#Calabrian_Greek::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8b/Flag_of_Calabria.svg/40px-Flag_of_Calabria.svg.png');}
.ns-0 h2 #Caluyanun::before, .ns-0 h2#Caluyanun::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/28/Flag_of_Caluya,_Antique.png/40px-Flag_of_Caluya,_Antique.png');}
.ns-0 h2 #Caló::before, .ns-0 h2#Caló::before, .ns-0 h2 #Romani::before, .ns-0 h2#Romani::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/10/Flag_of_the_Romani_people.svg/40px-Flag_of_the_Romani_people.svg.png');}
.ns-0 h2 #Cameroon_Pidgin::before, .ns-0 h2#Cameroon_Pidgin::before, .ns-0 h2 #Ghomala\'::before, .ns-0 h2#Ghomala\'::before, .ns-0 h2 #Matal::before, .ns-0 h2#Matal::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4f/Flag_of_Cameroon.svg/40px-Flag_of_Cameroon.svg.png');}
.ns-0 h2 #Cantonese::before, .ns-0 h2#Cantonese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5b/Flag_of_Hong_Kong.svg/40px-Flag_of_Hong_Kong.svg.png');}
.ns-0 h2 #Caribbean_Hindustani::before, .ns-0 h2#Caribbean_Hindustani::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/21/Flag_of_Suriname_and_Guyana.png/40px-Flag_of_Suriname_and_Guyana.png');}
.ns-0 h2 #Carolinian::before, .ns-0 h2#Carolinian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e0/Flag_of_the_Northern_Mariana_Islands.svg/40px-Flag_of_the_Northern_Mariana_Islands.svg.png');}
.ns-0 h2 #Carpathian_Rusyn::before, .ns-0 h2#Carpathian_Rusyn::before, .ns-0 h2 #Pannonian_Rusyn::before, .ns-0 h2#Pannonian_Rusyn::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/27/Flag_of_Rusyns_2007.svg/40px-Flag_of_Rusyns_2007.svg.png');}
.ns-0 h2 #Catalan::before, .ns-0 h2#Catalan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/ce/Flag_of_Catalonia.svg/40px-Flag_of_Catalonia.svg.png');}
.ns-0 h2 #Cavineña::before, .ns-0 h2#Cavineña::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/70/Flag_of_Beni.svg/40px-Flag_of_Beni.svg.png');}
.ns-0 h2 #Cebuano::before, .ns-0 h2#Cebuano::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/11/Flag_of_Cebu_(province).svg/40px-Flag_of_Cebu_(province).svg.png');}
.ns-0 h2 #Central_Atlas_Tamazight::before, .ns-0 h2#Central_Atlas_Tamazight::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Berber_flag.svg/40px-Berber_flag.svg.png');}
.ns-0 h2 #Central_Bikol::before, .ns-0 h2#Central_Bikol::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6e/Flag_of_the_Katipuneros_of_Bicol.svg/40px-Flag_of_the_Katipuneros_of_Bicol.svg.png');}
.ns-0 h2 #Central_Dusun::before, .ns-0 h2#Central_Dusun::before, .ns-0 h2 #Sabah_Malay::before, .ns-0 h2#Sabah_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b5/Flag_of_Sabah.svg/40px-Flag_of_Sabah.svg.png');}
.ns-0 h2 #Central_Kurdish::before, .ns-0 h2#Central_Kurdish::before, .ns-0 h2 #Northern_Kurdish::before, .ns-0 h2#Northern_Kurdish::before, .ns-0 h2 #Southern_Kurdish::before, .ns-0 h2#Southern_Kurdish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/35/Flag_of_Kurdistan.svg/40px-Flag_of_Kurdistan.svg.png');}
.ns-0 h2 #Central_Mansi::before, .ns-0 h2#Central_Mansi::before, .ns-0 h2 #Eastern_Khanty::before, .ns-0 h2#Eastern_Khanty::before, .ns-0 h2 #Northern_Khanty::before, .ns-0 h2#Northern_Khanty::before, .ns-0 h2 #Northern_Mansi::before, .ns-0 h2#Northern_Mansi::before, .ns-0 h2 #Southern_Khanty::before, .ns-0 h2#Southern_Khanty::before, .ns-0 h2 #Southern_Mansi::before, .ns-0 h2#Southern_Mansi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/70/Flag_of_Yugra.svg/40px-Flag_of_Yugra.svg.png');}
.ns-0 h2 #Central_Nahuatl::before, .ns-0 h2#Central_Nahuatl::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/26/Flag_of_Tlaxcala.svg/40px-Flag_of_Tlaxcala.svg.png');}
.ns-0 h2 #Chadian_Arabic::before, .ns-0 h2#Chadian_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4b/Flag_of_Chad.svg/40px-Flag_of_Chad.svg.png');}
.ns-0 h2 #Chakma::before, .ns-0 h2#Chakma::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d7/Chakma_Flag.jpg/40px-Chakma_Flag.jpg');}
.ns-0 h2 #Chaldean_Neo-Aramaic::before, .ns-0 h2#Chaldean_Neo-Aramaic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/95/Chaldean_flag.svg/40px-Chaldean_flag.svg.png');}
.ns-0 h2 #Chamorro::before, .ns-0 h2#Chamorro::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/07/Flag_of_Guam.svg/40px-Flag_of_Guam.svg.png');}
.ns-0 h2 #Champenois::before, .ns-0 h2#Champenois::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f1/Flag_of_Champagne-Ardenne.svg/40px-Flag_of_Champagne-Ardenne.svg.png');}
.ns-0 h2 #Chavacano::before, .ns-0 h2#Chavacano::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/16/Flag_of_Zamboanga.jpg/40px-Flag_of_Zamboanga.jpg');}
.ns-0 h2 #Chechen::before, .ns-0 h2#Chechen::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/13/Flag_of_the_Chechen_Republic.svg/40px-Flag_of_the_Chechen_Republic.svg.png');}
.ns-0 h2 #Cherokee::before, .ns-0 h2#Cherokee::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Flag_of_the_Cherokee_Nation.svg/40px-Flag_of_the_Cherokee_Nation.svg.png');}
.ns-0 h2 #Cheyenne::before, .ns-0 h2#Cheyenne::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e7/Flag_of_Northern_Cheyenne.svg/40px-Flag_of_Northern_Cheyenne.svg.png');}
.ns-0 h2 #Chhattisgarhi::before, .ns-0 h2#Chhattisgarhi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/87/Coat_of_arms_of_Chhattisgarh.svg/40px-Coat_of_arms_of_Chhattisgarh.svg.png');}
.ns-0 h2 #Chibcha::before, .ns-0 h2#Chibcha::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8d/Bandera_de_la_confederacion_muisca.png/33px-Bandera_de_la_confederacion_muisca.png');}
.ns-0 h2 #Chichewa::before, .ns-0 h2#Chichewa::before, .ns-0 h2 #Tumbuka::before, .ns-0 h2#Tumbuka::before, .ns-0 h2 #Yao_\(Africa\)::before, .ns-0 h2#Yao_\(Africa\)::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Flag_of_Malawi.svg/40px-Flag_of_Malawi.svg.png');}
.ns-0 h2 #Chickasaw::before, .ns-0 h2#Chickasaw::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/74/Flag_of_the_Chickasaw_Nation.svg/40px-Flag_of_the_Chickasaw_Nation.svg.png');}
.ns-0 h2 #Chimwiini::before, .ns-0 h2#Chimwiini::before, .ns-0 h2 #Maay::before, .ns-0 h2#Maay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8b/Flag_of_South_West_State_of_Somalia.svg/40px-Flag_of_South_West_State_of_Somalia.svg.png');}
.ns-0 h2 #Chinese::before, .ns-0 h2#Chinese::before, .ns-0 h2 #Chinese_Pidgin_English::before, .ns-0 h2#Chinese_Pidgin_English::before, .ns-0 h2 #Mandarin::before, .ns-0 h2#Mandarin::before{padding-right:5px;
content:url("//upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Flag_of_the_People's_Republic_of_China.svg/40px-Flag_of_the_People's_Republic_of_China.svg.png");}
.ns-0 h2 #Chittagonian::before, .ns-0 h2#Chittagonian::before, .ns-0 h2 #Sylheti::before, .ns-0 h2#Sylheti::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f9/Flag_of_Bangladesh.svg/40px-Flag_of_Bangladesh.svg.png');}
.ns-0 h2 #Choctaw::before, .ns-0 h2#Choctaw::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a6/Choctaw_flag.svg/40px-Choctaw_flag.svg.png');}
.ns-0 h2 #Chukchi::before, .ns-0 h2#Chukchi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a0/Flag_of_Chukotka.svg/40px-Flag_of_Chukotka.svg.png');}
.ns-0 h2 #Church_Slavonic::before, .ns-0 h2#Church_Slavonic::before, .ns-0 h2 #Old_Church_Slavonic::before, .ns-0 h2#Old_Church_Slavonic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8e/Bulgarian_Orthodox_Cross_6.svg/33px-Bulgarian_Orthodox_Cross_6.svg.png');}
.ns-0 h2 #Chuukese::before, .ns-0 h2#Chuukese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/55/Flag_of_Chuuk.svg/40px-Flag_of_Chuuk.svg.png');}
.ns-0 h2 #Chuvash::before, .ns-0 h2#Chuvash::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d7/Flag_of_Chuvashia.svg/40px-Flag_of_Chuvashia.svg.png');}
.ns-0 h2 #Cimbrian::before, .ns-0 h2#Cimbrian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/09/Flag_of_the_Province_of_Trento.svg/40px-Flag_of_the_Province_of_Trento.svg.png');}
.ns-0 h2 #Classical_Nahuatl::before, .ns-0 h2#Classical_Nahuatl::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/87/Aztec_Triple_Alliance.png/40px-Aztec_Triple_Alliance.png');}
.ns-0 h2 #Coptic::before, .ns-0 h2#Coptic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/71/Coptic_cross.svg/35px-Coptic_cross.svg.png');}
.ns-0 h2 #Cornish::before, .ns-0 h2#Cornish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b8/Flag_of_Cornwall.svg/40px-Flag_of_Cornwall.svg.png');}
.ns-0 h2 #Corsican::before, .ns-0 h2#Corsican::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7c/Flag_of_Corsica.svg/40px-Flag_of_Corsica.svg.png');}
.ns-0 h2 #Cree::before, .ns-0 h2#Cree::before, .ns-0 h2 #Dogrib::before, .ns-0 h2#Dogrib::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c1/Flag_of_the_Northwest_Territories.svg/40px-Flag_of_the_Northwest_Territories.svg.png');}
.ns-0 h2 #Creek::before, .ns-0 h2#Creek::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d4/State_of_Muskogee_(Florida,_1799-1803).svg/40px-State_of_Muskogee_(Florida,_1799-1803).svg.png');}
.ns-0 h2 #Crimean_Tatar::before, .ns-0 h2#Crimean_Tatar::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/08/Flag_of_the_Crimean_Tatar_people.svg/40px-Flag_of_the_Crimean_Tatar_people.svg.png');}
.ns-0 h2 #Croatian_Sign_Language::before, .ns-0 h2#Croatian_Sign_Language::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1b/Flag_of_Croatia.svg/40px-Flag_of_Croatia.svg.png');}
.ns-0 h2 #Cuban_Sign_Language::before, .ns-0 h2#Cuban_Sign_Language::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/bd/Flag_of_Cuba.svg/40px-Flag_of_Cuba.svg.png');}
.ns-0 h2 #Cubeo::before, .ns-0 h2#Cubeo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c0/Flag_of_Vaupés.svg/40px-Flag_of_Vaupés.svg.png');}
.ns-0 h2 #Cuyunon::before, .ns-0 h2#Cuyunon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Flag_of_Cuyo,_Palawan.png/40px-Flag_of_Cuyo,_Palawan.png');}
.ns-0 h2 #Cypriot_Arabic::before, .ns-0 h2#Cypriot_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d4/Flag_of_Cyprus.svg/40px-Flag_of_Cyprus.svg.png');}
.ns-0 h2 #Czech::before, .ns-0 h2#Czech::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/cb/Flag_of_the_Czech_Republic.svg/40px-Flag_of_the_Czech_Republic.svg.png');}
.ns-0 h2 #Dahalik::before, .ns-0 h2#Dahalik::before, .ns-0 h2 #Tigre::before, .ns-0 h2#Tigre::before, .ns-0 h2 #Tigrinya::before, .ns-0 h2#Tigrinya::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/29/Flag_of_Eritrea.svg/40px-Flag_of_Eritrea.svg.png');}
.ns-0 h2 #Dakota::before, .ns-0 h2#Dakota::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ee/Flag_of_North_Dakota.svg/40px-Flag_of_North_Dakota.svg.png');}
.ns-0 h2 #Dalmatian::before, .ns-0 h2#Dalmatian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Flag_of_Kingdom_of_Dalmatia_(1852-1860).svg/40px-Flag_of_Kingdom_of_Dalmatia_(1852-1860).svg.png');}
.ns-0 h2 #Danish::before, .ns-0 h2#Danish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9c/Flag_of_Denmark.svg/40px-Flag_of_Denmark.svg.png');}
.ns-0 h2 #Dhivehi::before, .ns-0 h2#Dhivehi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0f/Flag_of_Maldives.svg/40px-Flag_of_Maldives.svg.png');}
.ns-0 h2 #Dhofari_Arabic::before, .ns-0 h2#Dhofari_Arabic::before, .ns-0 h2 #Omani_Arabic::before, .ns-0 h2#Omani_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Flag_of_Oman.svg/40px-Flag_of_Oman.svg.png');}
.ns-0 h2 #Dinka::before, .ns-0 h2#Dinka::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1d/Flag_of_Dinka_people.svg/40px-Flag_of_Dinka_people.svg.png');}
.ns-0 h2 #Dutch::before, .ns-0 h2#Dutch::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/20/Flag_of_the_Netherlands.svg/40px-Flag_of_the_Netherlands.svg.png');}
.ns-0 h2 #East_Circassian::before, .ns-0 h2#East_Circassian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d8/Flag_of_Kabardino-Balkaria.svg/40px-Flag_of_Kabardino-Balkaria.svg.png');}
.ns-0 h2 #East_Futuna::before, .ns-0 h2#East_Futuna::before, .ns-0 h2 #Wallisian::before, .ns-0 h2#Wallisian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d2/Flag_of_Wallis_and_Futuna.svg/40px-Flag_of_Wallis_and_Futuna.svg.png');}
.ns-0 h2 #Eastern_Durango_Nahuatl::before, .ns-0 h2#Eastern_Durango_Nahuatl::before, .ns-0 h2 #Western_Durango_Nahuatl::before, .ns-0 h2#Western_Durango_Nahuatl::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4b/Coat_of_arms_of_Durango.svg/35px-Coat_of_arms_of_Durango.svg.png');}
.ns-0 h2 #Eastern_Maninkakan::before, .ns-0 h2#Eastern_Maninkakan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ed/Flag_of_Guinea.svg/40px-Flag_of_Guinea.svg.png');}
.ns-0 h2 #Eastern_Mari::before, .ns-0 h2#Eastern_Mari::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a7/Flag_of_Mari_El.svg/40px-Flag_of_Mari_El.svg.png');}
.ns-0 h2 #Edo::before, .ns-0 h2#Edo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Edo_State_Flag.png/40px-Edo_State_Flag.png');}
.ns-0 h2 #Egyptian::before, .ns-0 h2#Egyptian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1c/Ancient_Egyptian_Culture_Flag.webp/40px-Ancient_Egyptian_Culture_Flag.webp');}
.ns-0 h2 #Egyptian_Arabic::before, .ns-0 h2#Egyptian_Arabic::before, .ns-0 h2 #Nobiin::before, .ns-0 h2#Nobiin::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fe/Flag_of_Egypt.svg/40px-Flag_of_Egypt.svg.png');}
.ns-0 h2 #Elfdalian::before, .ns-0 h2#Elfdalian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/ff/Älvdalen_vapen.svg/33px-Älvdalen_vapen.svg.png');}
.ns-0 h2 #Emilian::before, .ns-0 h2#Emilian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1f/Flag_of_Bologna_2.png/40px-Flag_of_Bologna_2.png');}
.ns-0 h2 #Erzya::before, .ns-0 h2#Erzya::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/28/Erzya_Flag.svg/40px-Erzya_Flag.svg.png');}
.ns-0 h2 #Ese::before, .ns-0 h2#Ese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5d/Flag_of_Flag_Oro_new.png/40px-Flag_of_Flag_Oro_new.png');}
.ns-0 h2 #Esperanto::before, .ns-0 h2#Esperanto::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f5/Flag_of_Esperanto.svg/40px-Flag_of_Esperanto.svg.png');}
.ns-0 h2 #Estonian::before, .ns-0 h2#Estonian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8f/Flag_of_Estonia.svg/40px-Flag_of_Estonia.svg.png');}
.ns-0 h2 #Evenki::before, .ns-0 h2#Evenki::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/72/Flag_of_Evenks.svg/40px-Flag_of_Evenks.svg.png');}
.ns-0 h2 #Ewe::before, .ns-0 h2#Ewe::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b8/Flag_of_the_Ewe_people.svg/40px-Flag_of_the_Ewe_people.svg.png');}
.ns-0 h2 #Extremaduran::before, .ns-0 h2#Extremaduran::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/13/Flag_of_Extremadura,_Spain_(with_coat_of_arms).svg/40px-Flag_of_Extremadura,_Spain_(with_coat_of_arms).svg.png');}
.ns-0 h2 #Fala::before, .ns-0 h2#Fala::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b7/Bandera_de_Valverde_del_Fresno_(Cáceres).svg/40px-Bandera_de_Valverde_del_Fresno_(Cáceres).svg.png');}
.ns-0 h2 #Fang_\(Bantu\)::before, .ns-0 h2#Fang_\(Bantu\)::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/04/Flag_of_Gabon.svg/40px-Flag_of_Gabon.svg.png');}
.ns-0 h2 #Faroese::before, .ns-0 h2#Faroese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3c/Flag_of_the_Faroe_Islands.svg/40px-Flag_of_the_Faroe_Islands.svg.png');}
.ns-0 h2 #Fiji_Hindi::before, .ns-0 h2#Fiji_Hindi::before, .ns-0 h2 #Fijian::before, .ns-0 h2#Fijian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/ba/Flag_of_Fiji.svg/40px-Flag_of_Fiji.svg.png');}
.ns-0 h2 #Fingallian::before, .ns-0 h2#Fingallian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/22/Flag_of_County_Limerick.svg/40px-Flag_of_County_Limerick.svg.png');}
.ns-0 h2 #Finnish::before, .ns-0 h2#Finnish::before, .ns-0 h2 #Finnish_Sign_Language::before, .ns-0 h2#Finnish_Sign_Language::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Flag_of_Finland.svg/40px-Flag_of_Finland.svg.png');}
.ns-0 h2 #Fon::before, .ns-0 h2#Fon::before, .ns-0 h2 #Gun::before, .ns-0 h2#Gun::before, .ns-0 h2 #Ifè::before, .ns-0 h2#Ifè::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0a/Flag_of_Benin.svg/40px-Flag_of_Benin.svg.png');}
.ns-0 h2 #Forest_Nenets::before, .ns-0 h2#Forest_Nenets::before, .ns-0 h2 #Tundra_Nenets::before, .ns-0 h2#Tundra_Nenets::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/15/Flag_of_Nenets_Autonomous_District.svg/40px-Flag_of_Nenets_Autonomous_District.svg.png');}
.ns-0 h2 #Franc-Comtois::before, .ns-0 h2#Franc-Comtois::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/96/Flag_of_Franche-Comté.svg/40px-Flag_of_Franche-Comté.svg.png');}
.ns-0 h2 #Franco-Provençal::before, .ns-0 h2#Franco-Provençal::before{padding-right:5px;
content:url("//upload.wikimedia.org/wikipedia/commons/thumb/9/90/Flag_of_Valle_d'Aosta.svg/40px-Flag_of_Valle_d'Aosta.svg.png");}
.ns-0 h2 #French::before, .ns-0 h2#French::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Flag_of_France.svg/40px-Flag_of_France.svg.png');}
.ns-0 h2 #Friulian::before, .ns-0 h2#Friulian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/87/Bandiere_dal_Friûl.svg/40px-Bandiere_dal_Friûl.svg.png');}
.ns-0 h2 #Fula::before, .ns-0 h2#Fula::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/08/NE-CM-BF-GM_flag.svg/40px-NE-CM-BF-GM_flag.svg.png');}
.ns-0 h2 #Fur::before, .ns-0 h2#Fur::before, .ns-0 h2 #Sudanese_Arabic::before, .ns-0 h2#Sudanese_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/01/Flag_of_Sudan.svg/40px-Flag_of_Sudan.svg.png');}
.ns-0 h2 #Futuna-Aniwa::before, .ns-0 h2#Futuna-Aniwa::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/55/Tafea_Province_Flag.svg/40px-Tafea_Province_Flag.svg.png');}
.ns-0 h2 #Fyam::before, .ns-0 h2#Fyam::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/00/Plateau_State_Flag.jpg/40px-Plateau_State_Flag.jpg');}
.ns-0 h2 #Ga::before, .ns-0 h2#Ga::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/cd/Flag_of_Accra.svg/40px-Flag_of_Accra.svg.png');}
.ns-0 h2 #Gabadi::before, .ns-0 h2#Gabadi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/84/Flag_of_Central_Province,_Papua_New_Guinea.svg/40px-Flag_of_Central_Province,_Papua_New_Guinea.svg.png');}
.ns-0 h2 #Gagauz::before, .ns-0 h2#Gagauz::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/69/Flag_of_Gagauzia.svg/40px-Flag_of_Gagauzia.svg.png');}
.ns-0 h2 #Galician::before, .ns-0 h2#Galician::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/64/Flag_of_Galicia.svg/40px-Flag_of_Galicia.svg.png');}
.ns-0 h2 #Gallo-Italic_of_Basilicata::before, .ns-0 h2#Gallo-Italic_of_Basilicata::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/00/Flag_for_the_Lombards_of_Basilicata.png/40px-Flag_for_the_Lombards_of_Basilicata.png');}
.ns-0 h2 #Gallo-Italic_of_Sicily::before, .ns-0 h2#Gallo-Italic_of_Sicily::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/30/Flag_for_the_Lombards_of_Sicily.png/40px-Flag_for_the_Lombards_of_Sicily.png');}
.ns-0 h2 #Gamilaraay::before, .ns-0 h2#Gamilaraay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/00/Flag_of_New_South_Wales.svg/40px-Flag_of_New_South_Wales.svg.png');}
.ns-0 h2 #Garhwali::before, .ns-0 h2#Garhwali::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7d/Flag_of_the_Princely_State_of_Tehri_Garhwal.svg/40px-Flag_of_the_Princely_State_of_Tehri_Garhwal.svg.png');}
.ns-0 h2 #Garifuna::before, .ns-0 h2#Garifuna::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b1/Flag_of_Garifuna.svg/40px-Flag_of_Garifuna.svg.png');}
.ns-0 h2 #Garo::before, .ns-0 h2#Garo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/81/Garo_hills.png/40px-Garo_hills.png');}
.ns-0 h2 #Ge\'ez::before, .ns-0 h2#Ge\'ez::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/08/Lion_of_Judah_emblem_of_the_Ethiopian_Empire.svg/40px-Lion_of_Judah_emblem_of_the_Ethiopian_Empire.svg.png');}
.ns-0 h2 #Georgian::before, .ns-0 h2#Georgian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0f/Flag_of_Georgia.svg/40px-Flag_of_Georgia.svg.png');}
.ns-0 h2 #German::before, .ns-0 h2#German::before, .ns-0 h2 #German_Sign_Language::before, .ns-0 h2#German_Sign_Language::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/ba/Flag_of_Germany.svg/40px-Flag_of_Germany.svg.png');}
.ns-0 h2 #Ghadames::before, .ns-0 h2#Ghadames::before, .ns-0 h2 #Libyan_Arabic::before, .ns-0 h2#Libyan_Arabic::before, .ns-0 h2 #Nefusa::before, .ns-0 h2#Nefusa::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/05/Flag_of_Libya.svg/40px-Flag_of_Libya.svg.png');}
.ns-0 h2 #Gilbertese::before, .ns-0 h2#Gilbertese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d3/Flag_of_Kiribati.svg/40px-Flag_of_Kiribati.svg.png');}
.ns-0 h2 #Gorontalo::before, .ns-0 h2#Gorontalo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/01/Coat_of_arms_of_Gorontalo.svg/35px-Coat_of_arms_of_Gorontalo.svg.png');}
.ns-0 h2 #Greek::before, .ns-0 h2#Greek::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5c/Flag_of_Greece.svg/40px-Flag_of_Greece.svg.png');}
.ns-0 h2 #Green_Hmong::before, .ns-0 h2#Green_Hmong::before, .ns-0 h2 #White_Hmong::before, .ns-0 h2#White_Hmong::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0f/Hmong_Flag_(UNPO).svg/40px-Hmong_Flag_(UNPO).svg.png');}
.ns-0 h2 #Greenlandic::before, .ns-0 h2#Greenlandic::before, .ns-0 h2 #West_Greenlandic_Pidgin::before, .ns-0 h2#West_Greenlandic_Pidgin::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/09/Flag_of_Greenland.svg/40px-Flag_of_Greenland.svg.png');}
.ns-0 h2 #Grenadian_Creole_English::before, .ns-0 h2#Grenadian_Creole_English::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Flag_of_Grenada.svg/40px-Flag_of_Grenada.svg.png');}
.ns-0 h2 #Guajajára::before, .ns-0 h2#Guajajára::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/45/Bandeira_do_Maranhão.svg/40px-Bandeira_do_Maranhão.svg.png');}
.ns-0 h2 #Guanche::before, .ns-0 h2#Guanche::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b0/Flag_of_the_Canary_Islands.svg/40px-Flag_of_the_Canary_Islands.svg.png');}
.ns-0 h2 #Guerrero_Amuzgo::before, .ns-0 h2#Guerrero_Amuzgo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b8/Flag_of_Guerrero.svg/40px-Flag_of_Guerrero.svg.png');}
.ns-0 h2 #Guianese_Creole::before, .ns-0 h2#Guianese_Creole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/29/Flag_of_French_Guiana.svg/40px-Flag_of_French_Guiana.svg.png');}
.ns-0 h2 #Guinea-Bissau_Creole::before, .ns-0 h2#Guinea-Bissau_Creole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/01/Flag_of_Guinea-Bissau.svg/40px-Flag_of_Guinea-Bissau.svg.png');}
.ns-0 h2 #Gulf_Arabic::before, .ns-0 h2#Gulf_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/cb/Flag_of_the_United_Arab_Emirates.svg/40px-Flag_of_the_United_Arab_Emirates.svg.png');}
.ns-0 h2 #Gullah::before, .ns-0 h2#Gullah::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/ff/Black_American_Heritage_flag.svg/40px-Black_American_Heritage_flag.svg.png');}
.ns-0 h2 #Gurindji::before, .ns-0 h2#Gurindji::before, .ns-0 h2 #Warlpiri::before, .ns-0 h2#Warlpiri::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b7/Flag_of_the_Northern_Territory.svg/40px-Flag_of_the_Northern_Territory.svg.png');}
.ns-0 h2 #Gutnish::before, .ns-0 h2#Gutnish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c4/Gotlands_län_vapenflagga.svg/37px-Gotlands_län_vapenflagga.svg.png');}
.ns-0 h2 #Guyanese_Creole_English::before, .ns-0 h2#Guyanese_Creole_English::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/99/Flag_of_Guyana.svg/40px-Flag_of_Guyana.svg.png');}
.ns-0 h2 #Hadza::before, .ns-0 h2#Hadza::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/38/Flag_of_Tanzania.svg/40px-Flag_of_Tanzania.svg.png');}
.ns-0 h2 #Haida::before, .ns-0 h2#Haida::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8f/CHN_Crest.png/33px-CHN_Crest.png');}
.ns-0 h2 #Haitian_Creole::before, .ns-0 h2#Haitian_Creole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/56/Flag_of_Haiti.svg/40px-Flag_of_Haiti.svg.png');}
.ns-0 h2 #Hakka::before, .ns-0 h2#Hakka::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/84/Flag_of_Hakka_Party.svg/40px-Flag_of_Hakka_Party.svg.png');}
.ns-0 h2 #Halia::before, .ns-0 h2#Halia::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e4/Flag_of_Bougainville.svg/40px-Flag_of_Bougainville.svg.png');}
.ns-0 h2 #Harari::before, .ns-0 h2#Harari::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6f/Harari_Flag.svg/40px-Harari_Flag.svg.png');}
.ns-0 h2 #Haryanvi::before, .ns-0 h2#Haryanvi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e6/Flag_of_Haryana.svg/40px-Flag_of_Haryana.svg.png');}
.ns-0 h2 #Hassaniya_Arabic::before, .ns-0 h2#Hassaniya_Arabic::before, .ns-0 h2 #Zenaga::before, .ns-0 h2#Zenaga::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/43/Flag_of_Mauritania.svg/40px-Flag_of_Mauritania.svg.png');}
.ns-0 h2 #Hausa::before, .ns-0 h2#Hausa::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5d/Flag_of_the_Hausa_people.svg/40px-Flag_of_the_Hausa_people.svg.png');}
.ns-0 h2 #Hawaiian::before, .ns-0 h2#Hawaiian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ef/Flag_of_Hawaii.svg/40px-Flag_of_Hawaii.svg.png');}
.ns-0 h2 #Hebrew::before, .ns-0 h2#Hebrew::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d4/Flag_of_Israel.svg/40px-Flag_of_Israel.svg.png');}
.ns-0 h2 #Herero::before, .ns-0 h2#Herero::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3b/Namibia,_Botswana_and_Angola.gif/40px-Namibia,_Botswana_and_Angola.gif');}
.ns-0 h2 #Higaonon::before, .ns-0 h2#Higaonon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Ph_seal_misamis_oriental.2.png/40px-Ph_seal_misamis_oriental.2.png');}
.ns-0 h2 #Hijazi_Arabic::before, .ns-0 h2#Hijazi_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8f/Flag_of_Hejaz_(1920).svg/40px-Flag_of_Hejaz_(1920).svg.png');}
.ns-0 h2 #Hiligaynon::before, .ns-0 h2#Hiligaynon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0e/Flag_of_the_Province_of_Iloilo.svg/40px-Flag_of_the_Province_of_Iloilo.svg.png');}
.ns-0 h2 #Hiri_Motu::before, .ns-0 h2#Hiri_Motu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Flag_of_Port_Moresby.png/40px-Flag_of_Port_Moresby.png');}
.ns-0 h2 #Hokkien::before, .ns-0 h2#Hokkien::before, .ns-0 h2 #Min_Nan::before, .ns-0 h2#Min_Nan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/72/Flag_of_the_Republic_of_China.svg/40px-Flag_of_the_Republic_of_China.svg.png');}
.ns-0 h2 #Hungarian::before, .ns-0 h2#Hungarian::before, .ns-0 h2 #Hungarian_Sign_Language::before, .ns-0 h2#Hungarian_Sign_Language::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c1/Flag_of_Hungary.svg/40px-Flag_of_Hungary.svg.png');}
.ns-0 h2 #Hunsrik::before, .ns-0 h2#Hunsrik::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/63/Bandeira_do_Rio_Grande_do_Sul.svg/40px-Bandeira_do_Rio_Grande_do_Sul.svg.png');}
.ns-0 h2 #Iban::before, .ns-0 h2#Iban::before, .ns-0 h2 #Sarawak_Malay::before, .ns-0 h2#Sarawak_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7e/Flag_of_Sarawak.svg/40px-Flag_of_Sarawak.svg.png');}
.ns-0 h2 #Ibanag::before, .ns-0 h2#Ibanag::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/62/Flag_of_Cagayan.svg/40px-Flag_of_Cagayan.svg.png');}
.ns-0 h2 #Icelandic::before, .ns-0 h2#Icelandic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/ce/Flag_of_Iceland.svg/40px-Flag_of_Iceland.svg.png');}
.ns-0 h2 #Ido::before, .ns-0 h2#Ido::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f1/Flag_of_Ido.svg/40px-Flag_of_Ido.svg.png');}
.ns-0 h2 #Igala::before, .ns-0 h2#Igala::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0e/Kogi_State_Flag.svg/40px-Kogi_State_Flag.svg.png');}
.ns-0 h2 #Igbo::before, .ns-0 h2#Igbo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/82/Flag_of_Biafra.svg/40px-Flag_of_Biafra.svg.png');}
.ns-0 h2 #Ilocano::before, .ns-0 h2#Ilocano::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5d/PH-ILS_Flag.png/40px-PH-ILS_Flag.png');}
.ns-0 h2 #Indo-Portuguese::before, .ns-0 h2#Indo-Portuguese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/72/Flag_of_Portugal_(1640).svg/40px-Flag_of_Portugal_(1640).svg.png');}
.ns-0 h2 #Indonesian::before, .ns-0 h2#Indonesian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9f/Flag_of_Indonesia.svg/40px-Flag_of_Indonesia.svg.png');}
.ns-0 h2 #Ingrian::before, .ns-0 h2#Ingrian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/27/Flag_igora.svg/40px-Flag_igora.svg.png');}
.ns-0 h2 #Ingush::before, .ns-0 h2#Ingush::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/00/Flag_of_Ingushetia.svg/40px-Flag_of_Ingushetia.svg.png');}
.ns-0 h2 #Interlingua::before, .ns-0 h2#Interlingua::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9b/Bandiera_de_Interlingua.svg/40px-Bandiera_de_Interlingua.svg.png');}
.ns-0 h2 #Interlingue::before, .ns-0 h2#Interlingue::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5b/Flag_of_Interlingue.svg/40px-Flag_of_Interlingue.svg.png');}
.ns-0 h2 #Inuktitut::before, .ns-0 h2#Inuktitut::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/90/Flag_of_Nunavut.svg/40px-Flag_of_Nunavut.svg.png');}
.ns-0 h2 #Iranun::before, .ns-0 h2#Iranun::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b8/Seal_of_Maguindanao_del_Norte.png/40px-Seal_of_Maguindanao_del_Norte.png');}
.ns-0 h2 #Iraqi_Arabic::before, .ns-0 h2#Iraqi_Arabic::before, .ns-0 h2 #North_Mesopotamian_Arabic::before, .ns-0 h2#North_Mesopotamian_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f6/Flag_of_Iraq.svg/40px-Flag_of_Iraq.svg.png');}
.ns-0 h2 #Iraqw::before, .ns-0 h2#Iraqw::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Iraqw_Flag.png/40px-Iraqw_Flag.png');}
.ns-0 h2 #Irish::before, .ns-0 h2#Irish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/45/Flag_of_Ireland.svg/40px-Flag_of_Ireland.svg.png');}
.ns-0 h2 #Islander_Creole_English::before, .ns-0 h2#Islander_Creole_English::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/36/Flag_of_San_Andrés_y_Providencia.svg/40px-Flag_of_San_Andrés_y_Providencia.svg.png');}
.ns-0 h2 #Istriot::before, .ns-0 h2#Istriot::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/99/HRV_Rovinj_flag.svg/40px-HRV_Rovinj_flag.svg.png');}
.ns-0 h2 #Italian::before, .ns-0 h2#Italian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/03/Flag_of_Italy.svg/40px-Flag_of_Italy.svg.png');}
.ns-0 h2 #Italiot_Greek::before, .ns-0 h2#Italiot_Greek::before, .ns-0 h2 #Tarantino::before, .ns-0 h2#Tarantino::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b8/Flag_of_Apulia.svg/40px-Flag_of_Apulia.svg.png');}
.ns-0 h2 #Itsekiri::before, .ns-0 h2#Itsekiri::before, .ns-0 h2 #Olukumi::before, .ns-0 h2#Olukumi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/aa/Flag_of_Delta_State.gif/40px-Flag_of_Delta_State.gif');}
.ns-0 h2 #Jah_Hut::before, .ns-0 h2#Jah_Hut::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/aa/Flag_of_Pahang.svg/40px-Flag_of_Pahang.svg.png');}
.ns-0 h2 #Jamaican_Creole::before, .ns-0 h2#Jamaican_Creole::before, .ns-0 h2 #Maroon_Spirit_Language::before, .ns-0 h2#Maroon_Spirit_Language::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0a/Flag_of_Jamaica.svg/40px-Flag_of_Jamaica.svg.png');}
.ns-0 h2 #Jamtish::before, .ns-0 h2#Jamtish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Jämtlands_vapen.svg/35px-Jämtlands_vapen.svg.png');}
.ns-0 h2 #Japanese::before, .ns-0 h2#Japanese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9e/Flag_of_Japan.svg/40px-Flag_of_Japan.svg.png');}
.ns-0 h2 #Jaqaru::before, .ns-0 h2#Jaqaru::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/84/Bandera_Provincia_Yauyos.jpg/40px-Bandera_Provincia_Yauyos.jpg');}
.ns-0 h2 #Jaunsari::before, .ns-0 h2#Jaunsari::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/99/Seal_of_Uttarakhand.svg/35px-Seal_of_Uttarakhand.svg.png');}
.ns-0 h2 #Javanese::before, .ns-0 h2#Javanese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Flag_of_the_Mataram_Sultanate.svg/40px-Flag_of_the_Mataram_Sultanate.svg.png');}
.ns-0 h2 #Jeju::before, .ns-0 h2#Jeju::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b9/Flag_of_Jeju_Province.svg/40px-Flag_of_Jeju_Province.svg.png');}
.ns-0 h2 #Jersey_Dutch::before, .ns-0 h2#Jersey_Dutch::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/92/Flag_of_New_Jersey.svg/40px-Flag_of_New_Jersey.svg.png');}
.ns-0 h2 #Jingpho::before, .ns-0 h2#Jingpho::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/47/Kachin_People_Flag.svg/40px-Kachin_People_Flag.svg.png');}
.ns-0 h2 #Juba_Arabic::before, .ns-0 h2#Juba_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7a/Flag_of_South_Sudan.svg/40px-Flag_of_South_Sudan.svg.png');}
.ns-0 h2 #Judeo-Italian::before, .ns-0 h2#Judeo-Italian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b1/Flag_of_Italian_Jews.svg/40px-Flag_of_Italian_Jews.svg.png');}
.ns-0 h2 #K\'iche\'::before, .ns-0 h2#K\'iche\'::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1e/El_Quiché_Flag_(GUATEMALA).png/40px-El_Quiché_Flag_(GUATEMALA).png');}
.ns-0 h2 #Kabiye::before, .ns-0 h2#Kabiye::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7a/Flag_of_Togo_(3-2).svg/40px-Flag_of_Togo_(3-2).svg.png');}
.ns-0 h2 #Kabuverdianu::before, .ns-0 h2#Kabuverdianu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/65/Flag_of_Cape_Verde_(10-17).svg/40px-Flag_of_Cape_Verde_(10-17).svg.png');}
.ns-0 h2 #Kabyle::before, .ns-0 h2#Kabyle::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/71/Flag_of_the_Republic_of_Kabylia.svg/40px-Flag_of_the_Republic_of_Kabylia.svg.png');}
.ns-0 h2 #Kaingang::before, .ns-0 h2#Kaingang::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/93/Bandeira_do_Paraná.svg/40px-Bandeira_do_Paraná.svg.png');}
.ns-0 h2 #Kaitag::before, .ns-0 h2#Kaitag::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Flag_of_Dagestan.svg/40px-Flag_of_Dagestan.svg.png');}
.ns-0 h2 #Kalasha::before, .ns-0 h2#Kalasha::before, .ns-0 h2 #Khowar::before, .ns-0 h2#Khowar::before, .ns-0 h2 #Palula::before, .ns-0 h2#Palula::before, .ns-0 h2 #Ushojo::before, .ns-0 h2#Ushojo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/54/Flag_of_Khyber_Pakhtunkhwa.svg/40px-Flag_of_Khyber_Pakhtunkhwa.svg.png');}
.ns-0 h2 #Kalmyk::before, .ns-0 h2#Kalmyk::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9f/Flag_of_Kalmykia.svg/40px-Flag_of_Kalmykia.svg.png');}
.ns-0 h2 #Kamayurá::before, .ns-0 h2#Kamayurá::before, .ns-0 h2 #Xavante::before, .ns-0 h2#Xavante::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0b/Bandeira_de_Mato_Grosso.svg/40px-Bandeira_de_Mato_Grosso.svg.png');}
.ns-0 h2 #Kankanaey::before, .ns-0 h2#Kankanaey::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/02/Flag_of_Mountain_Province.png/40px-Flag_of_Mountain_Province.png');}
.ns-0 h2 #Kannada::before, .ns-0 h2#Kannada::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0b/Flag_of_Karnataka,_India.svg/40px-Flag_of_Karnataka,_India.svg.png');}
.ns-0 h2 #Kanuri::before, .ns-0 h2#Kanuri::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/95/Flag_of_the_Kanuri_people.svg/40px-Flag_of_the_Kanuri_people.svg.png');}
.ns-0 h2 #Kapampangan::before, .ns-0 h2#Kapampangan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/46/Pampanga_Flag.png/40px-Pampanga_Flag.png');}
.ns-0 h2 #Karachay-Balkar::before, .ns-0 h2#Karachay-Balkar::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/59/Flag_of_Karachay-Cherkessia.svg/40px-Flag_of_Karachay-Cherkessia.svg.png');}
.ns-0 h2 #Karakalpak::before, .ns-0 h2#Karakalpak::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/16/Flag_of_Karakalpakstan.svg/40px-Flag_of_Karakalpakstan.svg.png');}
.ns-0 h2 #Karakhanid::before, .ns-0 h2#Karakhanid::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f4/Flag_of_the_Kara-Khanids.svg/40px-Flag_of_the_Kara-Khanids.svg.png');}
.ns-0 h2 #Karelian::before, .ns-0 h2#Karelian::before, .ns-0 h2 #Livvi::before, .ns-0 h2#Livvi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/78/Itäkarjalaisten_lippu.svg/40px-Itäkarjalaisten_lippu.svg.png');}
.ns-0 h2 #Karipúna_Creole_French::before, .ns-0 h2#Karipúna_Creole_French::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0c/Bandeira_do_Amapá.svg/40px-Bandeira_do_Amapá.svg.png');}
.ns-0 h2 #Kashmiri::before, .ns-0 h2#Kashmiri::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/32/Flag_of_Jammu_and_Kashmir_(1936-1953).svg/40px-Flag_of_Jammu_and_Kashmir_(1936-1953).svg.png');}
.ns-0 h2 #Kashubian::before, .ns-0 h2#Kashubian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fa/POL_Kaszuby_flag.svg/40px-POL_Kaszuby_flag.svg.png');}
.ns-0 h2 #Kazakh::before, .ns-0 h2#Kazakh::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d3/Flag_of_Kazakhstan.svg/40px-Flag_of_Kazakhstan.svg.png');}
.ns-0 h2 #Kedah_Malay::before, .ns-0 h2#Kedah_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/67/Flag_of_Kedah_(18th_century_-_1821).svg/40px-Flag_of_Kedah_(18th_century_-_1821).svg.png');}
.ns-0 h2 #Kelantan_Peranakan_Hokkien::before, .ns-0 h2#Kelantan_Peranakan_Hokkien::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/61/Flag_of_Kelantan.svg/40px-Flag_of_Kelantan.svg.png');}
.ns-0 h2 #Ket::before, .ns-0 h2#Ket::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2d/Flag_of_Krasnoyarsk_Krai.svg/40px-Flag_of_Krasnoyarsk_Krai.svg.png');}
.ns-0 h2 #Khakas::before, .ns-0 h2#Khakas::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Flag_of_Khakassia.svg/40px-Flag_of_Khakassia.svg.png');}
.ns-0 h2 #Khasi::before, .ns-0 h2#Khasi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7c/Seng-Khasi.png/40px-Seng-Khasi.png');}
.ns-0 h2 #Khmer::before, .ns-0 h2#Khmer::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/83/Flag_of_Cambodia.svg/40px-Flag_of_Cambodia.svg.png');}
.ns-0 h2 #Khoekhoe::before, .ns-0 h2#Khoekhoe::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/00/Flag_of_Namibia.svg/40px-Flag_of_Namibia.svg.png');}
.ns-0 h2 #Kikuyu::before, .ns-0 h2#Kikuyu::before, .ns-0 h2 #Maasai::before, .ns-0 h2#Maasai::before, .ns-0 h2 #Swahili::before, .ns-0 h2#Swahili::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/49/Flag_of_Kenya.svg/40px-Flag_of_Kenya.svg.png');}
.ns-0 h2 #Kimbundu::before, .ns-0 h2#Kimbundu::before, .ns-0 h2 #Umbundu::before, .ns-0 h2#Umbundu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9d/Flag_of_Angola.svg/40px-Flag_of_Angola.svg.png');}
.ns-0 h2 #Kiowa::before, .ns-0 h2#Kiowa::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6e/Flag_of_Oklahoma.svg/40px-Flag_of_Oklahoma.svg.png');}
.ns-0 h2 #Kituba::before, .ns-0 h2#Kituba::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/92/Flag_of_the_Republic_of_the_Congo.svg/40px-Flag_of_the_Republic_of_the_Congo.svg.png');}
.ns-0 h2 #Klingon::before, .ns-0 h2#Klingon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/02/Klingon_Empire_Flag.svg/40px-Klingon_Empire_Flag.svg.png');}
.ns-0 h2 #Kokborok::before, .ns-0 h2#Kokborok::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Flag_of_the_National_Liberation_Front_of_Tripura.svg/40px-Flag_of_the_National_Liberation_Front_of_Tripura.svg.png');}
.ns-0 h2 #Komi-Permyak::before, .ns-0 h2#Komi-Permyak::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/02/Flag_of_Permyakia.svg/40px-Flag_of_Permyakia.svg.png');}
.ns-0 h2 #Komi-Zyrian::before, .ns-0 h2#Komi-Zyrian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/54/Flag_of_Komi.svg/40px-Flag_of_Komi.svg.png');}
.ns-0 h2 #Kongo::before, .ns-0 h2#Kongo::before, .ns-0 h2 #Lingala::before, .ns-0 h2#Lingala::before, .ns-0 h2 #Luba-Kasai::before, .ns-0 h2#Luba-Kasai::before, .ns-0 h2 #Luba-Katanga::before, .ns-0 h2#Luba-Katanga::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6f/Flag_of_the_Democratic_Republic_of_the_Congo.svg/40px-Flag_of_the_Democratic_Republic_of_the_Congo.svg.png');}
.ns-0 h2 #Konzo::before, .ns-0 h2#Konzo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/09/Flag_of_Rwenzururu.svg/40px-Flag_of_Rwenzururu.svg.png');}
.ns-0 h2 #Korean::before, .ns-0 h2#Korean::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8a/Unification_flag_of_Korea.svg/40px-Unification_flag_of_Korea.svg.png');}
.ns-0 h2 #Korlai_Creole_Portuguese::before, .ns-0 h2#Korlai_Creole_Portuguese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d3/Seal_of_Maharashtra.svg/35px-Seal_of_Maharashtra.svg.png');}
.ns-0 h2 #Koryak::before, .ns-0 h2#Koryak::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Flag_of_Koryakia.svg/40px-Flag_of_Koryakia.svg.png');}
.ns-0 h2 #Kosraean::before, .ns-0 h2#Kosraean::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/23/Flag_of_Kosrae.svg/40px-Flag_of_Kosrae.svg.png');}
.ns-0 h2 #Krio::before, .ns-0 h2#Krio::before, .ns-0 h2 #Mende_\(Sierra_Leone\)::before, .ns-0 h2#Mende_\(Sierra_Leone\)::before, .ns-0 h2 #Temne::before, .ns-0 h2#Temne::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/17/Flag_of_Sierra_Leone.svg/40px-Flag_of_Sierra_Leone.svg.png');}
.ns-0 h2 #Kristang::before, .ns-0 h2#Kristang::before, .ns-0 h2 #Malaccan_Creole_Malay::before, .ns-0 h2#Malaccan_Creole_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/09/Flag_of_Malacca.svg/40px-Flag_of_Malacca.svg.png');}
.ns-0 h2 #Kumaoni::before, .ns-0 h2#Kumaoni::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a1/Flag_of_the_Kumaon_Kingdom.svg/40px-Flag_of_the_Kumaon_Kingdom.svg.png');}
.ns-0 h2 #Kumyk::before, .ns-0 h2#Kumyk::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1c/Flag_of_Kumyks.svg/40px-Flag_of_Kumyks.svg.png');}
.ns-0 h2 #Kuna::before, .ns-0 h2#Kuna::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3d/Bandera_de_la_Comarca_Guna_Yala.svg/40px-Bandera_de_la_Comarca_Guna_Yala.svg.png');}
.ns-0 h2 #Kunigami::before, .ns-0 h2#Kunigami::before, .ns-0 h2 #Miyako::before, .ns-0 h2#Miyako::before, .ns-0 h2 #Okinawan::before, .ns-0 h2#Okinawan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Flag_of_Okinawa_Prefecture.svg/40px-Flag_of_Okinawa_Prefecture.svg.png');}
.ns-0 h2 #Kupang_Malay::before, .ns-0 h2#Kupang_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c6/Flag_of_Kupang_City.png/40px-Flag_of_Kupang_City.png');}
.ns-0 h2 #Kven::before, .ns-0 h2#Kven::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/66/Flag_of_the_Kven_people.svg/40px-Flag_of_the_Kven_people.svg.png');}
.ns-0 h2 #Kwanyama::before, .ns-0 h2#Kwanyama::before, .ns-0 h2 #Ndonga::before, .ns-0 h2#Ndonga::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/46/Flag_of_Ovamboland.svg/40px-Flag_of_Ovamboland.svg.png');}
.ns-0 h2 #Kyrgyz::before, .ns-0 h2#Kyrgyz::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c7/Flag_of_Kyrgyzstan.svg/40px-Flag_of_Kyrgyzstan.svg.png');}
.ns-0 h2 #Laboya::before, .ns-0 h2#Laboya::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e9/Coat_of_Arms_of_East_Nusa_Tenggara_NEW.png/35px-Coat_of_Arms_of_East_Nusa_Tenggara_NEW.png');}
.ns-0 h2 #Ladakhi::before, .ns-0 h2#Ladakhi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5e/Flag_of_the_Ladakh_Nationalist_Movement.svg/40px-Flag_of_the_Ladakh_Nationalist_Movement.svg.png');}
.ns-0 h2 #Ladin::before, .ns-0 h2#Ladin::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/62/Flag_of_Ladinia.svg/40px-Flag_of_Ladinia.svg.png');}
.ns-0 h2 #Ladino::before, .ns-0 h2#Ladino::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3d/Spanish_Jewish_flag.png/40px-Spanish_Jewish_flag.png');}
.ns-0 h2 #Lahu::before, .ns-0 h2#Lahu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/17/Lahu_flag.svg/40px-Lahu_flag.svg.png');}
.ns-0 h2 #Lai::before, .ns-0 h2#Lai::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/af/Emblem_of_Lai_Autonomous_Council.png/40px-Emblem_of_Lai_Autonomous_Council.png');}
.ns-0 h2 #Lakota::before, .ns-0 h2#Lakota::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/77/Pine_Ridge_Flag.svg/40px-Pine_Ridge_Flag.svg.png');}
.ns-0 h2 #Lao::before, .ns-0 h2#Lao::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/56/Flag_of_Laos.svg/40px-Flag_of_Laos.svg.png');}
.ns-0 h2 #Latgalian::before, .ns-0 h2#Latgalian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7d/Official_flag_of_Latgale.svg/40px-Official_flag_of_Latgale.svg.png');}
.ns-0 h2 #Latin::before, .ns-0 h2#Latin::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a6/Flag_of_Rome.svg/40px-Flag_of_Rome.svg.png');}
.ns-0 h2 #Latvian::before, .ns-0 h2#Latvian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/84/Flag_of_Latvia.svg/40px-Flag_of_Latvia.svg.png');}
.ns-0 h2 #Leonese::before, .ns-0 h2#Leonese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/75/Bandera_del_País_Leonés.svg/40px-Bandera_del_País_Leonés.svg.png');}
.ns-0 h2 #Lepcha::before, .ns-0 h2#Lepcha::before, .ns-0 h2 #Sherpa::before, .ns-0 h2#Sherpa::before, .ns-0 h2 #Thulung::before, .ns-0 h2#Thulung::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c0/Seal_of_Sikkim.svg/40px-Seal_of_Sikkim.svg.png');}
.ns-0 h2 #Lezgi::before, .ns-0 h2#Lezgi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3f/Lezgian_flag.svg/40px-Lezgian_flag.svg.png');}
.ns-0 h2 #Ligurian::before, .ns-0 h2#Ligurian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/88/Flag_of_Liguria.svg/40px-Flag_of_Liguria.svg.png');}
.ns-0 h2 #Limbu::before, .ns-0 h2#Limbu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a2/Flag_of_Limbuwan.svg/40px-Flag_of_Limbuwan.svg.png');}
.ns-0 h2 #Limburgish::before, .ns-0 h2#Limburgish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Flag_of_Limburg_(Netherlands).svg/40px-Flag_of_Limburg_(Netherlands).svg.png');}
.ns-0 h2 #Limos_Kalinga::before, .ns-0 h2#Limos_Kalinga::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a9/Ph_seal_kalinga.png/40px-Ph_seal_kalinga.png');}
.ns-0 h2 #Lindu::before, .ns-0 h2#Lindu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/46/Coat_of_arms_of_Central_Sulawesi.svg/30px-Coat_of_arms_of_Central_Sulawesi.svg.png');}
.ns-0 h2 #Lithuanian::before, .ns-0 h2#Lithuanian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/11/Flag_of_Lithuania.svg/40px-Flag_of_Lithuania.svg.png');}
.ns-0 h2 #Livonian::before, .ns-0 h2#Livonian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/42/Flag_of_the_Livonians.svg/40px-Flag_of_the_Livonians.svg.png');}
.ns-0 h2 #Lojban::before, .ns-0 h2#Lojban::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/48/Lojban_logo.svg/40px-Lojban_logo.svg.png');}
.ns-0 h2 #Lombard::before, .ns-0 h2#Lombard::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Flag_of_Lombardy.svg/40px-Flag_of_Lombardy.svg.png');}
.ns-0 h2 #Louisiana_Creole::before, .ns-0 h2#Louisiana_Creole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e0/Flag_of_Louisiana.svg/40px-Flag_of_Louisiana.svg.png');}
.ns-0 h2 #Lovono::before, .ns-0 h2#Lovono::before, .ns-0 h2 #Äiwoo::before, .ns-0 h2#Äiwoo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b3/Temotu_province_flag.svg/40px-Temotu_province_flag.svg.png');}
.ns-0 h2 #Lower_Sorbian::before, .ns-0 h2#Lower_Sorbian::before, .ns-0 h2 #Upper_Sorbian::before, .ns-0 h2#Upper_Sorbian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c0/Flag_of_Sorbs.svg/40px-Flag_of_Sorbs.svg.png');}
.ns-0 h2 #Lozi::before, .ns-0 h2#Lozi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/62/Flag_of_Barotseland.svg/40px-Flag_of_Barotseland.svg.png');}
.ns-0 h2 #Ludian::before, .ns-0 h2#Ludian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6c/Ludic_flag.svg/40px-Ludic_flag.svg.png');}
.ns-0 h2 #Luganda::before, .ns-0 h2#Luganda::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4e/Flag_of_Uganda.svg/40px-Flag_of_Uganda.svg.png');}
.ns-0 h2 #Lushootseed::before, .ns-0 h2#Lushootseed::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/54/Flag_of_Washington.svg/40px-Flag_of_Washington.svg.png');}
.ns-0 h2 #Lutuv::before, .ns-0 h2#Lutuv::before, .ns-0 h2 #Zou::before, .ns-0 h2#Zou::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/16/Flag_of_Chin_State.svg/40px-Flag_of_Chin_State.svg.png');}
.ns-0 h2 #Luxembourgish::before, .ns-0 h2#Luxembourgish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/da/Flag_of_Luxembourg.svg/40px-Flag_of_Luxembourg.svg.png');}
.ns-0 h2 #Macanese::before, .ns-0 h2#Macanese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/63/Flag_of_Macau.svg/40px-Flag_of_Macau.svg.png');}
.ns-0 h2 #Macedonian::before, .ns-0 h2#Macedonian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/79/Flag_of_North_Macedonia.svg/40px-Flag_of_North_Macedonia.svg.png');}
.ns-0 h2 #Maguindanao::before, .ns-0 h2#Maguindanao::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/87/Flag_of_Maguindanao.svg/40px-Flag_of_Maguindanao.svg.png');}
.ns-0 h2 #Makasar::before, .ns-0 h2#Makasar::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Flag_of_the_Sultanate_of_Gowa.svg/40px-Flag_of_the_Sultanate_of_Gowa.svg.png');}
.ns-0 h2 #Malagasy::before, .ns-0 h2#Malagasy::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Flag_of_Madagascar.svg/40px-Flag_of_Madagascar.svg.png');}
.ns-0 h2 #Malay::before, .ns-0 h2#Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/01/Flag_of_Malaysia_and_Indonesia.svg/40px-Flag_of_Malaysia_and_Indonesia.svg.png');}
.ns-0 h2 #Maltese::before, .ns-0 h2#Maltese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/73/Flag_of_Malta.svg/40px-Flag_of_Malta.svg.png');}
.ns-0 h2 #Manado_Malay::before, .ns-0 h2#Manado_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ef/Lambang_Kota_Manado.png/30px-Lambang_Kota_Manado.png');}
.ns-0 h2 #Manchu::before, .ns-0 h2#Manchu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/86/Flag_of_China_(1862–1889).svg/40px-Flag_of_China_(1862–1889).svg.png');}
.ns-0 h2 #Mandinka::before, .ns-0 h2#Mandinka::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/77/Flag_of_The_Gambia.svg/40px-Flag_of_The_Gambia.svg.png');}
.ns-0 h2 #Manipuri::before, .ns-0 h2#Manipuri::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4e/Flag_of_Manipur.svg/40px-Flag_of_Manipur.svg.png');}
.ns-0 h2 #Mansaka::before, .ns-0 h2#Mansaka::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/94/Davao_De_Oro_Flag.jpg/40px-Davao_De_Oro_Flag.jpg');}
.ns-0 h2 #Manx::before, .ns-0 h2#Manx::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Flag_of_the_Isle_of_Man.svg/40px-Flag_of_the_Isle_of_Man.svg.png');}
.ns-0 h2 #Maore_Comorian::before, .ns-0 h2#Maore_Comorian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/bf/Coat_of_Arms_of_Mayotte.svg/40px-Coat_of_Arms_of_Mayotte.svg.png');}
.ns-0 h2 #Mapudungun::before, .ns-0 h2#Mapudungun::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/20/Flag_of_the_Mapuches_(1992).svg/40px-Flag_of_the_Mapuches_(1992).svg.png');}
.ns-0 h2 #Maranao::before, .ns-0 h2#Maranao::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b8/PH-LAS_Flag.png/40px-PH-LAS_Flag.png');}
.ns-0 h2 #Margi::before, .ns-0 h2#Margi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1d/Borno_State_Flag.gif/40px-Borno_State_Flag.gif');}
.ns-0 h2 #Marshallese::before, .ns-0 h2#Marshallese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2e/Flag_of_the_Marshall_Islands.svg/40px-Flag_of_the_Marshall_Islands.svg.png');}
.ns-0 h2 #Masalit::before, .ns-0 h2#Masalit::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b0/Flag_of_the_Masalit.svg/40px-Flag_of_the_Masalit.svg.png');}
.ns-0 h2 #Masbatenyo::before, .ns-0 h2#Masbatenyo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/dd/PH-MAS_Flag.png/40px-PH-MAS_Flag.png');}
.ns-0 h2 #Mauritian_Creole::before, .ns-0 h2#Mauritian_Creole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/77/Flag_of_Mauritius.svg/40px-Flag_of_Mauritius.svg.png');}
.ns-0 h2 #Maybrat::before, .ns-0 h2#Maybrat::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/43/Flag_of_Southwest_Papua.svg/40px-Flag_of_Southwest_Papua.svg.png');}
.ns-0 h2 #Mbya_Guarani::before, .ns-0 h2#Mbya_Guarani::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/24/Flag_of_Uruguay,_Brazil_and_Argentina.png/40px-Flag_of_Uruguay,_Brazil_and_Argentina.png');}
.ns-0 h2 #Media_Lengua::before, .ns-0 h2#Media_Lengua::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/dc/Bandera_Provincia_Imbabura.svg/40px-Bandera_Provincia_Imbabura.svg.png');}
.ns-0 h2 #Megleno-Romanian::before, .ns-0 h2#Megleno-Romanian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1a/Flag_of_Greek_Macedonia.svg/40px-Flag_of_Greek_Macedonia.svg.png');}
.ns-0 h2 #Mehri::before, .ns-0 h2#Mehri::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d8/Flag_of_the_Mahra_Sultanate.svg/40px-Flag_of_the_Mahra_Sultanate.svg.png');}
.ns-0 h2 #Meriam::before, .ns-0 h2#Meriam::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/37/Flag_of_Murray_Island_(Mer).svg/40px-Flag_of_Murray_Island_(Mer).svg.png');}
.ns-0 h2 #Meru::before, .ns-0 h2#Meru::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5c/Flag_of_Meru_County.png/40px-Flag_of_Meru_County.png');}
.ns-0 h2 #Meänkieli::before, .ns-0 h2#Meänkieli::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/94/Flag_of_Tornedalians_(2007).svg/40px-Flag_of_Tornedalians_(2007).svg.png');}
.ns-0 h2 #Mi\'kmaq::before, .ns-0 h2#Mi\'kmaq::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3f/Mikmaq_State_Flag.svg/40px-Mikmaq_State_Flag.svg.png');}
.ns-0 h2 #Michif::before, .ns-0 h2#Michif::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f4/Metis_Blue.svg/40px-Metis_Blue.svg.png');}
.ns-0 h2 #Michoacán_Nahuatl::before, .ns-0 h2#Michoacán_Nahuatl::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Coat_of_arms_of_Michoacan.svg/32px-Coat_of_arms_of_Michoacan.svg.png');}
.ns-0 h2 #Middle_Dutch::before, .ns-0 h2#Middle_Dutch::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3c/Flag_of_the_prince-bishopric_of_Utrecht.svg/36px-Flag_of_the_prince-bishopric_of_Utrecht.svg.png');}
.ns-0 h2 #Middle_English::before, .ns-0 h2#Middle_English::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/be/Flag_of_England.svg/40px-Flag_of_England.svg.png');}
.ns-0 h2 #Middle_French::before, .ns-0 h2#Middle_French::before, .ns-0 h2 #Old_French::before, .ns-0 h2#Old_French::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/82/Royal_flag_of_France.svg/40px-Royal_flag_of_France.svg.png');}
.ns-0 h2 #Middle_Irish::before, .ns-0 h2#Middle_Irish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8d/Flag_of_Leinster.svg/40px-Flag_of_Leinster.svg.png');}
.ns-0 h2 #Middle_Korean::before, .ns-0 h2#Middle_Korean::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/54/Royal_flag_of_Goryeo_(Bong-gi).svg/40px-Royal_flag_of_Goryeo_(Bong-gi).svg.png');}
.ns-0 h2 #Middle_Mongol::before, .ns-0 h2#Middle_Mongol::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Flag_of_the_Mongol_Empire_3.svg/40px-Flag_of_the_Mongol_Empire_3.svg.png');}
.ns-0 h2 #Middle_Norwegian::before, .ns-0 h2#Middle_Norwegian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/36/Royal_Banner_of_Norway_(14th_Century).svg/34px-Royal_Banner_of_Norway_(14th_Century).svg.png');}
.ns-0 h2 #Middle_Persian::before, .ns-0 h2#Middle_Persian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8f/Derafsh_kaviani.svg/38px-Derafsh_kaviani.svg.png');}
.ns-0 h2 #Middle_Scots::before, .ns-0 h2#Middle_Scots::before, .ns-0 h2 #Scottish_Gaelic::before, .ns-0 h2#Scottish_Gaelic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c5/Royal_Banner_of_Scotland.svg/40px-Royal_Banner_of_Scotland.svg.png');}
.ns-0 h2 #Mikasuki::before, .ns-0 h2#Mikasuki::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/34/Flag_of_the_Miccosukee_Tribe_of_Indians_of_Florida.svg/40px-Flag_of_the_Miccosukee_Tribe_of_Indians_of_Florida.svg.png');}
.ns-0 h2 #Minangkabau::before, .ns-0 h2#Minangkabau::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2a/Flag_of_Minang.svg/40px-Flag_of_Minang.svg.png');}
.ns-0 h2 #Mingrelian::before, .ns-0 h2#Mingrelian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fb/Flag_of_The_Principality_of_Mingrelia_(Portolan_1560).svg/40px-Flag_of_The_Principality_of_Mingrelia_(Portolan_1560).svg.png');}
.ns-0 h2 #Mirandese::before, .ns-0 h2#Mirandese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/41/Bandeira-de-Miranda-do-Douro.png/36px-Bandeira-de-Miranda-do-Douro.png');}
.ns-0 h2 #Miskito::before, .ns-0 h2#Miskito::before, .ns-0 h2 #Nicaraguan_Creole::before, .ns-0 h2#Nicaraguan_Creole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/19/Flag_of_Nicaragua.svg/40px-Flag_of_Nicaragua.svg.png');}
.ns-0 h2 #Mohawk::before, .ns-0 h2#Mohawk::before, .ns-0 h2 #Oneida::before, .ns-0 h2#Oneida::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/77/Flag_of_the_Iroquois_Confederacy.svg/40px-Flag_of_the_Iroquois_Confederacy.svg.png');}
.ns-0 h2 #Mokilese::before, .ns-0 h2#Mokilese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/ba/Flag_of_Mwoakilloa.svg/40px-Flag_of_Mwoakilloa.svg.png');}
.ns-0 h2 #Moksha::before, .ns-0 h2#Moksha::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e1/Flag_of_the_Moksha_people.svg/40px-Flag_of_the_Moksha_people.svg.png');}
.ns-0 h2 #Mon::before, .ns-0 h2#Mon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/03/Flag_of_Mon_State_(2018).svg/40px-Flag_of_Mon_State_(2018).svg.png');}
.ns-0 h2 #Mongolian::before, .ns-0 h2#Mongolian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4c/Flag_of_Mongolia.svg/40px-Flag_of_Mongolia.svg.png');}
.ns-0 h2 #Mongondow::before, .ns-0 h2#Mongondow::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d0/Lambang_Kabupaten_Bolaang_Mongondow.png/35px-Lambang_Kabupaten_Bolaang_Mongondow.png');}
.ns-0 h2 #Moore::before, .ns-0 h2#Moore::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/31/Flag_of_Burkina_Faso.svg/40px-Flag_of_Burkina_Faso.svg.png');}
.ns-0 h2 #Morelos_Nahuatl::before, .ns-0 h2#Morelos_Nahuatl::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/01/Coat_of_arms_of_Morelos.svg/32px-Coat_of_arms_of_Morelos.svg.png');}
.ns-0 h2 #Moroccan_Amazigh::before, .ns-0 h2#Moroccan_Amazigh::before, .ns-0 h2 #Moroccan_Arabic::before, .ns-0 h2#Moroccan_Arabic::before, .ns-0 h2 #Tashelhit::before, .ns-0 h2#Tashelhit::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Flag_of_Morocco.svg/40px-Flag_of_Morocco.svg.png');}
.ns-0 h2 #Muong::before, .ns-0 h2#Muong::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/14/Flag_of_Muong_Autonomous_Region.svg/40px-Flag_of_Muong_Autonomous_Region.svg.png');}
.ns-0 h2 #Mwali_Comorian::before, .ns-0 h2#Mwali_Comorian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c2/Flag_of_Mohéli_(official).svg/40px-Flag_of_Mohéli_(official).svg.png');}
.ns-0 h2 #Mwani::before, .ns-0 h2#Mwani::before, .ns-0 h2 #Ronga::before, .ns-0 h2#Ronga::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d0/Flag_of_Mozambique.svg/40px-Flag_of_Mozambique.svg.png');}
.ns-0 h2 #Mòcheno::before, .ns-0 h2#Mòcheno::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/51/Flag_of_Comunità_Alta_Valsugana_e_Bersntol.png/40px-Flag_of_Comunità_Alta_Valsugana_e_Bersntol.png');}
.ns-0 h2 #Māori::before, .ns-0 h2#Māori::before, .ns-0 h2 #New_Zealand_Sign_Language::before, .ns-0 h2#New_Zealand_Sign_Language::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3e/Flag_of_New_Zealand.svg/40px-Flag_of_New_Zealand.svg.png');}
.ns-0 h2 #Najdi_Arabic::before, .ns-0 h2#Najdi_Arabic::before{padding-right:5px;
content:url("//upload.wikimedia.org/wikipedia/commons/thumb/1/17/Flag_of_the_Emirate_of_Ha'il.svg/40px-Flag_of_the_Emirate_of_Ha'il.svg.png");}
.ns-0 h2 #Nauruan::before, .ns-0 h2#Nauruan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/30/Flag_of_Nauru.svg/40px-Flag_of_Nauru.svg.png');}
.ns-0 h2 #Navajo::before, .ns-0 h2#Navajo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/ce/Official_design_of_the_Navajo_Nation_flag_as_it_was_adopted_on_May_21,_1968.svg/40px-Official_design_of_the_Navajo_Nation_flag_as_it_was_adopted_on_May_21,_1968.svg.png');}
.ns-0 h2 #Ndzwani_Comorian::before, .ns-0 h2#Ndzwani_Comorian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a6/Flag_of_Anjouan_(official).svg/40px-Flag_of_Anjouan_(official).svg.png');}
.ns-0 h2 #Neapolitan::before, .ns-0 h2#Neapolitan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c5/Flag_of_Campania.svg/40px-Flag_of_Campania.svg.png');}
.ns-0 h2 #Negeri_Sembilan_Malay::before, .ns-0 h2#Negeri_Sembilan_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/db/Flag_of_Negeri_Sembilan.svg/40px-Flag_of_Negeri_Sembilan.svg.png');}
.ns-0 h2 #Nepali::before, .ns-0 h2#Nepali::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9b/Flag_of_Nepal.svg/20px-Flag_of_Nepal.svg.png');}
.ns-0 h2 #Newar::before, .ns-0 h2#Newar::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Flag_of_Kathmandu,_Nepal.svg/40px-Flag_of_Kathmandu,_Nepal.svg.png');}
.ns-0 h2 #Ngazidja_Comorian::before, .ns-0 h2#Ngazidja_Comorian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/94/Flag_of_the_Comoros.svg/40px-Flag_of_the_Comoros.svg.png');}
.ns-0 h2 #Nheengatu::before, .ns-0 h2#Nheengatu::before, .ns-0 h2 #Trió::before, .ns-0 h2#Trió::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/02/Bandeira_do_Pará.svg/40px-Bandeira_do_Pará.svg.png');}
.ns-0 h2 #Nias::before, .ns-0 h2#Nias::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c8/Coat_of_arms_of_North_Sumatra.svg/30px-Coat_of_arms_of_North_Sumatra.svg.png');}
.ns-0 h2 #Nigerian_Pidgin::before, .ns-0 h2#Nigerian_Pidgin::before, .ns-0 h2 #Nigerian_Sign_Language::before, .ns-0 h2#Nigerian_Sign_Language::before, .ns-0 h2 #Yoruba::before, .ns-0 h2#Yoruba::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/79/Flag_of_Nigeria.svg/40px-Flag_of_Nigeria.svg.png');}
.ns-0 h2 #Niuean::before, .ns-0 h2#Niuean::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/01/Flag_of_Niue.svg/40px-Flag_of_Niue.svg.png');}
.ns-0 h2 #Nivkh::before, .ns-0 h2#Nivkh::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/57/Flag_of_Sakhalin_Oblast.svg/40px-Flag_of_Sakhalin_Oblast.svg.png');}
.ns-0 h2 #Norman::before, .ns-0 h2#Norman::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/41/Flag_of_Normandie.svg/40px-Flag_of_Normandie.svg.png');}
.ns-0 h2 #North_Frisian::before, .ns-0 h2#North_Frisian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b2/Nordfriesischeflagge.svg/40px-Nordfriesischeflagge.svg.png');}
.ns-0 h2 #North_Moluccan_Malay::before, .ns-0 h2#North_Moluccan_Malay::before, .ns-0 h2 #West_Makian::before, .ns-0 h2#West_Makian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/73/Coat_of_arms_of_North_Maluku.svg/35px-Coat_of_arms_of_North_Maluku.svg.png');}
.ns-0 h2 #Northern_Altai::before, .ns-0 h2#Northern_Altai::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4b/Flag_of_Altai_Krai.svg/40px-Flag_of_Altai_Krai.svg.png');}
.ns-0 h2 #Northern_Catanduanes_Bikol::before, .ns-0 h2#Northern_Catanduanes_Bikol::before, .ns-0 h2 #Southern_Catanduanes_Bikol::before, .ns-0 h2#Southern_Catanduanes_Bikol::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/61/PH-CAT_Flag.png/40px-PH-CAT_Flag.png');}
.ns-0 h2 #Northern_Ndebele::before, .ns-0 h2#Northern_Ndebele::before, .ns-0 h2 #Shona::before, .ns-0 h2#Shona::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6a/Flag_of_Zimbabwe.svg/40px-Flag_of_Zimbabwe.svg.png');}
.ns-0 h2 #Northern_Sotho::before, .ns-0 h2#Northern_Sotho::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/52/Flag_of_Lebowa.svg/40px-Flag_of_Lebowa.svg.png');}
.ns-0 h2 #Northern_Thai::before, .ns-0 h2#Northern_Thai::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d4/Seal_of_Lanna_Kingdom.png/40px-Seal_of_Lanna_Kingdom.png');}
.ns-0 h2 #Norwegian::before, .ns-0 h2#Norwegian::before, .ns-0 h2 #Norwegian_Bokmål::before, .ns-0 h2#Norwegian_Bokmål::before, .ns-0 h2 #Norwegian_Nynorsk::before, .ns-0 h2#Norwegian_Nynorsk::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d9/Flag_of_Norway.svg/40px-Flag_of_Norway.svg.png');}
.ns-0 h2 #Novial::before, .ns-0 h2#Novial::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ed/Flag_of_Novial.svg/40px-Flag_of_Novial.svg.png');}
.ns-0 h2 #Nubi::before, .ns-0 h2#Nubi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c2/Kenia-Uganda-Flagge.png/40px-Kenia-Uganda-Flagge.png');}
.ns-0 h2 #Nuosu::before, .ns-0 h2#Nuosu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9d/Proposed_Yi_flag.svg/40px-Proposed_Yi_flag.svg.png');}
.ns-0 h2 #Nupe::before, .ns-0 h2#Nupe::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a7/Niger_state_flag.png/40px-Niger_state_flag.png');}
.ns-0 h2 #Nyankole::before, .ns-0 h2#Nyankole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3c/Flag_of_Ankole.svg/40px-Flag_of_Ankole.svg.png');}
.ns-0 h2 #Nyoro::before, .ns-0 h2#Nyoro::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2a/Bunyoro-Kitara_Kingdom_Flag.png/40px-Bunyoro-Kitara_Kingdom_Flag.png');}
.ns-0 h2 #Nùng::before, .ns-0 h2#Nùng::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/75/Flag_of_the_Nùng_Autonomous_Territory.svg/40px-Flag_of_the_Nùng_Autonomous_Territory.svg.png');}
.ns-0 h2 #O\'odham::before, .ns-0 h2#O\'odham::before{padding-right:5px;
content:url("//upload.wikimedia.org/wikipedia/commons/thumb/0/05/O'odham_unofficial_flag.svg/40px-O'odham_unofficial_flag.svg.png");}
.ns-0 h2 #Occitan::before, .ns-0 h2#Occitan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6d/Flag_of_Occitania_(with_star).svg/40px-Flag_of_Occitania_(with_star).svg.png');}
.ns-0 h2 #Odia::before, .ns-0 h2#Odia::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5d/Flag_of_Mayurbhanj.png/40px-Flag_of_Mayurbhanj.png');}
.ns-0 h2 #Ojibwe::before, .ns-0 h2#Ojibwe::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/94/Flag_of_the_Peguis_First_Nation.svg/40px-Flag_of_the_Peguis_First_Nation.svg.png');}
.ns-0 h2 #Old_Armenian::before, .ns-0 h2#Old_Armenian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b3/Standard_of_the_Artaxiad_dynasty.svg/40px-Standard_of_the_Artaxiad_dynasty.svg.png');}
.ns-0 h2 #Old_Catalan::before, .ns-0 h2#Old_Catalan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/58/Bandera_del_Principat_de_Catalunya_(1200_dC).svg/40px-Bandera_del_Principat_de_Catalunya_(1200_dC).svg.png');}
.ns-0 h2 #Old_Czech::before, .ns-0 h2#Old_Czech::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/41/Banner_of_Přemyslid_family.svg/40px-Banner_of_Přemyslid_family.svg.png');}
.ns-0 h2 #Old_English::before, .ns-0 h2#Old_English::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e0/Flag_of_Wessex.svg/40px-Flag_of_Wessex.svg.png');}
.ns-0 h2 #Old_Galician-Portuguese::before, .ns-0 h2#Old_Galician-Portuguese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/25/PortugueseFlag1143.svg/40px-PortugueseFlag1143.svg.png');}
.ns-0 h2 #Old_Georgian::before, .ns-0 h2#Old_Georgian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Kartli_-_drosha_jvari.svg/40px-Kartli_-_drosha_jvari.svg.png');}
.ns-0 h2 #Old_High_German::before, .ns-0 h2#Old_High_German::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Flag_of_East_Francia_(843–962).png/40px-Flag_of_East_Francia_(843–962).png');}
.ns-0 h2 #Old_Irish::before, .ns-0 h2#Old_Irish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/42/Triskele-Symbol1.svg/40px-Triskele-Symbol1.svg.png');}
.ns-0 h2 #Old_Javanese::before, .ns-0 h2#Old_Javanese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/10/Naval_flag_of_Majapahit_Kingdom.svg/40px-Naval_flag_of_Majapahit_Kingdom.svg.png');}
.ns-0 h2 #Old_Korean::before, .ns-0 h2#Old_Korean::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/80/Military_Banner_of_Silla.svg/40px-Military_Banner_of_Silla.svg.png');}
.ns-0 h2 #Old_Leonese::before, .ns-0 h2#Old_Leonese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/73/Royal_Banner_of_Leon_(Variant).svg/40px-Royal_Banner_of_Leon_(Variant).svg.png');}
.ns-0 h2 #Old_Navarro-Aragonese::before, .ns-0 h2#Old_Navarro-Aragonese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/45/Navarro-Aragonese_flag.png/40px-Navarro-Aragonese_flag.png');}
.ns-0 h2 #Old_Norse::before, .ns-0 h2#Old_Norse::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/10/Raven_Banner.svg/40px-Raven_Banner.svg.png');}
.ns-0 h2 #Old_Novgorodian::before, .ns-0 h2#Old_Novgorodian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/73/Banner_of_the_Novgorod_Republic_(c._1385).svg/40px-Banner_of_the_Novgorod_Republic_(c._1385).svg.png');}
.ns-0 h2 #Old_Nubian::before, .ns-0 h2#Old_Nubian::before{padding-right:5px;
content:url("//upload.wikimedia.org/wikipedia/commons/thumb/4/47/The_flag_of_the_'Kingdom_of_Dongola'_(Makuria)_in_the_%22Book_of_all_kingdoms%22_(C._1350).svg/40px-The_flag_of_the_'Kingdom_of_Dongola'_(Makuria)_in_the_%22Book_of_all_kingdoms%22_(C._1350).svg.png");}
.ns-0 h2 #Old_Occitan::before, .ns-0 h2#Old_Occitan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6f/Flag_of_Provence.svg/40px-Flag_of_Provence.svg.png');}
.ns-0 h2 #Old_Polish::before, .ns-0 h2#Old_Polish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/12/Flag_of_the_Kingdom_of_Poland.svg/40px-Flag_of_the_Kingdom_of_Poland.svg.png');}
.ns-0 h2 #Old_Ruthenian::before, .ns-0 h2#Old_Ruthenian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e1/Royal_banner_of_the_Grand_Duchy_of_Lithuania.svg/40px-Royal_banner_of_the_Grand_Duchy_of_Lithuania.svg.png');}
.ns-0 h2 #Old_Saxon::before, .ns-0 h2#Old_Saxon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3b/Flag_of_Old_Saxony.svg/40px-Flag_of_Old_Saxony.svg.png');}
.ns-0 h2 #Old_Spanish::before, .ns-0 h2#Old_Spanish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/71/Flag_of_Castile.svg/40px-Flag_of_Castile.svg.png');}
.ns-0 h2 #Old_Swedish::before, .ns-0 h2#Old_Swedish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/25/Royal_Banner_of_Sweden_(14th_Century).svg/30px-Royal_Banner_of_Sweden_(14th_Century).svg.png');}
.ns-0 h2 #Old_Welsh::before, .ns-0 h2#Old_Welsh::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/66/Flag_of_Gwynedd_(3-2).svg/40px-Flag_of_Gwynedd_(3-2).svg.png');}
.ns-0 h2 #Orizaba_Nahuatl::before, .ns-0 h2#Orizaba_Nahuatl::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/57/Escudo_de_Armas_de_la_Ciudad_de_Orizaba.svg/35px-Escudo_de_Armas_de_la_Ciudad_de_Orizaba.svg.png');}
.ns-0 h2 #Oromo::before, .ns-0 h2#Oromo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3c/Flag_of_the_Oromia_Region.svg/40px-Flag_of_the_Oromia_Region.svg.png');}
.ns-0 h2 #Osage::before, .ns-0 h2#Osage::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3e/Flag_of_the_Osage_Nation.svg/40px-Flag_of_the_Osage_Nation.svg.png');}
.ns-0 h2 #Ossetian::before, .ns-0 h2#Ossetian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1c/Flag_of_North_Ossetia.svg/40px-Flag_of_North_Ossetia.svg.png');}
.ns-0 h2 #Ottoman_Turkish::before, .ns-0 h2#Ottoman_Turkish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/06/Flag_of_the_Ottoman_Empire_(Thicker_Crescent).svg/40px-Flag_of_the_Ottoman_Empire_(Thicker_Crescent).svg.png');}
.ns-0 h2 #Pa\'o_Karen::before, .ns-0 h2#Pa\'o_Karen::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/28/Flag_of_the_Pa-O_National_Organisation.svg/40px-Flag_of_the_Pa-O_National_Organisation.svg.png');}
.ns-0 h2 #Pahari-Potwari::before, .ns-0 h2#Pahari-Potwari::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Flag_of_Azad_Kashmir.svg/40px-Flag_of_Azad_Kashmir.svg.png');}
.ns-0 h2 #Palauan::before, .ns-0 h2#Palauan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/48/Flag_of_Palau.svg/40px-Flag_of_Palau.svg.png');}
.ns-0 h2 #Palenquero::before, .ns-0 h2#Palenquero::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/21/Flag_of_Colombia.svg/40px-Flag_of_Colombia.svg.png');}
.ns-0 h2 #Pali::before, .ns-0 h2#Pali::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f1/Buddhism_dham_jak.svg/35px-Buddhism_dham_jak.svg.png');}
.ns-0 h2 #Panamanian_Sign_Language::before, .ns-0 h2#Panamanian_Sign_Language::before, .ns-0 h2 #San_Miguel_Creole_French::before, .ns-0 h2#San_Miguel_Creole_French::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/ab/Flag_of_Panama.svg/40px-Flag_of_Panama.svg.png');}
.ns-0 h2 #Pangasinan::before, .ns-0 h2#Pangasinan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a1/Flag_of_Pangasinan.svg/40px-Flag_of_Pangasinan.svg.png');}
.ns-0 h2 #Papiamentu::before, .ns-0 h2#Papiamentu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/ae/Flag_of_the_Netherlands_Antilles_(1986–2010).svg/40px-Flag_of_the_Netherlands_Antilles_(1986–2010).svg.png');}
.ns-0 h2 #Papuan_Malay::before, .ns-0 h2#Papuan_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Kerajaan_Kaimana_Papua.gif/40px-Kerajaan_Kaimana_Papua.gif');}
.ns-0 h2 #Paraguayan_Guarani::before, .ns-0 h2#Paraguayan_Guarani::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/27/Flag_of_Paraguay.svg/40px-Flag_of_Paraguay.svg.png');}
.ns-0 h2 #Parauk::before, .ns-0 h2#Parauk::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/48/Flag_of_Wa_State.svg/40px-Flag_of_Wa_State.svg.png');}
.ns-0 h2 #Pashto::before, .ns-0 h2#Pashto::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d4/Pashto_Banner_flag_(Neutral).png/40px-Pashto_Banner_flag_(Neutral).png');}
.ns-0 h2 #Pennsylvania_German::before, .ns-0 h2#Pennsylvania_German::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f7/Flag_of_Pennsylvania.svg/40px-Flag_of_Pennsylvania.svg.png');}
.ns-0 h2 #Penrhyn::before, .ns-0 h2#Penrhyn::before, .ns-0 h2 #Pukapukan::before, .ns-0 h2#Pukapukan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/35/Flag_of_the_Cook_Islands.svg/40px-Flag_of_the_Cook_Islands.svg.png');}
.ns-0 h2 #Persian::before, .ns-0 h2#Persian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/ca/Flag_of_Iran.svg/40px-Flag_of_Iran.svg.png');}
.ns-0 h2 #Phuthi::before, .ns-0 h2#Phuthi::before, .ns-0 h2 #Sotho::before, .ns-0 h2#Sotho::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4a/Flag_of_Lesotho.svg/40px-Flag_of_Lesotho.svg.png');}
.ns-0 h2 #Picard::before, .ns-0 h2#Picard::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/50/Picardy_flag.png/40px-Picardy_flag.png');}
.ns-0 h2 #Pichinglis::before, .ns-0 h2#Pichinglis::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/31/Flag_of_Equatorial_Guinea.svg/40px-Flag_of_Equatorial_Guinea.svg.png');}
.ns-0 h2 #Piedmontese::before, .ns-0 h2#Piedmontese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b9/Flag_of_Piedmont.svg/40px-Flag_of_Piedmont.svg.png');}
.ns-0 h2 #Pijin::before, .ns-0 h2#Pijin::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/74/Flag_of_the_Solomon_Islands.svg/40px-Flag_of_the_Solomon_Islands.svg.png');}
.ns-0 h2 #Pitcairn-Norfolk::before, .ns-0 h2#Pitcairn-Norfolk::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/48/Flag_of_Norfolk_Island.svg/40px-Flag_of_Norfolk_Island.svg.png');}
.ns-0 h2 #Pitjantjatjara::before, .ns-0 h2#Pitjantjatjara::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fd/Flag_of_South_Australia.svg/40px-Flag_of_South_Australia.svg.png');}
.ns-0 h2 #Pnar::before, .ns-0 h2#Pnar::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/87/Emblem_of_Jaintia_Hills_Autonomous_District_Council.png/40px-Emblem_of_Jaintia_Hills_Autonomous_District_Council.png');}
.ns-0 h2 #Pohnpeian::before, .ns-0 h2#Pohnpeian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d6/Flag_of_Pohnpei_(1977-1992).svg/40px-Flag_of_Pohnpei_(1977-1992).svg.png');}
.ns-0 h2 #Polabian::before, .ns-0 h2#Polabian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/36/Banner_of_Wends.png/40px-Banner_of_Wends.png');}
.ns-0 h2 #Polish::before, .ns-0 h2#Polish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/12/Flag_of_Poland.svg/40px-Flag_of_Poland.svg.png');}
.ns-0 h2 #Pontic_Greek::before, .ns-0 h2#Pontic_Greek::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/bf/Flag_of_Pontus.svg/40px-Flag_of_Pontus.svg.png');}
.ns-0 h2 #Portuguese::before, .ns-0 h2#Portuguese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/31/Flag_of_Portuguese_language_(PT-BR).svg/40px-Flag_of_Portuguese_language_(PT-BR).svg.png');}
.ns-0 h2 #Principense::before, .ns-0 h2#Principense::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/21/Flag_of_Príncipe_Autonomous_Region.svg/40px-Flag_of_Príncipe_Autonomous_Region.svg.png');}
.ns-0 h2 #Puerto_Rican_Sign_Language::before, .ns-0 h2#Puerto_Rican_Sign_Language::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/28/Flag_of_Puerto_Rico.svg/40px-Flag_of_Puerto_Rico.svg.png');}
.ns-0 h2 #Punjabi::before, .ns-0 h2#Punjabi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/36/Flag_of_Punjab_(Paired).png/40px-Flag_of_Punjab_(Paired).png');}
.ns-0 h2 #Quechua::before, .ns-0 h2#Quechua::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8f/Quechuas_flag.svg/40px-Quechuas_flag.svg.png');}
.ns-0 h2 #Rakhine::before, .ns-0 h2#Rakhine::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fd/Flag_of_Rakhine.svg/40px-Flag_of_Rakhine.svg.png');}
.ns-0 h2 #Rapa_Nui::before, .ns-0 h2#Rapa_Nui::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d8/Flag_of_Rapa_Nui,_Chile.svg/40px-Flag_of_Rapa_Nui,_Chile.svg.png');}
.ns-0 h2 #Ratagnon::before, .ns-0 h2#Ratagnon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Province_of_Occidental_Mindoro_seal.svg/40px-Province_of_Occidental_Mindoro_seal.svg.png');}
.ns-0 h2 #Rennellese::before, .ns-0 h2#Rennellese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/83/Flag_of_Rennell_and_Bellona_Province.svg/40px-Flag_of_Rennell_and_Bellona_Province.svg.png');}
.ns-0 h2 #Rohingya::before, .ns-0 h2#Rohingya::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/83/Rohingya_flag.svg/40px-Rohingya_flag.svg.png');}
.ns-0 h2 #Romagnol::before, .ns-0 h2#Romagnol::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b1/Flag_of_San_Marino.svg/40px-Flag_of_San_Marino.svg.png');}
.ns-0 h2 #Romanian::before, .ns-0 h2#Romanian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/73/Flag_of_Romania.svg/40px-Flag_of_Romania.svg.png');}
.ns-0 h2 #Romansh::before, .ns-0 h2#Romansh::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/0f/Flag_of_Canton_of_Graubünden.svg/40px-Flag_of_Canton_of_Graubünden.svg.png');}
.ns-0 h2 #Rotuman::before, .ns-0 h2#Rotuman::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/bb/Flag_of_Rotuma_(1987-1988).svg/40px-Flag_of_Rotuma_(1987-1988).svg.png');}
.ns-0 h2 #Russian::before, .ns-0 h2#Russian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f3/Flag_of_Russia.svg/40px-Flag_of_Russia.svg.png');}
.ns-0 h2 #Rutul::before, .ns-0 h2#Rutul::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/da/Flag_of_the_Rutul_People.svg/40px-Flag_of_the_Rutul_People.svg.png');}
.ns-0 h2 #Rwanda-Rundi::before, .ns-0 h2#Rwanda-Rundi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/81/Flag_of_Rwanda_and_Burundi.svg/40px-Flag_of_Rwanda_and_Burundi.svg.png');}
.ns-0 h2 #Réunion_Creole_French::before, .ns-0 h2#Réunion_Creole_French::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8e/Proposed_flag_of_Réunion_(VAR).svg/40px-Proposed_flag_of_Réunion_(VAR).svg.png');}
.ns-0 h2 #S\'gaw_Karen::before, .ns-0 h2#S\'gaw_Karen::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Flag_of_the_Karen_National_Union.svg/40px-Flag_of_the_Karen_National_Union.svg.png');}
.ns-0 h2 #Saho::before, .ns-0 h2#Saho::before{padding-right:5px;
content:url("//upload.wikimedia.org/wikipedia/commons/thumb/c/c4/Flag_of_the_Saho_People's_Democratic_Movement.svg/40px-Flag_of_the_Saho_People's_Democratic_Movement.svg.png");}
.ns-0 h2 #Sambali::before, .ns-0 h2#Sambali::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/81/Seal_of_Zambales.svg/40px-Seal_of_Zambales.svg.png');}
.ns-0 h2 #Samoan::before, .ns-0 h2#Samoan::before, .ns-0 h2 #Samoan_Plantation_Pidgin::before, .ns-0 h2#Samoan_Plantation_Pidgin::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/31/Flag_of_Samoa.svg/40px-Flag_of_Samoa.svg.png');}
.ns-0 h2 #Samogitian::before, .ns-0 h2#Samogitian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c4/Flag_of_Žemaitija.svg/40px-Flag_of_Žemaitija.svg.png');}
.ns-0 h2 #Sanaviron::before, .ns-0 h2#Sanaviron::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/aa/Bandera_de_la_Ciudad_de_Córdoba.svg/40px-Bandera_de_la_Ciudad_de_Córdoba.svg.png');}
.ns-0 h2 #Sango::before, .ns-0 h2#Sango::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6f/Flag_of_the_Central_African_Republic.svg/40px-Flag_of_the_Central_African_Republic.svg.png');}
.ns-0 h2 #Sanskrit::before, .ns-0 h2#Sanskrit::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8e/Red_om_symbol.png/35px-Red_om_symbol.png');}
.ns-0 h2 #Sardinian::before, .ns-0 h2#Sardinian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4e/Flag_of_Sardinia,_Italy.svg/40px-Flag_of_Sardinia,_Italy.svg.png');}
.ns-0 h2 #Sassarese::before, .ns-0 h2#Sassarese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fe/Flag_of_Sassari.svg/40px-Flag_of_Sassari.svg.png');}
.ns-0 h2 #Saterland_Frisian::before, .ns-0 h2#Saterland_Frisian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2f/DEU_Saterland_Flag.svg/40px-DEU_Saterland_Flag.svg.png');}
.ns-0 h2 #Scanian::before, .ns-0 h2#Scanian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1e/Banner_of_Arms_of_Scania.svg/35px-Banner_of_Arms_of_Scania.svg.png');}
.ns-0 h2 #Scots::before, .ns-0 h2#Scots::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/10/Flag_of_Scotland.svg/40px-Flag_of_Scotland.svg.png');}
.ns-0 h2 #Sedang::before, .ns-0 h2#Sedang::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/19/Flag_of_Sedang.svg/36px-Flag_of_Sedang.svg.png');}
.ns-0 h2 #Selk\'nam::before, .ns-0 h2#Selk\'nam::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/94/Bandera_de_la_Provincia_de_Tierra_del_Fuego.svg/40px-Bandera_de_la_Provincia_de_Tierra_del_Fuego.svg.png');}
.ns-0 h2 #Serbo-Croatian::before, .ns-0 h2#Serbo-Croatian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fb/HR-BA-ME-RS_flag.svg/40px-HR-BA-ME-RS_flag.svg.png');}
.ns-0 h2 #Serer::before, .ns-0 h2#Serer::before, .ns-0 h2 #Wolof::before, .ns-0 h2#Wolof::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fd/Flag_of_Senegal.svg/40px-Flag_of_Senegal.svg.png');}
.ns-0 h2 #Seri::before, .ns-0 h2#Seri::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4c/Bandera_de_la_Nacion_Comcaac.svg/40px-Bandera_de_la_Nacion_Comcaac.svg.png');}
.ns-0 h2 #Seychellois_Creole::before, .ns-0 h2#Seychellois_Creole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fc/Flag_of_Seychelles.svg/40px-Flag_of_Seychelles.svg.png');}
.ns-0 h2 #Shan::before, .ns-0 h2#Shan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/68/Flag_of_Shan_State.svg/40px-Flag_of_Shan_State.svg.png');}
.ns-0 h2 #Shihhi_Arabic::before, .ns-0 h2#Shihhi_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5d/Flag_of_Sharjah_and_Ras_Al_Khaimah.svg/40px-Flag_of_Sharjah_and_Ras_Al_Khaimah.svg.png');}
.ns-0 h2 #Sicilian::before, .ns-0 h2#Sicilian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7a/Flag_of_Sicily.svg/40px-Flag_of_Sicily.svg.png');}
.ns-0 h2 #Sidamo::before, .ns-0 h2#Sidamo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/59/Flag_of_Sidama.svg/40px-Flag_of_Sidama.svg.png');}
.ns-0 h2 #Sikkimese::before, .ns-0 h2#Sikkimese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6b/Flag_of_Sikkim_(1967-1975).svg/40px-Flag_of_Sikkim_(1967-1975).svg.png');}
.ns-0 h2 #Silesian::before, .ns-0 h2#Silesian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c1/Flag_of_Upper_Silesia.svg/40px-Flag_of_Upper_Silesia.svg.png');}
.ns-0 h2 #Sindhi::before, .ns-0 h2#Sindhi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Flag_of_Sindh.svg/40px-Flag_of_Sindh.svg.png');}
.ns-0 h2 #Sinhalese::before, .ns-0 h2#Sinhalese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/11/Flag_of_Sri_Lanka.svg/40px-Flag_of_Sri_Lanka.svg.png');}
.ns-0 h2 #Slavomolisano::before, .ns-0 h2#Slavomolisano::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/84/Flag_of_Molise.svg/40px-Flag_of_Molise.svg.png');}
.ns-0 h2 #Slovak::before, .ns-0 h2#Slovak::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e6/Flag_of_Slovakia.svg/40px-Flag_of_Slovakia.svg.png');}
.ns-0 h2 #Slovene::before, .ns-0 h2#Slovene::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f0/Flag_of_Slovenia.svg/40px-Flag_of_Slovenia.svg.png');}
.ns-0 h2 #Slovincian::before, .ns-0 h2#Slovincian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/08/Wappen_Pommern.svg/35px-Wappen_Pommern.svg.png');}
.ns-0 h2 #Soga::before, .ns-0 h2#Soga::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d8/Flag_of_Busoga_(royal_standard).png/40px-Flag_of_Busoga_(royal_standard).png');}
.ns-0 h2 #Solombala_English::before, .ns-0 h2#Solombala_English::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/20/Coat_of_Arms_of_Arkhangelsk.svg/32px-Coat_of_Arms_of_Arkhangelsk.svg.png');}
.ns-0 h2 #Somali::before, .ns-0 h2#Somali::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a0/Flag_of_Somalia.svg/40px-Flag_of_Somalia.svg.png');}
.ns-0 h2 #South_Levantine_Arabic::before, .ns-0 h2#South_Levantine_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/00/Flag_of_Palestine.svg/40px-Flag_of_Palestine.svg.png');}
.ns-0 h2 #Southern_Altai::before, .ns-0 h2#Southern_Altai::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/ff/Flag_of_Altai_Republic.svg/40px-Flag_of_Altai_Republic.svg.png');}
.ns-0 h2 #Southern_Ndebele::before, .ns-0 h2#Southern_Ndebele::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/93/Flag_of_Mpumalanga_Province.svg/40px-Flag_of_Mpumalanga_Province.svg.png');}
.ns-0 h2 #Spanish::before, .ns-0 h2#Spanish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/49/Flag_of_Spanish_language_(ES-MX).svg/40px-Flag_of_Spanish_language_(ES-MX).svg.png');}
.ns-0 h2 #Sri_Lankan_Creole_Malay::before, .ns-0 h2#Sri_Lankan_Creole_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b3/Flag_of_the_Southern_Province_(Sri_Lanka).PNG/40px-Flag_of_the_Southern_Province_(Sri_Lanka).PNG');}
.ns-0 h2 #Sudovian::before, .ns-0 h2#Sudovian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/54/Flag_of_Suvalkija.svg/40px-Flag_of_Suvalkija.svg.png');}
.ns-0 h2 #Sumerian::before, .ns-0 h2#Sumerian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3d/Cuneiform_sumer_dingir.svg/28px-Cuneiform_sumer_dingir.svg.png');}
.ns-0 h2 #Sundanese::before, .ns-0 h2#Sundanese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/41/Flag_of_West_Java_(vectorised).svg/40px-Flag_of_West_Java_(vectorised).svg.png');}
.ns-0 h2 #Surigaonon::before, .ns-0 h2#Surigaonon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e8/Flag_of_Surigao_del_Norte.svg/40px-Flag_of_Surigao_del_Norte.svg.png');}
.ns-0 h2 #Swabian::before, .ns-0 h2#Swabian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/14/Flagge_Königreich_Württemberg.svg/40px-Flagge_Königreich_Württemberg.svg.png');}
.ns-0 h2 #Swazi::before, .ns-0 h2#Swazi::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fb/Flag_of_Eswatini.svg/40px-Flag_of_Eswatini.svg.png');}
.ns-0 h2 #Swedish::before, .ns-0 h2#Swedish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4c/Flag_of_Sweden.svg/40px-Flag_of_Sweden.svg.png');}
.ns-0 h2 #Tabasaran::before, .ns-0 h2#Tabasaran::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d0/Ethnic_flag_of_Tabasarans.svg/40px-Ethnic_flag_of_Tabasarans.svg.png');}
.ns-0 h2 #Tabasco_Nahuatl::before, .ns-0 h2#Tabasco_Nahuatl::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b0/Coat_of_arms_of_Tabasco_(México).svg/32px-Coat_of_arms_of_Tabasco_(México).svg.png');}
.ns-0 h2 #Tachawit::before, .ns-0 h2#Tachawit::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6f/Flag_of_Chaouia.svg/40px-Flag_of_Chaouia.svg.png');}
.ns-0 h2 #Tagalog::before, .ns-0 h2#Tagalog::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/99/Flag_of_the_Philippines.svg/40px-Flag_of_the_Philippines.svg.png');}
.ns-0 h2 #Tahitian::before, .ns-0 h2#Tahitian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/db/Flag_of_French_Polynesia.svg/40px-Flag_of_French_Polynesia.svg.png');}
.ns-0 h2 #Tai_Dam::before, .ns-0 h2#Tai_Dam::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/93/Flag_of_the_Tai_Dam_People.svg/40px-Flag_of_the_Tai_Dam_People.svg.png');}
.ns-0 h2 #Tai_Dón::before, .ns-0 h2#Tai_Dón::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/86/Flag_of_Tay_Khao.png/40px-Flag_of_Tay_Khao.png');}
.ns-0 h2 #Taimyr_Pidgin_Russian::before, .ns-0 h2#Taimyr_Pidgin_Russian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Flag_of_the_Siberian_Federal_District.png/40px-Flag_of_the_Siberian_Federal_District.png');}
.ns-0 h2 #Tajik::before, .ns-0 h2#Tajik::before, .ns-0 h2 #Tajiki_Arabic::before, .ns-0 h2#Tajiki_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d0/Flag_of_Tajikistan.svg/40px-Flag_of_Tajikistan.svg.png');}
.ns-0 h2 #Talysh::before, .ns-0 h2#Talysh::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/de/Flag_of_the_Talysh_National_Movement.svg/40px-Flag_of_the_Talysh_National_Movement.svg.png');}
.ns-0 h2 #Tarifit::before, .ns-0 h2#Tarifit::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/61/Flag_of_the_Republic_of_the_Rif.svg/40px-Flag_of_the_Republic_of_the_Rif.svg.png');}
.ns-0 h2 #Tarjumo::before, .ns-0 h2#Tarjumo::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/49/Kanem_flag_from_dulcerta_1339-pt.svg/40px-Kanem_flag_from_dulcerta_1339-pt.svg.png');}
.ns-0 h2 #Tatar::before, .ns-0 h2#Tatar::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/28/Flag_of_Tatarstan.svg/40px-Flag_of_Tatarstan.svg.png');}
.ns-0 h2 #Tausug::before, .ns-0 h2#Tausug::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/da/Late_19th_Century_Flag_of_Sulu.svg/40px-Late_19th_Century_Flag_of_Sulu.svg.png');}
.ns-0 h2 #Tayo::before, .ns-0 h2#Tayo::before, .ns-0 h2 #Xârâcùù::before, .ns-0 h2#Xârâcùù::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/66/Flag_of_FLNKS.svg/40px-Flag_of_FLNKS.svg.png');}
.ns-0 h2 #Tboli::before, .ns-0 h2#Tboli::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/7f/South_Cotabato_Flag.png/40px-South_Cotabato_Flag.png');}
.ns-0 h2 #Temascaltepec_Nahuatl::before, .ns-0 h2#Temascaltepec_Nahuatl::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/4b/TEMASCALTEPEC.svg/35px-TEMASCALTEPEC.svg.png');}
.ns-0 h2 #Terengganu_Malay::before, .ns-0 h2#Terengganu_Malay::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6b/Flag_of_Terengganu.svg/40px-Flag_of_Terengganu.svg.png');}
.ns-0 h2 #Ternate::before, .ns-0 h2#Ternate::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/61/Bendera_Ternate_-_Almulk_Buldan_Ternate.svg/40px-Bendera_Ternate_-_Almulk_Buldan_Ternate.svg.png');}
.ns-0 h2 #Tetum::before, .ns-0 h2#Tetum::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/26/Flag_of_East_Timor.svg/40px-Flag_of_East_Timor.svg.png');}
.ns-0 h2 #Thai::before, .ns-0 h2#Thai::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/a/a9/Flag_of_Thailand.svg/40px-Flag_of_Thailand.svg.png');}
.ns-0 h2 #Tibetan::before, .ns-0 h2#Tibetan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3c/Flag_of_Tibet.svg/40px-Flag_of_Tibet.svg.png');}
.ns-0 h2 #Ticuna::before, .ns-0 h2#Ticuna::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6b/Bandeira_do_Amazonas.svg/40px-Bandeira_do_Amazonas.svg.png');}
.ns-0 h2 #Tlingit::before, .ns-0 h2#Tlingit::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/94/Flag_of_the_Organized_Village_of_Kake.svg/40px-Flag_of_the_Organized_Village_of_Kake.svg.png');}
.ns-0 h2 #Toba_Batak::before, .ns-0 h2#Toba_Batak::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/5e/Flag_of_Batak_WRB.svg/40px-Flag_of_Batak_WRB.svg.png');}
.ns-0 h2 #Tobagonian_Creole_English::before, .ns-0 h2#Tobagonian_Creole_English::before, .ns-0 h2 #Trinidadian_Creole_English::before, .ns-0 h2#Trinidadian_Creole_English::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/64/Flag_of_Trinidad_and_Tobago.svg/40px-Flag_of_Trinidad_and_Tobago.svg.png');}
.ns-0 h2 #Tok_Pisin::before, .ns-0 h2#Tok_Pisin::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e3/Flag_of_Papua_New_Guinea.svg/40px-Flag_of_Papua_New_Guinea.svg.png');}
.ns-0 h2 #Tokelauan::before, .ns-0 h2#Tokelauan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8e/Flag_of_Tokelau.svg/40px-Flag_of_Tokelau.svg.png');}
.ns-0 h2 #Tongan::before, .ns-0 h2#Tongan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/9a/Flag_of_Tonga.svg/40px-Flag_of_Tonga.svg.png');}
.ns-0 h2 #Tooro::before, .ns-0 h2#Tooro::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c7/Flag_of_Toro.gif/40px-Flag_of_Toro.gif');}
.ns-0 h2 #Transylvanian_Saxon::before, .ns-0 h2#Transylvanian_Saxon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8c/Flag_of_Transylvania_Saxons.svg/40px-Flag_of_Transylvania_Saxons.svg.png');}
.ns-0 h2 #Tsonga::before, .ns-0 h2#Tsonga::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/70/Flag_of_Tsonga_language.svg/40px-Flag_of_Tsonga_language.svg.png');}
.ns-0 h2 #Tsou::before, .ns-0 h2#Tsou::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3e/Flag_of_Chiayi_County.svg/40px-Flag_of_Chiayi_County.svg.png');}
.ns-0 h2 #Tswana::before, .ns-0 h2#Tswana::before, .ns-0 h2 #ǃXóõ::before, .ns-0 h2#ǃXóõ::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Flag_of_Botswana.svg/40px-Flag_of_Botswana.svg.png');}
.ns-0 h2 #Tuareg::before, .ns-0 h2#Tuareg::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/54/Touareg_People_Flag.svg/40px-Touareg_People_Flag.svg.png');}
.ns-0 h2 #Tunisian_Arabic::before, .ns-0 h2#Tunisian_Arabic::before, .ns-0 h2 #Tunisian_Berber::before, .ns-0 h2#Tunisian_Berber::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/ce/Flag_of_Tunisia.svg/40px-Flag_of_Tunisia.svg.png');}
.ns-0 h2 #Turkana::before, .ns-0 h2#Turkana::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e8/Flag_of_Turkana_County.gif/40px-Flag_of_Turkana_County.gif');}
.ns-0 h2 #Turkish::before, .ns-0 h2#Turkish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b4/Flag_of_Turkey.svg/40px-Flag_of_Turkey.svg.png');}
.ns-0 h2 #Turkmen::before, .ns-0 h2#Turkmen::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1b/Flag_of_Turkmenistan.svg/40px-Flag_of_Turkmenistan.svg.png');}
.ns-0 h2 #Tuvaluan::before, .ns-0 h2#Tuvaluan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/38/Flag_of_Tuvalu.svg/40px-Flag_of_Tuvalu.svg.png');}
.ns-0 h2 #Tuvan::before, .ns-0 h2#Tuvan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/7/77/Flag_of_Tuva.svg/40px-Flag_of_Tuva.svg.png');}
.ns-0 h2 #Tzotzil::before, .ns-0 h2#Tzotzil::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3e/Flag_of_the_Mayan_People.svg/40px-Flag_of_the_Mayan_People.svg.png');}
.ns-0 h2 #Tày::before, .ns-0 h2#Tày::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3d/Flag_of_the_Tay_people_(1947-1954).svg/40px-Flag_of_the_Tay_people_(1947-1954).svg.png');}
.ns-0 h2 #Tübatulabal::before, .ns-0 h2#Tübatulabal::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/45/Seal_of_Kern_County,_California.png/40px-Seal_of_Kern_County,_California.png');}
.ns-0 h2 #Udmurt::before, .ns-0 h2#Udmurt::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c7/Flag_of_Udmurtia.svg/40px-Flag_of_Udmurtia.svg.png');}
.ns-0 h2 #Ukrainian::before, .ns-0 h2#Ukrainian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/49/Flag_of_Ukraine.svg/40px-Flag_of_Ukraine.svg.png');}
.ns-0 h2 #Umbrian::before, .ns-0 h2#Umbrian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/cc/Flag_of_Umbria.svg/40px-Flag_of_Umbria.svg.png');}
.ns-0 h2 #Unserdeutsch::before, .ns-0 h2#Unserdeutsch::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/10/Flag_of_East_New_Britain.svg/40px-Flag_of_East_New_Britain.svg.png');}
.ns-0 h2 #Urdu::before, .ns-0 h2#Urdu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/32/Flag_of_Pakistan.svg/40px-Flag_of_Pakistan.svg.png');}
.ns-0 h2 #Uyghur::before, .ns-0 h2#Uyghur::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Kokbayraq_flag.svg/40px-Kokbayraq_flag.svg.png');}
.ns-0 h2 #Uzbek::before, .ns-0 h2#Uzbek::before, .ns-0 h2 #Uzbeki_Arabic::before, .ns-0 h2#Uzbeki_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/84/Flag_of_Uzbekistan.svg/40px-Flag_of_Uzbekistan.svg.png');}
.ns-0 h2 #Vai::before, .ns-0 h2#Vai::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b8/Flag_of_Liberia.svg/40px-Flag_of_Liberia.svg.png');}
.ns-0 h2 #Veddah::before, .ns-0 h2#Veddah::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1c/Flag_of_the_Uva_Province_(Sri_Lanka).svg/40px-Flag_of_the_Uva_Province_(Sri_Lanka).svg.png');}
.ns-0 h2 #Venda::before, .ns-0 h2#Venda::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/21/Flag_of_Venda_(1973–1994).svg/40px-Flag_of_Venda_(1973–1994).svg.png');}
.ns-0 h2 #Venetan::before, .ns-0 h2#Venetan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d5/Flag_of_Veneto.svg/40px-Flag_of_Veneto.svg.png');}
.ns-0 h2 #Veps::before, .ns-0 h2#Veps::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/45/Flag_of_Vepsia.svg/40px-Flag_of_Vepsia.svg.png');}
.ns-0 h2 #Vilamovian::before, .ns-0 h2#Vilamovian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/3/3d/POL_Wilamowice_COA.svg/35px-POL_Wilamowice_COA.svg.png');}
.ns-0 h2 #Vincentian_Creole_English::before, .ns-0 h2#Vincentian_Creole_English::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/6d/Flag_of_Saint_Vincent_and_the_Grenadines.svg/40px-Flag_of_Saint_Vincent_and_the_Grenadines.svg.png');}
.ns-0 h2 #Virgin_Islands_Creole::before, .ns-0 h2#Virgin_Islands_Creole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f8/Flag_of_the_United_States_Virgin_Islands.svg/40px-Flag_of_the_United_States_Virgin_Islands.svg.png');}
.ns-0 h2 #Volapük::before, .ns-0 h2#Volapük::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/e3/Volapuk_symbol.svg/40px-Volapuk_symbol.svg.png');}
.ns-0 h2 #Volga_German::before, .ns-0 h2#Volga_German::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/55/Flag_of_Volga_Germans_(version).svg/40px-Flag_of_Volga_Germans_(version).svg.png');}
.ns-0 h2 #Votic::before, .ns-0 h2#Votic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Votic_Flag.svg/40px-Votic_Flag.svg.png');}
.ns-0 h2 #Võro::before, .ns-0 h2#Võro::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c1/Võrumaa_lipp.svg/40px-Võrumaa_lipp.svg.png');}
.ns-0 h2 #Walloon::before, .ns-0 h2#Walloon::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/42/Flag_of_Wallonia.svg/40px-Flag_of_Wallonia.svg.png');}
.ns-0 h2 #Wapishana::before, .ns-0 h2#Wapishana::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/9/98/Bandeira_de_Roraima.svg/40px-Bandeira_de_Roraima.svg.png');}
.ns-0 h2 #Waray-Waray::before, .ns-0 h2#Waray-Waray::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f2/Biliran_Provincial_Flag.png/40px-Biliran_Provincial_Flag.png');}
.ns-0 h2 #Wayuu::before, .ns-0 h2#Wayuu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/13/Panteera_Wayuu_Aumentado.png/40px-Panteera_Wayuu_Aumentado.png');}
.ns-0 h2 #Welsh::before, .ns-0 h2#Welsh::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/dc/Flag_of_Wales.svg/40px-Flag_of_Wales.svg.png');}
.ns-0 h2 #West_Circassian::before, .ns-0 h2#West_Circassian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/16/Flag_of_Adygea.svg/40px-Flag_of_Adygea.svg.png');}
.ns-0 h2 #West_Flemish::before, .ns-0 h2#West_Flemish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c2/Flag_of_West_Flanders.svg/40px-Flag_of_West_Flanders.svg.png');}
.ns-0 h2 #West_Frisian::before, .ns-0 h2#West_Frisian::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/ca/Frisian_flag.svg/40px-Frisian_flag.svg.png');}
.ns-0 h2 #West_Miraya_Bikol::before, .ns-0 h2#West_Miraya_Bikol::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/c2/Albay_Flag.png/40px-Albay_Flag.png');}
.ns-0 h2 #Western_Apache::before, .ns-0 h2#Western_Apache::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/8b/Coat_of_arms_of_Sonora.svg/35px-Coat_of_arms_of_Sonora.svg.png');}
.ns-0 h2 #Western_Cham::before, .ns-0 h2#Western_Cham::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/45/Bandera_Front_Alliberament_Cham.svg/40px-Bandera_Front_Alliberament_Cham.svg.png');}
.ns-0 h2 #Western_Mari::before, .ns-0 h2#Western_Mari::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/24/Flag_of_Gornomariysky_rayon.svg/40px-Flag_of_Gornomariysky_rayon.svg.png');}
.ns-0 h2 #Wichí_Lhamtés_Güisnay::before, .ns-0 h2#Wichí_Lhamtés_Güisnay::before, .ns-0 h2 #Wichí_Lhamtés_Nocten::before, .ns-0 h2#Wichí_Lhamtés_Nocten::before, .ns-0 h2 #Wichí_Lhamtés_Vejoz::before, .ns-0 h2#Wichí_Lhamtés_Vejoz::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/66/Bandera_del_pueblo_Wichi.svg/40px-Bandera_del_pueblo_Wichi.svg.png');}
.ns-0 h2 #Woiwurrung::before, .ns-0 h2#Woiwurrung::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/08/Flag_of_Victoria_(Australia).svg/40px-Flag_of_Victoria_(Australia).svg.png');}
.ns-0 h2 #Wolio::before, .ns-0 h2#Wolio::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/55/Lambang_Kota_Bau-Bau.png/35px-Lambang_Kota_Bau-Bau.png');}
.ns-0 h2 #Xerénte::before, .ns-0 h2#Xerénte::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/ff/Bandeira_do_Tocantins.svg/40px-Bandeira_do_Tocantins.svg.png');}
.ns-0 h2 #Xhosa::before, .ns-0 h2#Xhosa::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/c/cc/Flag_of_Ciskei.svg/40px-Flag_of_Ciskei.svg.png');}
.ns-0 h2 #Xokleng::before, .ns-0 h2#Xokleng::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/1/1a/Bandeira_de_Santa_Catarina.svg/40px-Bandeira_de_Santa_Catarina.svg.png');}
.ns-0 h2 #Yakan::before, .ns-0 h2#Yakan::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/87/Flag_of_Basilan.svg/40px-Flag_of_Basilan.svg.png');}
.ns-0 h2 #Yakut::before, .ns-0 h2#Yakut::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/e/eb/Flag_of_Sakha.svg/40px-Flag_of_Sakha.svg.png');}
.ns-0 h2 #Yapese::before, .ns-0 h2#Yapese::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Flag_of_Yap.svg/40px-Flag_of_Yap.svg.png');}
.ns-0 h2 #Yaqui::before, .ns-0 h2#Yaqui::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/41/Flag_of_the_Pascua_Yaqui_Tribe_of_Arizona.svg/40px-Flag_of_the_Pascua_Yaqui_Tribe_of_Arizona.svg.png');}
.ns-0 h2 #Ye\'kwana::before, .ns-0 h2#Ye\'kwana::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/0/06/Flag_of_Venezuela.svg/40px-Flag_of_Venezuela.svg.png');}
.ns-0 h2 #Yemeni_Arabic::before, .ns-0 h2#Yemeni_Arabic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/89/Flag_of_Yemen.svg/40px-Flag_of_Yemen.svg.png');}
.ns-0 h2 #Yiddish::before, .ns-0 h2#Yiddish::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/d/d6/Proposed_Yiddish_flag.svg/40px-Proposed_Yiddish_flag.svg.png');}
.ns-0 h2 #Yilan_Creole::before, .ns-0 h2#Yilan_Creole::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b4/Flag_of_Yilan_County.svg/40px-Flag_of_Yilan_County.svg.png');}
.ns-0 h2 #Yogad::before, .ns-0 h2#Yogad::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/bf/Flag_of_Echague,_Isabela_(2022-present).png/40px-Flag_of_Echague,_Isabela_(2022-present).png');}
.ns-0 h2 #Yola::before, .ns-0 h2#Yola::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/40/Flag_of_Yola.svg/40px-Flag_of_Yola.svg.png');}
.ns-0 h2 #Yonaguni::before, .ns-0 h2#Yonaguni::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/4/45/Flag_of_Yonaguni,_Okinawa.svg/40px-Flag_of_Yonaguni,_Okinawa.svg.png');}
.ns-0 h2 #Yucatec_Maya::before, .ns-0 h2#Yucatec_Maya::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/2/26/Flag_of_the_Republic_of_Yucatan.svg/40px-Flag_of_the_Republic_of_Yucatan.svg.png');}
.ns-0 h2 #Yuracare::before, .ns-0 h2#Yuracare::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f0/Flag_of_Cochabamba.svg/40px-Flag_of_Cochabamba.svg.png');}
.ns-0 h2 #Zaniza_Zapotec::before, .ns-0 h2#Zaniza_Zapotec::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/b/b2/Flag_of_Oaxaca.svg/40px-Flag_of_Oaxaca.svg.png');}
.ns-0 h2 #Zarma::before, .ns-0 h2#Zarma::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/f/f4/Flag_of_Niger.svg/40px-Flag_of_Niger.svg.png');}
.ns-0 h2 #Zazaki::before, .ns-0 h2#Zazaki::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/8/89/Flag_of_Zazaistan.svg/40px-Flag_of_Zazaistan.svg.png');}
.ns-0 h2 #Zealandic::before, .ns-0 h2#Zealandic::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/5/54/Flag_of_Zeeland.svg/40px-Flag_of_Zeeland.svg.png');}
.ns-0 h2 #Zhuang::before, .ns-0 h2#Zhuang::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/64/Flag_of_the_Zhuang_people.svg/40px-Flag_of_the_Zhuang_people.svg.png');}
.ns-0 h2 #Zulu::before, .ns-0 h2#Zulu::before{padding-right:5px;
content:url('//upload.wikimedia.org/wikipedia/commons/thumb/6/60/Flag_of_the_KwaZulu-Natal_Province.png/40px-Flag_of_the_KwaZulu-Natal_Province.png');}
jck1btrdumjdqvveo7urrvsepfplb4z
MediaWiki:Gadget-WiktPreviewRightTOCs.css
8
17940
88882
2017-09-03T07:27:11Z
en>Dixtosa
0
88882
css
text/css
html:not([dummyparam]) .ns-0 #toc {
float: right;
clear: right;
margin-left: 7px;
margin-bottom: 6px;
}
97xoqtgvaxinybefujx240ftm0v7mhb
88883
88882
2026-08-06T23:09:19Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-WiktPreviewRightTOCs.css]]
88882
css
text/css
html:not([dummyparam]) .ns-0 #toc {
float: right;
clear: right;
margin-left: 7px;
margin-bottom: 6px;
}
97xoqtgvaxinybefujx240ftm0v7mhb
MediaWiki:Gadget-LogoTiles.css
8
17941
88884
2026-04-28T13:04:23Z
en>Catonif
0
lower logo a bit, see [[Wiktionary:Grease_pit/2026/April#Setting_to_enable_the_"tiles"_logo_is_suddenly_broken|discussion]]
88884
css
text/css
#p-logo a {
background-image: url(//upload.wikimedia.org/wikipedia/commons/thumb/f/ff/WiktionaryEn.svg/250px-WiktionaryEn.svg.png);
background-size: 136px;
background-repeat: no-repeat;
background-position-y: 0;
}
mpf24gdbw8xbu4dt5n7w716rkty4axh
88885
88884
2026-08-06T23:09:27Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-LogoTiles.css]]
88884
css
text/css
#p-logo a {
background-image: url(//upload.wikimedia.org/wikipedia/commons/thumb/f/ff/WiktionaryEn.svg/250px-WiktionaryEn.svg.png);
background-size: 136px;
background-repeat: no-repeat;
background-position-y: 0;
}
mpf24gdbw8xbu4dt5n7w716rkty4axh
MediaWiki:Gadget-RoundBullets4Lists.css
8
17942
88886
2014-07-31T06:05:56Z
en>Kephir
0
Migrating pure-CSS gadgets from [[WT:PREFS]]
88886
css
text/css
ul {
list-style-image:none; list-style-type:disc;}
d4wgez9ezgmv0qm269tbobokv69mkgh
88887
88886
2026-08-06T23:09:38Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-RoundBullets4Lists.css]]
88886
css
text/css
ul {
list-style-image:none; list-style-type:disc;}
d4wgez9ezgmv0qm269tbobokv69mkgh
MediaWiki:Gadget-BC-AD.js
8
17943
88888
2019-11-15T19:29:56Z
en>Erutuon
0
fix handling of CE ranges such as "46 CE – 120 CE"
88888
javascript
text/javascript
// <nowiki>
// Switches the year-numbering system from BCE/CE to BC/AD
/* jshint undef: true */
/* globals $, mw */
"use strict";
function yearFormatSwitch() {
/*
* This code supports date labels formatted using the following wikitext.
* (See {{B.C.E}} and {{C.E.}}.) BCE can be replaced by CE:
<small class="ce-date">[[Appendix:Glossary#BCE|<span title="Glossary and display preference">BCE</span>]]</small>
*/
var convert = { "BCE": "BC", "B.C.E.": "B.C.", "CE": "AD", "C.E.": "A.D." };
var datetags = document.getElementsByClassName("ce-date");
var articlePath = mw.config.get("wgArticlePath");
var links = {
BC: articlePath.replace("$1", "Appendix:Glossary#BC"),
AD: articlePath.replace("$1", "Appendix:Glossary#AD")
};
for (var i = 0; i < datetags.length; i++) {
var datetag = datetags[i],
link = datetag && datetag.firstChild,
innerSpan = link && link.firstChild,
abbreviation = innerSpan && innerSpan.firstChild && innerSpan.firstChild.nodeValue;
if (!(link && innerSpan && abbreviation))
continue;
if (abbreviation === "CE" || abbreviation === "C.E.") {
link.href = links.AD;
innerSpan.textContent = convert[abbreviation];
// Change position of year number: 10 CE → AD 10.
var beforeDatetag = datetag.previousSibling;
if (!beforeDatetag)
continue;
var patt = /((?:\d{1,4}\s*(?:-|–|to)\s*)?\d{1,4})\s*$/; // first if hyphen-minus, then en-dash
var matches = beforeDatetag.nodeValue.match(patt);
if (matches === null)
continue;
var yearNumber = matches[matches.length - 1];
beforeDatetag.nodeValue = beforeDatetag.nodeValue.substring(0, beforeDatetag.nodeValue.length - matches[0].length);
datetag.parentElement.insertBefore(document.createTextNode(" " + yearNumber), datetag.nextSibling);
} else if (abbreviation === "BCE" || abbreviation === "B.C.E.") {
datetag.firstChild.href = links.BC;
innerSpan.textContent = convert[abbreviation];
}
}
/*
* This code handles dates with the class ce-date2:
* <small class="ce-date2">CE</small>
*/
var datetags2 = document.getElementsByClassName("ce-date2");
for (i = 0; i < datetags2.length; ++i ) {
var datetag2 = datetags2[i];
datetag2.textContent = convert[datetag2.textContent];
}
}
$(yearFormatSwitch);
// </nowiki>
cb3cj9vs7sca9owe00ofpdhot9lavv0
88889
88888
2026-08-06T23:09:47Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-BC-AD.js]]
88888
javascript
text/javascript
// <nowiki>
// Switches the year-numbering system from BCE/CE to BC/AD
/* jshint undef: true */
/* globals $, mw */
"use strict";
function yearFormatSwitch() {
/*
* This code supports date labels formatted using the following wikitext.
* (See {{B.C.E}} and {{C.E.}}.) BCE can be replaced by CE:
<small class="ce-date">[[Appendix:Glossary#BCE|<span title="Glossary and display preference">BCE</span>]]</small>
*/
var convert = { "BCE": "BC", "B.C.E.": "B.C.", "CE": "AD", "C.E.": "A.D." };
var datetags = document.getElementsByClassName("ce-date");
var articlePath = mw.config.get("wgArticlePath");
var links = {
BC: articlePath.replace("$1", "Appendix:Glossary#BC"),
AD: articlePath.replace("$1", "Appendix:Glossary#AD")
};
for (var i = 0; i < datetags.length; i++) {
var datetag = datetags[i],
link = datetag && datetag.firstChild,
innerSpan = link && link.firstChild,
abbreviation = innerSpan && innerSpan.firstChild && innerSpan.firstChild.nodeValue;
if (!(link && innerSpan && abbreviation))
continue;
if (abbreviation === "CE" || abbreviation === "C.E.") {
link.href = links.AD;
innerSpan.textContent = convert[abbreviation];
// Change position of year number: 10 CE → AD 10.
var beforeDatetag = datetag.previousSibling;
if (!beforeDatetag)
continue;
var patt = /((?:\d{1,4}\s*(?:-|–|to)\s*)?\d{1,4})\s*$/; // first if hyphen-minus, then en-dash
var matches = beforeDatetag.nodeValue.match(patt);
if (matches === null)
continue;
var yearNumber = matches[matches.length - 1];
beforeDatetag.nodeValue = beforeDatetag.nodeValue.substring(0, beforeDatetag.nodeValue.length - matches[0].length);
datetag.parentElement.insertBefore(document.createTextNode(" " + yearNumber), datetag.nextSibling);
} else if (abbreviation === "BCE" || abbreviation === "B.C.E.") {
datetag.firstChild.href = links.BC;
innerSpan.textContent = convert[abbreviation];
}
}
/*
* This code handles dates with the class ce-date2:
* <small class="ce-date2">CE</small>
*/
var datetags2 = document.getElementsByClassName("ce-date2");
for (i = 0; i < datetags2.length; ++i ) {
var datetag2 = datetags2[i];
datetag2.textContent = convert[datetag2.textContent];
}
}
$(yearFormatSwitch);
// </nowiki>
cb3cj9vs7sca9owe00ofpdhot9lavv0
MediaWiki:Gadget-AutoContrastFixer.js
8
17944
88890
2025-10-08T16:06:59Z
en>Surjection
0
88890
javascript
text/javascript
// <nowiki>
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, esversion:6 */
/* global $, mw */
/**
* Automatically adjusts contrast of templates in night mode.
*
* Author(s): Surjection
* Last updated: 2025-10-08
*/
// [[MediaWiki:Gadget-WiktGadgetPrefs.js]]
const preferences = mw.wiktGadgetPrefs.get(
"autoContrastFixer",
{
label: {
en: "Automatically fix custom template colors in night mode",
},
},
{
darkTextColorOverride: {
type: "boolean",
default: false,
label: {
en: "Apply also to templates with explicit dark text colors",
},
},
}
);
const darkTextColorOverride = preferences.darkTextColorOverride;
/* Color conversion code. */
/**************************/
function parseCssValue(cssAlpha, divisor) {
/**
* Parses a numeric value in CSS syntax, either given directly or
* as a percentage.
*
* If a divisor is given, it is used to scale the value, unless it is
* a percentage.
*/
if (cssAlpha.endsWith("%")) {
return parseCssValue(cssAlpha.slice(0, -1).trim(), 100.0);
}
return Number(cssAlpha) / (divisor || 1.0);
}
// RegEx used by parseCssColor
const RE_CSS_HEX_COLOR_3 = /^#([0-9A-Fa-f])([0-9A-Fa-f])([0-9A-Fa-f])$/;
const RE_CSS_HEX_COLOR_6 =
/^#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$/;
const RE_CSS_RGB_COLOR =
/^rgb\(\s*([0-9.]+\s*%?)(?:\s+|\s*,\s*)([0-9.]+\s*%?)(?:\s+|\s*,\s*)([0-9.]+\s*%?)\s*\)$/;
const RE_CSS_RGBA_COLOR =
/^rgba?\(\s*([0-9.]+\s*%?)\s*,\s*([0-9.]+\s*%?)\s*,\s*([0-9.]+\s*%?)\s*,\s*([0-9.]+\s*%?)\s*\)$/;
const RE_CSS_RGBA_SLASH_COLOR =
/^rgba?\(\s*([0-9.]+\s*%?)\s+([0-9.]+\s*%?)\s+([0-9.]+\s*%?)\s*\/\s*([0-9.]+\s*%?)\s*\)$/;
function parseCssColor(cssColor) {
/**
* Parses a color in CSS syntax and returns it in RGBA format:
*
* [r, g, b, a]
* with each value within [0, 1].
*
* Returns undefined if the color could not be parsed.
*/
let m;
if ((m = cssColor.match(RE_CSS_RGB_COLOR))) {
return [
parseCssValue(m[1], 255.0),
parseCssValue(m[2], 255.0),
parseCssValue(m[3], 255.0),
1.0,
];
}
if ((m = cssColor.match(RE_CSS_RGBA_COLOR))) {
return [
parseCssValue(m[1], 255.0),
parseCssValue(m[2], 255.0),
parseCssValue(m[3], 255.0),
parseCssValue(m[4]),
];
}
if ((m = cssColor.match(RE_CSS_RGBA_SLASH_COLOR))) {
return [
parseCssValue(m[1], 255.0),
parseCssValue(m[2], 255.0),
parseCssValue(m[3], 255.0),
parseCssValue(m[4]),
];
}
if ((m = cssColor.match(RE_CSS_HEX_COLOR_6))) {
return [
parseInt(m[1], 16) / 255.0,
parseInt(m[2], 16) / 255.0,
parseInt(m[3], 16) / 255.0,
1.0,
];
}
if ((m = cssColor.match(RE_CSS_HEX_COLOR_3))) {
return [
parseInt(m[1], 16) / 15.0,
parseInt(m[2], 16) / 15.0,
parseInt(m[3], 16) / 15.0,
1.0,
];
}
if (cssColor === "transparent") return [0, 0, 0, 0];
return undefined;
}
function makeCssColor(rgbaColor) {
/**
* Converts the given RGBA color into CSS format.
*/
const [r, g, b, a] = rgbaColor;
const normR = Math.round(r * 255.0);
const normG = Math.round(g * 255.0);
const normB = Math.round(b * 255.0);
if (a >= 1.0) {
return `rgb(${normR}, ${normG}, ${normB})`;
} else {
return `rgba(${normR}, ${normG}, ${normB}, ${a})`;
}
}
function rgbToHsl(r, g, b) {
/**
* Converts colors from RGB to HSL (hue, saturation, lightness).
*
* The RGB inputs should all lie within [0, 1].
*
* With valid inputs, the hue value will lie within [0, 6],
* while the saturation and lightness values will lie within [0, 1].
*/
const min = Math.min(r, g, b);
const max = Math.max(r, g, b);
const delta = max - min;
const l = (max + min) / 2;
if (delta === 0) {
return [0, 0, l];
}
const s = l > 0.5 ? delta / (2 - max - min) : delta / (max + min);
let h;
if (max === r) h = (g - b) / delta + (g < b ? 6 : 0);
else if (max === g) h = (b - r) / delta + 2;
else if (max === b) h = (r - g) / delta + 4;
return [h, s, l];
}
function hslToRgb(h, s, l) {
/**
* Converts colors from HSL (hue, saturation, lightness) to RGB.
*
* The hue value should lie within [0, 6], and the saturation and
* lightness values within [0, 1].
*
* With valid inputs, the RGB outputs will all lie within [0, 1].
*/
if (s === 0) {
return [l, l, l];
}
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
h /= 6;
if (h < 0) h = 1 - h;
h %= 1;
function hueToRgb(p, q, t) {
if (t < 0) t += 1;
else if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * 6 * (2 / 3 - t);
return p;
}
const r = hueToRgb(p, q, h + 1 / 3);
const g = hueToRgb(p, q, h);
const b = hueToRgb(p, q, h - 1 / 3);
return [r, g, b];
}
/* Color processing code. */
/**************************/
function colorLuminance(rgbaColor) {
/**
* Returns the luminance of the RGB color (alpha is ignored) using the
* sRGB/BT.709 luminance formula.
*/
const [r, g, b] = rgbaColor;
const convertSrgbChannel = (x) => x <= 0.03928 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);
return 0.2126 * convertSrgbChannel(r) + 0.7152 * convertSrgbChannel(g) + 0.0722 * convertSrgbChannel(b);
}
function colorContrast(fgRgbaColor, bgRgbaColor) {
/**
* Returns the WCAG contrast ratio (>= 1) for the given two colors.
*/
const q =
(colorLuminance(fgRgbaColor) + 0.05) /
(colorLuminance(bgRgbaColor) + 0.05);
return q < 1 ? 1 / q : q;
}
function darkenBackgroundColor(rgbaColor) {
/**
* Darkens the given RGBA color for it to make it a suitable
* background color in night mode.
*/
const [r, g, b, a] = rgbaColor;
const [h, s, l] = rgbToHsl(r, g, b);
let lAdj = 1 - colorLuminance(rgbaColor);
lAdj = 0.02 + 0.98 * Math.pow(lAdj, 0.95);
const lScale = Math.min(Math.max(lAdj, 0.01) / Math.max(l, 0.01), 1);
const [rr, gg, bb] = hslToRgb(h, (s + s * lScale) / 2, lAdj);
return [rr, gg, bb, a];
}
function darkenBorderColor(rgbaColor) {
/**
* Darkens the given RGBA color for it to make it a suitable
* border color in night mode.
*/
const [r, g, b, a] = rgbaColor;
const [h, s, l] = rgbToHsl(r, g, b);
const [rr, gg, bb] = hslToRgb(h, s * 0.5, l * 0.5);
return [rr, gg, bb, a];
}
function lightenForegroundColor(rgbaColor) {
/**
* Lightens the given RGBA color for it to make it a suitable
* text color in night mode.
*/
const [r, g, b, a] = rgbaColor;
const [h, s, l] = rgbToHsl(r, g, b);
const [rr, gg, bb] = hslToRgb(h, s * 0.5, 0.5 + (1 - l) * 0.5);
return [rr, gg, bb, a];
}
/* Auto-fixer parameters. */
/**************************/
/* Whether to adjust border colors. */
const PARAMETER_ADJUST_BORDER_COLORS = false;
/** Any background-foreground combo with the contrast ratio exceeding
* this value are ignored by the fixer. */
const PARAMETER_MAXIMUM_CONTRAST_TO_FIX = 3;
/** The background color must have at least this much alpha,
* or the element is ignored by the fixer. */
const PARAMETER_MINIMUM_BG_ALPHA = 0.5;
/** The foreground color must have at least this much luminance,
* or the element is ignored by the fixer. */
const PARAMETER_MINIMUM_FG_LUMA = 0.5;
/** The border color must have at least this much luminance,
* or it is ignored by the fixer. */
const PARAMETER_MINIMUM_BORDER_LUMA = 0.7;
/* Auto-fixer framework. */
/*************************/
function isSkinNightMode() {
/**
* Returns true if the user has requested night mode.
*/
if (!window.matchMedia) return;
const htmlRoot = document.documentElement;
const nightPref = htmlRoot.classList.contains(
"skin-theme-clientpref-night"
);
const osPref = htmlRoot.classList.contains("skin-theme-clientpref-os");
const isScreen = window.matchMedia("screen").matches;
const prefersDark = window.matchMedia(
"(prefers-color-scheme: dark)"
).matches;
return isScreen && (nightPref || (osPref && prefersDark));
}
function doAutoContrastFix() {
/**
* Applies the auto-contrast fix to all elements in the
* wikipage body.
*/
// see if the user has requested night mode.
if (!isSkinNightMode()) return;
const view = document.defaultView;
const ELEMENTS_PER_FRAME = 50;
function isCandidate(el) {
const style = view.getComputedStyle(el);
return style.background !== "none" || style.borderStyle !== "none";
}
// TODO optimize; can we skip searching candidates or split it up
// into multiple frames to avoid blocking the page for some time?
const candidates = Array.prototype.filter.call(
document.querySelectorAll("#mw-content-text *"),
isCandidate
);
let autoContrastReport = [];
function autoContrastReportElement(el) {
// find top level element within mw-content-text
let top = el;
while (
top.parentElement &&
!top.parentElement.classList.contains("mw-parser-output")
) {
top = top.parentElement;
}
let nextHeadingLevel = 6;
const foundHeadings = Array(nextHeadingLevel + 1);
let sectionId, sectionNumber;
if (top) {
// find elements
let previous = top;
const recordHeading = function (headingElement, headingLevel) {
foundHeadings[headingLevel] = headingElement.textContent;
if (sectionId == null) sectionId = headingElement.id;
if (sectionNumber == null) {
const editLink =
headingElement.parentElement.querySelector(
".mw-editsection a"
);
const editUrl = !editLink ? null : new URL(editLink.href);
if (
editUrl &&
editUrl.searchParams &&
editUrl.searchParams.has("section")
)
sectionNumber = Number(
editUrl.searchParams.get("section")
);
}
};
while ((previous = previous.previousElementSibling)) {
if (previous.classList.contains("mw-heading")) {
// get heading level
const headingElement =
previous.querySelector("h1,h2,h3,h4,h5,h6");
if (headingElement) {
const headingLevel = Number(
headingElement.tagName.replace(/h/i, "")
);
if (headingLevel && headingLevel <= nextHeadingLevel) {
recordHeading(headingElement, headingLevel);
nextHeadingLevel = headingLevel - 1;
}
}
}
}
}
autoContrastReport.push({
headings: foundHeadings
.filter(function (v) {
return v != null;
})
.join(" > "),
sectionId: sectionId,
sectionNumber: sectionNumber,
topClasses: top ? top.className : undefined,
classes: el.className,
element: el,
});
el.classList.add("wikt-auto-contrast-fixed");
}
function autoContrastFixDone() {
/**
* Executed when the auto-contrast fix has been applied
* to all elements.
*/
if (autoContrastReport.length > 0) {
console.log(
`Applied auto-contrast fix to ${autoContrastReport.length} elements`
);
console.log("Full auto-contrast report", autoContrastReport);
}
}
function doAutoContrastFixOne(el) {
/**
* Applies the auto-contrast fix to the specified element.
*/
// exemptions
if (el.closest(".wikt-auto-contrast-exempt")) return;
if (el.closest(".mw-no-invert")) return;
const computedStyle = view.getComputedStyle(el);
// get background and foreground colors.
const cssBackgroundColor = computedStyle.backgroundColor;
const parsedBackgroundColor = parseCssColor(cssBackgroundColor);
if (parsedBackgroundColor == null) return;
const cssForegroundColor = computedStyle.color;
const parsedForegroundColor = parseCssColor(cssForegroundColor);
if (parsedForegroundColor == null) return;
let fixedElement = false;
const hasDarkTextColor =
colorLuminance(parsedForegroundColor) < PARAMETER_MINIMUM_FG_LUMA;
// check that:
// * the background color has enough alpha
// * the text color is actually light (or overridden)
// * the contrast is bad enough to consider fixing (or dark text color and overridden)
// * this isn't something like an image tag
if (
parsedBackgroundColor[3] >= PARAMETER_MINIMUM_BG_ALPHA &&
(darkTextColorOverride || !hasDarkTextColor) &&
(colorContrast(parsedForegroundColor, parsedBackgroundColor) <=
PARAMETER_MAXIMUM_CONTRAST_TO_FIX ||
(hasDarkTextColor && darkTextColorOverride)) &&
el.tagName !== "IMG"
) {
if (
colorLuminance(parsedForegroundColor) <
PARAMETER_MINIMUM_FG_LUMA
) {
// generate a new foreground color and apply it
const newForegroundColor = lightenForegroundColor(
parsedForegroundColor
);
if (
colorLuminance(newForegroundColor) >
colorLuminance(parsedForegroundColor)
) {
fixedElement = true;
el.style.color = makeCssColor(newForegroundColor);
}
}
// generate a background color and apply it, but only if it is darker.
const newBackgroundColor = darkenBackgroundColor(
parsedBackgroundColor
);
if (
colorLuminance(newBackgroundColor) <
colorLuminance(parsedBackgroundColor)
) {
fixedElement = true;
el.style.backgroundColor = makeCssColor(newBackgroundColor);
}
}
if (computedStyle.borderStyle !== "none" && PARAMETER_ADJUST_BORDER_COLORS) {
// odds are the border color needs the same treatment.
const cssBorderColor = view.getComputedStyle(el).borderColor;
const parsedBorderColor = parseCssColor(cssBorderColor);
if (
parsedBorderColor != null &&
colorLuminance(parsedBorderColor) >=
PARAMETER_MINIMUM_BORDER_LUMA
) {
// generate a border color and apply it, but only if it is darker.
const newBorderColor = darkenBorderColor(parsedBorderColor);
if (
colorLuminance(newBorderColor) <
colorLuminance(parsedBorderColor)
) {
fixedElement = true;
el.style.borderColor = makeCssColor(newBorderColor);
}
}
}
if (fixedElement) {
// report that we found an element to fix.
autoContrastReportElement(el);
}
}
function doAutoContrastFixBatch(base) {
/**
* Applies the auto-contrast fix to the next batch
* of elements.
*/
for (let offset = 0; offset < ELEMENTS_PER_FRAME; ++offset) {
const index = base + offset;
if (index >= candidates.length) {
autoContrastFixDone();
return;
}
doAutoContrastFixOne(candidates[index]);
}
window.requestAnimationFrame(function () {
doAutoContrastFixBatch(base + ELEMENTS_PER_FRAME);
});
}
doAutoContrastFixBatch(0);
}
$(document).ready(function () {
doAutoContrastFix();
});
// </nowiki>
de6lc9zew25kkllal43ljzyhnkyh6h9
88891
88890
2026-08-06T23:09:56Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-AutoContrastFixer.js]]
88890
javascript
text/javascript
// <nowiki>
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, esversion:6 */
/* global $, mw */
/**
* Automatically adjusts contrast of templates in night mode.
*
* Author(s): Surjection
* Last updated: 2025-10-08
*/
// [[MediaWiki:Gadget-WiktGadgetPrefs.js]]
const preferences = mw.wiktGadgetPrefs.get(
"autoContrastFixer",
{
label: {
en: "Automatically fix custom template colors in night mode",
},
},
{
darkTextColorOverride: {
type: "boolean",
default: false,
label: {
en: "Apply also to templates with explicit dark text colors",
},
},
}
);
const darkTextColorOverride = preferences.darkTextColorOverride;
/* Color conversion code. */
/**************************/
function parseCssValue(cssAlpha, divisor) {
/**
* Parses a numeric value in CSS syntax, either given directly or
* as a percentage.
*
* If a divisor is given, it is used to scale the value, unless it is
* a percentage.
*/
if (cssAlpha.endsWith("%")) {
return parseCssValue(cssAlpha.slice(0, -1).trim(), 100.0);
}
return Number(cssAlpha) / (divisor || 1.0);
}
// RegEx used by parseCssColor
const RE_CSS_HEX_COLOR_3 = /^#([0-9A-Fa-f])([0-9A-Fa-f])([0-9A-Fa-f])$/;
const RE_CSS_HEX_COLOR_6 =
/^#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$/;
const RE_CSS_RGB_COLOR =
/^rgb\(\s*([0-9.]+\s*%?)(?:\s+|\s*,\s*)([0-9.]+\s*%?)(?:\s+|\s*,\s*)([0-9.]+\s*%?)\s*\)$/;
const RE_CSS_RGBA_COLOR =
/^rgba?\(\s*([0-9.]+\s*%?)\s*,\s*([0-9.]+\s*%?)\s*,\s*([0-9.]+\s*%?)\s*,\s*([0-9.]+\s*%?)\s*\)$/;
const RE_CSS_RGBA_SLASH_COLOR =
/^rgba?\(\s*([0-9.]+\s*%?)\s+([0-9.]+\s*%?)\s+([0-9.]+\s*%?)\s*\/\s*([0-9.]+\s*%?)\s*\)$/;
function parseCssColor(cssColor) {
/**
* Parses a color in CSS syntax and returns it in RGBA format:
*
* [r, g, b, a]
* with each value within [0, 1].
*
* Returns undefined if the color could not be parsed.
*/
let m;
if ((m = cssColor.match(RE_CSS_RGB_COLOR))) {
return [
parseCssValue(m[1], 255.0),
parseCssValue(m[2], 255.0),
parseCssValue(m[3], 255.0),
1.0,
];
}
if ((m = cssColor.match(RE_CSS_RGBA_COLOR))) {
return [
parseCssValue(m[1], 255.0),
parseCssValue(m[2], 255.0),
parseCssValue(m[3], 255.0),
parseCssValue(m[4]),
];
}
if ((m = cssColor.match(RE_CSS_RGBA_SLASH_COLOR))) {
return [
parseCssValue(m[1], 255.0),
parseCssValue(m[2], 255.0),
parseCssValue(m[3], 255.0),
parseCssValue(m[4]),
];
}
if ((m = cssColor.match(RE_CSS_HEX_COLOR_6))) {
return [
parseInt(m[1], 16) / 255.0,
parseInt(m[2], 16) / 255.0,
parseInt(m[3], 16) / 255.0,
1.0,
];
}
if ((m = cssColor.match(RE_CSS_HEX_COLOR_3))) {
return [
parseInt(m[1], 16) / 15.0,
parseInt(m[2], 16) / 15.0,
parseInt(m[3], 16) / 15.0,
1.0,
];
}
if (cssColor === "transparent") return [0, 0, 0, 0];
return undefined;
}
function makeCssColor(rgbaColor) {
/**
* Converts the given RGBA color into CSS format.
*/
const [r, g, b, a] = rgbaColor;
const normR = Math.round(r * 255.0);
const normG = Math.round(g * 255.0);
const normB = Math.round(b * 255.0);
if (a >= 1.0) {
return `rgb(${normR}, ${normG}, ${normB})`;
} else {
return `rgba(${normR}, ${normG}, ${normB}, ${a})`;
}
}
function rgbToHsl(r, g, b) {
/**
* Converts colors from RGB to HSL (hue, saturation, lightness).
*
* The RGB inputs should all lie within [0, 1].
*
* With valid inputs, the hue value will lie within [0, 6],
* while the saturation and lightness values will lie within [0, 1].
*/
const min = Math.min(r, g, b);
const max = Math.max(r, g, b);
const delta = max - min;
const l = (max + min) / 2;
if (delta === 0) {
return [0, 0, l];
}
const s = l > 0.5 ? delta / (2 - max - min) : delta / (max + min);
let h;
if (max === r) h = (g - b) / delta + (g < b ? 6 : 0);
else if (max === g) h = (b - r) / delta + 2;
else if (max === b) h = (r - g) / delta + 4;
return [h, s, l];
}
function hslToRgb(h, s, l) {
/**
* Converts colors from HSL (hue, saturation, lightness) to RGB.
*
* The hue value should lie within [0, 6], and the saturation and
* lightness values within [0, 1].
*
* With valid inputs, the RGB outputs will all lie within [0, 1].
*/
if (s === 0) {
return [l, l, l];
}
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
h /= 6;
if (h < 0) h = 1 - h;
h %= 1;
function hueToRgb(p, q, t) {
if (t < 0) t += 1;
else if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * 6 * (2 / 3 - t);
return p;
}
const r = hueToRgb(p, q, h + 1 / 3);
const g = hueToRgb(p, q, h);
const b = hueToRgb(p, q, h - 1 / 3);
return [r, g, b];
}
/* Color processing code. */
/**************************/
function colorLuminance(rgbaColor) {
/**
* Returns the luminance of the RGB color (alpha is ignored) using the
* sRGB/BT.709 luminance formula.
*/
const [r, g, b] = rgbaColor;
const convertSrgbChannel = (x) => x <= 0.03928 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);
return 0.2126 * convertSrgbChannel(r) + 0.7152 * convertSrgbChannel(g) + 0.0722 * convertSrgbChannel(b);
}
function colorContrast(fgRgbaColor, bgRgbaColor) {
/**
* Returns the WCAG contrast ratio (>= 1) for the given two colors.
*/
const q =
(colorLuminance(fgRgbaColor) + 0.05) /
(colorLuminance(bgRgbaColor) + 0.05);
return q < 1 ? 1 / q : q;
}
function darkenBackgroundColor(rgbaColor) {
/**
* Darkens the given RGBA color for it to make it a suitable
* background color in night mode.
*/
const [r, g, b, a] = rgbaColor;
const [h, s, l] = rgbToHsl(r, g, b);
let lAdj = 1 - colorLuminance(rgbaColor);
lAdj = 0.02 + 0.98 * Math.pow(lAdj, 0.95);
const lScale = Math.min(Math.max(lAdj, 0.01) / Math.max(l, 0.01), 1);
const [rr, gg, bb] = hslToRgb(h, (s + s * lScale) / 2, lAdj);
return [rr, gg, bb, a];
}
function darkenBorderColor(rgbaColor) {
/**
* Darkens the given RGBA color for it to make it a suitable
* border color in night mode.
*/
const [r, g, b, a] = rgbaColor;
const [h, s, l] = rgbToHsl(r, g, b);
const [rr, gg, bb] = hslToRgb(h, s * 0.5, l * 0.5);
return [rr, gg, bb, a];
}
function lightenForegroundColor(rgbaColor) {
/**
* Lightens the given RGBA color for it to make it a suitable
* text color in night mode.
*/
const [r, g, b, a] = rgbaColor;
const [h, s, l] = rgbToHsl(r, g, b);
const [rr, gg, bb] = hslToRgb(h, s * 0.5, 0.5 + (1 - l) * 0.5);
return [rr, gg, bb, a];
}
/* Auto-fixer parameters. */
/**************************/
/* Whether to adjust border colors. */
const PARAMETER_ADJUST_BORDER_COLORS = false;
/** Any background-foreground combo with the contrast ratio exceeding
* this value are ignored by the fixer. */
const PARAMETER_MAXIMUM_CONTRAST_TO_FIX = 3;
/** The background color must have at least this much alpha,
* or the element is ignored by the fixer. */
const PARAMETER_MINIMUM_BG_ALPHA = 0.5;
/** The foreground color must have at least this much luminance,
* or the element is ignored by the fixer. */
const PARAMETER_MINIMUM_FG_LUMA = 0.5;
/** The border color must have at least this much luminance,
* or it is ignored by the fixer. */
const PARAMETER_MINIMUM_BORDER_LUMA = 0.7;
/* Auto-fixer framework. */
/*************************/
function isSkinNightMode() {
/**
* Returns true if the user has requested night mode.
*/
if (!window.matchMedia) return;
const htmlRoot = document.documentElement;
const nightPref = htmlRoot.classList.contains(
"skin-theme-clientpref-night"
);
const osPref = htmlRoot.classList.contains("skin-theme-clientpref-os");
const isScreen = window.matchMedia("screen").matches;
const prefersDark = window.matchMedia(
"(prefers-color-scheme: dark)"
).matches;
return isScreen && (nightPref || (osPref && prefersDark));
}
function doAutoContrastFix() {
/**
* Applies the auto-contrast fix to all elements in the
* wikipage body.
*/
// see if the user has requested night mode.
if (!isSkinNightMode()) return;
const view = document.defaultView;
const ELEMENTS_PER_FRAME = 50;
function isCandidate(el) {
const style = view.getComputedStyle(el);
return style.background !== "none" || style.borderStyle !== "none";
}
// TODO optimize; can we skip searching candidates or split it up
// into multiple frames to avoid blocking the page for some time?
const candidates = Array.prototype.filter.call(
document.querySelectorAll("#mw-content-text *"),
isCandidate
);
let autoContrastReport = [];
function autoContrastReportElement(el) {
// find top level element within mw-content-text
let top = el;
while (
top.parentElement &&
!top.parentElement.classList.contains("mw-parser-output")
) {
top = top.parentElement;
}
let nextHeadingLevel = 6;
const foundHeadings = Array(nextHeadingLevel + 1);
let sectionId, sectionNumber;
if (top) {
// find elements
let previous = top;
const recordHeading = function (headingElement, headingLevel) {
foundHeadings[headingLevel] = headingElement.textContent;
if (sectionId == null) sectionId = headingElement.id;
if (sectionNumber == null) {
const editLink =
headingElement.parentElement.querySelector(
".mw-editsection a"
);
const editUrl = !editLink ? null : new URL(editLink.href);
if (
editUrl &&
editUrl.searchParams &&
editUrl.searchParams.has("section")
)
sectionNumber = Number(
editUrl.searchParams.get("section")
);
}
};
while ((previous = previous.previousElementSibling)) {
if (previous.classList.contains("mw-heading")) {
// get heading level
const headingElement =
previous.querySelector("h1,h2,h3,h4,h5,h6");
if (headingElement) {
const headingLevel = Number(
headingElement.tagName.replace(/h/i, "")
);
if (headingLevel && headingLevel <= nextHeadingLevel) {
recordHeading(headingElement, headingLevel);
nextHeadingLevel = headingLevel - 1;
}
}
}
}
}
autoContrastReport.push({
headings: foundHeadings
.filter(function (v) {
return v != null;
})
.join(" > "),
sectionId: sectionId,
sectionNumber: sectionNumber,
topClasses: top ? top.className : undefined,
classes: el.className,
element: el,
});
el.classList.add("wikt-auto-contrast-fixed");
}
function autoContrastFixDone() {
/**
* Executed when the auto-contrast fix has been applied
* to all elements.
*/
if (autoContrastReport.length > 0) {
console.log(
`Applied auto-contrast fix to ${autoContrastReport.length} elements`
);
console.log("Full auto-contrast report", autoContrastReport);
}
}
function doAutoContrastFixOne(el) {
/**
* Applies the auto-contrast fix to the specified element.
*/
// exemptions
if (el.closest(".wikt-auto-contrast-exempt")) return;
if (el.closest(".mw-no-invert")) return;
const computedStyle = view.getComputedStyle(el);
// get background and foreground colors.
const cssBackgroundColor = computedStyle.backgroundColor;
const parsedBackgroundColor = parseCssColor(cssBackgroundColor);
if (parsedBackgroundColor == null) return;
const cssForegroundColor = computedStyle.color;
const parsedForegroundColor = parseCssColor(cssForegroundColor);
if (parsedForegroundColor == null) return;
let fixedElement = false;
const hasDarkTextColor =
colorLuminance(parsedForegroundColor) < PARAMETER_MINIMUM_FG_LUMA;
// check that:
// * the background color has enough alpha
// * the text color is actually light (or overridden)
// * the contrast is bad enough to consider fixing (or dark text color and overridden)
// * this isn't something like an image tag
if (
parsedBackgroundColor[3] >= PARAMETER_MINIMUM_BG_ALPHA &&
(darkTextColorOverride || !hasDarkTextColor) &&
(colorContrast(parsedForegroundColor, parsedBackgroundColor) <=
PARAMETER_MAXIMUM_CONTRAST_TO_FIX ||
(hasDarkTextColor && darkTextColorOverride)) &&
el.tagName !== "IMG"
) {
if (
colorLuminance(parsedForegroundColor) <
PARAMETER_MINIMUM_FG_LUMA
) {
// generate a new foreground color and apply it
const newForegroundColor = lightenForegroundColor(
parsedForegroundColor
);
if (
colorLuminance(newForegroundColor) >
colorLuminance(parsedForegroundColor)
) {
fixedElement = true;
el.style.color = makeCssColor(newForegroundColor);
}
}
// generate a background color and apply it, but only if it is darker.
const newBackgroundColor = darkenBackgroundColor(
parsedBackgroundColor
);
if (
colorLuminance(newBackgroundColor) <
colorLuminance(parsedBackgroundColor)
) {
fixedElement = true;
el.style.backgroundColor = makeCssColor(newBackgroundColor);
}
}
if (computedStyle.borderStyle !== "none" && PARAMETER_ADJUST_BORDER_COLORS) {
// odds are the border color needs the same treatment.
const cssBorderColor = view.getComputedStyle(el).borderColor;
const parsedBorderColor = parseCssColor(cssBorderColor);
if (
parsedBorderColor != null &&
colorLuminance(parsedBorderColor) >=
PARAMETER_MINIMUM_BORDER_LUMA
) {
// generate a border color and apply it, but only if it is darker.
const newBorderColor = darkenBorderColor(parsedBorderColor);
if (
colorLuminance(newBorderColor) <
colorLuminance(parsedBorderColor)
) {
fixedElement = true;
el.style.borderColor = makeCssColor(newBorderColor);
}
}
}
if (fixedElement) {
// report that we found an element to fix.
autoContrastReportElement(el);
}
}
function doAutoContrastFixBatch(base) {
/**
* Applies the auto-contrast fix to the next batch
* of elements.
*/
for (let offset = 0; offset < ELEMENTS_PER_FRAME; ++offset) {
const index = base + offset;
if (index >= candidates.length) {
autoContrastFixDone();
return;
}
doAutoContrastFixOne(candidates[index]);
}
window.requestAnimationFrame(function () {
doAutoContrastFixBatch(base + ELEMENTS_PER_FRAME);
});
}
doAutoContrastFixBatch(0);
}
$(document).ready(function () {
doAutoContrastFix();
});
// </nowiki>
de6lc9zew25kkllal43ljzyhnkyh6h9
MediaWiki:Gadget-ShowIDs.js
8
17945
88892
2026-05-29T20:09:29Z
en>Fenakhay
0
88892
javascript
text/javascript
// <nowiki>
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, esversion:6 */
/* global $, mw */
/**
* Display sense and etym IDs (including Wikidata item/lexeme links).
*
* Note: this gadget also loads ShowIDs-pagestyles, which offers a
* CSS-only fallback to this gadget that does not require JavaScript.
* The CSS is nevertheless required for this JavaScript gadget
* to display correctly.
* CSS: [[MediaWiki:Gadget-ShowIDs-pagestyles.css]]
*
* Author(s): Surjection, Fenakhay
* Last updated: 2026-05-29
*/
$(document).ready(() => {
'use strict';
const ID_CLASSES = [
{ name: 'senseid', showTooltip: true },
{ name: 'etymid', showTooltip: true },
{ name: 'etymonid', showTooltip: false }
];
const WIKIDATA_ITEM_PATTERN = /^Q\d+$/;
const WIKIDATA_LEXEME_PATTERN = /^L(\d+)(?:-S(\d+))?$/;
function getWikidataUrl(idStr) {
if (WIKIDATA_ITEM_PATTERN.test(idStr)) {
return `https://www.wikidata.org/wiki/${idStr}`;
}
const lexemeMatch = idStr.match(WIKIDATA_LEXEME_PATTERN);
if (lexemeMatch) {
const lexemeId = `L${lexemeMatch[1]}`;
const baseUrl = `https://www.wikidata.org/wiki/Lexeme:${lexemeId}`;
const senseId = lexemeMatch[2];
return senseId ? `${baseUrl}#S${senseId}` : baseUrl;
}
return null;
}
/**
* Generate CSS selectors for all ID classes
*/
function generateSelectors(suffix = '') {
return ID_CLASSES.map(cls => `.${cls.name}${suffix}`).join(', ');
}
/**
* Returns the text content of element, excluding children (unlike .text()).
*/
function getTextOnlyRoot($elem) {
return $elem.clone().children().remove().end().text();
}
/**
* Move element (sole child span) into the next visible, non-floated paragraph.
*/
function moveIntoNextParagraph($elem) {
const $parentP = $elem.parent('p');
// Only proceed if parent is a p with single child and no text content
if (!$parentP.length ||
$parentP.children().length !== 1 ||
getTextOnlyRoot($parentP).trim()) {
return;
}
let $next = $parentP.next();
while ($next.length) {
// Skip floated or hidden elements
if ($next.css('float') !== 'none' || !$next.is(':visible')) {
$next = $next.next();
continue;
}
// Move content to next paragraph if found
if ($next.is('p')) {
$next.prepend('\n');
$parentP.children().prependTo($next);
$parentP.remove();
}
break;
}
}
/**
* Create display element for ID (link for Wikidata, span for others).
*/
function createDisplayElement(idStr, tooltip) {
const wikidataUrl = getWikidataUrl(idStr);
return wikidataUrl
? $('<a/>', {
href: wikidataUrl,
text: idStr,
title: tooltip || undefined,
target: '_blank',
rel: 'noopener noreferrer'
})
: $('<span/>', {
text: idStr,
title: tooltip || undefined
});
}
/**
* Generate tooltip text for template.
*/
function generateTooltip(classConfig, lang, idStr) {
return (classConfig.showTooltip && lang && idStr)
? `{{${classConfig.name}|${lang}|${idStr}}}`
: '';
}
// Process each ID element
$(generateSelectors()).each(function () {
const $elem = $(this);
// Find the matching class configuration
const classConfig = ID_CLASSES.find(cls => $elem.hasClass(cls.name));
if (!classConfig) return;
const displayedClass = `${classConfig.name}-displayed`;
// Skip if already processed
if ($elem.hasClass(displayedClass)) return;
// Extract data
const lang = $elem.attr('data-lang');
const rawId = $elem.data('id');
// Skip if no ID (e.g., etymonid without id=)
if (rawId === undefined || rawId === null || rawId === '') {
return;
}
const idStr = String(rawId);
const tooltip = generateTooltip(classConfig, lang, idStr);
const hasText = !!$elem.text().trim();
// Create display element
const $content = createDisplayElement(idStr, tooltip);
if ($elem.is('li') || hasText) {
// Wrap in new span to preserve existing content
const $wrapper = $('<span/>', { class: displayedClass }).append($content);
// Add space if needed
if (!hasText || !$elem.text().match(/^\s/)) {
$elem.prepend(' ');
}
$elem.prepend($wrapper);
} else {
// Replace element content entirely
$elem.addClass(displayedClass).empty().append($content);
moveIntoNextParagraph($elem);
}
});
// Prevent CSS-only pseudo-elements from duplicating the display
mw.util.addCSS(
`${generateSelectors('::before')} { content: none !important; display: none !important; }`
);
});
// Ensure CSS fallback is loaded
mw.loader.load('ext.gadget.ShowIDs-pagestyles');
// </nowiki>
6olupmbgmixgloydhtnn7oudokddd9c
88893
88892
2026-08-06T23:10:06Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-ShowIDs.js]]
88892
javascript
text/javascript
// <nowiki>
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, esversion:6 */
/* global $, mw */
/**
* Display sense and etym IDs (including Wikidata item/lexeme links).
*
* Note: this gadget also loads ShowIDs-pagestyles, which offers a
* CSS-only fallback to this gadget that does not require JavaScript.
* The CSS is nevertheless required for this JavaScript gadget
* to display correctly.
* CSS: [[MediaWiki:Gadget-ShowIDs-pagestyles.css]]
*
* Author(s): Surjection, Fenakhay
* Last updated: 2026-05-29
*/
$(document).ready(() => {
'use strict';
const ID_CLASSES = [
{ name: 'senseid', showTooltip: true },
{ name: 'etymid', showTooltip: true },
{ name: 'etymonid', showTooltip: false }
];
const WIKIDATA_ITEM_PATTERN = /^Q\d+$/;
const WIKIDATA_LEXEME_PATTERN = /^L(\d+)(?:-S(\d+))?$/;
function getWikidataUrl(idStr) {
if (WIKIDATA_ITEM_PATTERN.test(idStr)) {
return `https://www.wikidata.org/wiki/${idStr}`;
}
const lexemeMatch = idStr.match(WIKIDATA_LEXEME_PATTERN);
if (lexemeMatch) {
const lexemeId = `L${lexemeMatch[1]}`;
const baseUrl = `https://www.wikidata.org/wiki/Lexeme:${lexemeId}`;
const senseId = lexemeMatch[2];
return senseId ? `${baseUrl}#S${senseId}` : baseUrl;
}
return null;
}
/**
* Generate CSS selectors for all ID classes
*/
function generateSelectors(suffix = '') {
return ID_CLASSES.map(cls => `.${cls.name}${suffix}`).join(', ');
}
/**
* Returns the text content of element, excluding children (unlike .text()).
*/
function getTextOnlyRoot($elem) {
return $elem.clone().children().remove().end().text();
}
/**
* Move element (sole child span) into the next visible, non-floated paragraph.
*/
function moveIntoNextParagraph($elem) {
const $parentP = $elem.parent('p');
// Only proceed if parent is a p with single child and no text content
if (!$parentP.length ||
$parentP.children().length !== 1 ||
getTextOnlyRoot($parentP).trim()) {
return;
}
let $next = $parentP.next();
while ($next.length) {
// Skip floated or hidden elements
if ($next.css('float') !== 'none' || !$next.is(':visible')) {
$next = $next.next();
continue;
}
// Move content to next paragraph if found
if ($next.is('p')) {
$next.prepend('\n');
$parentP.children().prependTo($next);
$parentP.remove();
}
break;
}
}
/**
* Create display element for ID (link for Wikidata, span for others).
*/
function createDisplayElement(idStr, tooltip) {
const wikidataUrl = getWikidataUrl(idStr);
return wikidataUrl
? $('<a/>', {
href: wikidataUrl,
text: idStr,
title: tooltip || undefined,
target: '_blank',
rel: 'noopener noreferrer'
})
: $('<span/>', {
text: idStr,
title: tooltip || undefined
});
}
/**
* Generate tooltip text for template.
*/
function generateTooltip(classConfig, lang, idStr) {
return (classConfig.showTooltip && lang && idStr)
? `{{${classConfig.name}|${lang}|${idStr}}}`
: '';
}
// Process each ID element
$(generateSelectors()).each(function () {
const $elem = $(this);
// Find the matching class configuration
const classConfig = ID_CLASSES.find(cls => $elem.hasClass(cls.name));
if (!classConfig) return;
const displayedClass = `${classConfig.name}-displayed`;
// Skip if already processed
if ($elem.hasClass(displayedClass)) return;
// Extract data
const lang = $elem.attr('data-lang');
const rawId = $elem.data('id');
// Skip if no ID (e.g., etymonid without id=)
if (rawId === undefined || rawId === null || rawId === '') {
return;
}
const idStr = String(rawId);
const tooltip = generateTooltip(classConfig, lang, idStr);
const hasText = !!$elem.text().trim();
// Create display element
const $content = createDisplayElement(idStr, tooltip);
if ($elem.is('li') || hasText) {
// Wrap in new span to preserve existing content
const $wrapper = $('<span/>', { class: displayedClass }).append($content);
// Add space if needed
if (!hasText || !$elem.text().match(/^\s/)) {
$elem.prepend(' ');
}
$elem.prepend($wrapper);
} else {
// Replace element content entirely
$elem.addClass(displayedClass).empty().append($content);
moveIntoNextParagraph($elem);
}
});
// Prevent CSS-only pseudo-elements from duplicating the display
mw.util.addCSS(
`${generateSelectors('::before')} { content: none !important; display: none !important; }`
);
});
// Ensure CSS fallback is loaded
mw.loader.load('ext.gadget.ShowIDs-pagestyles');
// </nowiki>
6olupmbgmixgloydhtnn7oudokddd9c
MediaWiki:Gadget-TreeDescs.js
8
17946
88894
2025-09-07T20:15:03Z
en>Catonif
0
fix the desctree issue, while keeping separate uls when immediate
88894
javascript
text/javascript
mw.loader.load("ext.gadget.TreeDescs-pagestyles");
$(".mw-heading").filter(function() {
return $(this).children().first().text() === "Descendants"
}).each(function() {
let $selected = $(this).nextUntil(".mw-heading");
$selected.find("ul + ul").each(function () {
$(this).prev("ul").append($(this).children());
$(this).remove();
});
$selected.filter("ul").addClass("treeview");
$selected.filter(":not(ul)").find("ul").addClass("treeview");
});
n0tnt5mcbjkodiy8ft24phf4kqc4xh2
88895
88894
2026-08-06T23:10:14Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-TreeDescs.js]]
88894
javascript
text/javascript
mw.loader.load("ext.gadget.TreeDescs-pagestyles");
$(".mw-heading").filter(function() {
return $(this).children().first().text() === "Descendants"
}).each(function() {
let $selected = $(this).nextUntil(".mw-heading");
$selected.find("ul + ul").each(function () {
$(this).prev("ul").append($(this).children());
$(this).remove();
});
$selected.filter("ul").addClass("treeview");
$selected.filter(":not(ul)").find("ul").addClass("treeview");
});
n0tnt5mcbjkodiy8ft24phf4kqc4xh2
MediaWiki:Gadget-NoMiniToc.js
8
17947
88896
2025-09-10T17:33:46Z
en>Surjection
0
88896
javascript
text/javascript
// <nowiki>
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, esversion:6 */
/* global $, mw */
$(document).ready(() => {
// If the TOC is inside a div with the data-toc-length attribute, move it out of there.
const toc = document.querySelector("#mw-content-text #toc");
if (toc) {
const minitocWrapped = toc.closest("[data-toc-length]");
if (minitocWrapped) {
minitocWrapped.parentNode.insertBefore(toc, minitocWrapped);
minitocWrapped.style.display = "none";
}
}
});
t0zb45l3ptr8xj0nyq01roj43h4k2ek
88897
88896
2026-08-06T23:10:22Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-NoMiniToc.js]]
88896
javascript
text/javascript
// <nowiki>
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, esversion:6 */
/* global $, mw */
$(document).ready(() => {
// If the TOC is inside a div with the data-toc-length attribute, move it out of there.
const toc = document.querySelector("#mw-content-text #toc");
if (toc) {
const minitocWrapped = toc.closest("[data-toc-length]");
if (minitocWrapped) {
minitocWrapped.parentNode.insertBefore(toc, minitocWrapped);
minitocWrapped.style.display = "none";
}
}
});
t0zb45l3ptr8xj0nyq01roj43h4k2ek
MediaWiki:Gadget-NoMiniToc.css
8
17948
88898
2025-12-11T13:21:48Z
en>Surjection
0
88898
css
text/css
.minitoc { display: none; }
/* increased specificity */
.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc { display: none; }
/* selector intentionally repeated a bunch of times - CSS specificity trick */
div[data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length] > #toc { display: inline-block; }
.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc { display: block; }
oh53ebe99fwlaunck0mn8xdzcph7qfw
88899
88898
2026-08-06T23:10:30Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-NoMiniToc.css]]
88898
css
text/css
.minitoc { display: none; }
/* increased specificity */
.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc.minitoc { display: none; }
/* selector intentionally repeated a bunch of times - CSS specificity trick */
div[data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length][data-toc-length] > #toc { display: inline-block; }
.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc.mammoth-minitoc { display: block; }
oh53ebe99fwlaunck0mn8xdzcph7qfw
MediaWiki:Gadget-NsfwOverride.js
8
17949
88900
2026-04-23T18:11:56Z
en>Fenakhay
0
Created page with "// <nowiki> /* jshint maxerr:1048576, strict:true, undef:true, latedef:true, esversion:8 */ /* global mw */ /** * Disable NSFW blur/overlay used in {{multiple images}}/{{img}}. * * Author(s): Fenakhay * Last updated: 2026-04-23 */ ( function () { "use strict"; function applyNsfwOverride( $content ) { if ( !$content || !$content.length ) return; $content.find( ".tmulti-nsfw .thumbimage" ).each( function () { this.classList.add( "tmulti-nsfw-gadget-ready"..."
88900
javascript
text/javascript
// <nowiki>
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, esversion:8 */
/* global mw */
/**
* Disable NSFW blur/overlay used in {{multiple images}}/{{img}}.
*
* Author(s): Fenakhay
* Last updated: 2026-04-23
*/
( function () {
"use strict";
function applyNsfwOverride( $content ) {
if ( !$content || !$content.length ) return;
$content.find( ".tmulti-nsfw .thumbimage" ).each( function () {
this.classList.add( "tmulti-nsfw-gadget-ready" );
} );
}
mw.hook( "wikipage.content" ).add( function ( $content ) {
if ( mw.config.get( "wgAction" ) !== "view" ) return;
applyNsfwOverride( $content );
} );
}() );
// </nowiki>
nbwkx51vnqmj9njp5j9c14tt2nplxj6
88901
88900
2026-08-06T23:10:39Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-NsfwOverride.js]]
88900
javascript
text/javascript
// <nowiki>
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, esversion:8 */
/* global mw */
/**
* Disable NSFW blur/overlay used in {{multiple images}}/{{img}}.
*
* Author(s): Fenakhay
* Last updated: 2026-04-23
*/
( function () {
"use strict";
function applyNsfwOverride( $content ) {
if ( !$content || !$content.length ) return;
$content.find( ".tmulti-nsfw .thumbimage" ).each( function () {
this.classList.add( "tmulti-nsfw-gadget-ready" );
} );
}
mw.hook( "wikipage.content" ).add( function ( $content ) {
if ( mw.config.get( "wgAction" ) !== "view" ) return;
applyNsfwOverride( $content );
} );
}() );
// </nowiki>
nbwkx51vnqmj9njp5j9c14tt2nplxj6
MediaWiki:Gadget-NsfwOverride.css
8
17950
88902
2026-04-23T18:12:27Z
en>Fenakhay
0
Created page with ".tmulti-nsfw .thumbimage.tmulti-nsfw-gadget-ready { position: relative !important; } .tmulti-nsfw .thumbimage.tmulti-nsfw-gadget-ready .tmulti-nsfw-media { filter: none !important; -webkit-filter: none !important; clip-path: none !important; -webkit-clip-path: none !important; margin: 0 !important; padding: 0 !important; transform: none !important; } .tmulti-nsfw .thumbimage.tmulti-nsfw-gadget-ready .tmulti-nsfw-media > span, .tmulti-nsfw .thumbimage.tmulti-nsf..."
88902
css
text/css
.tmulti-nsfw .thumbimage.tmulti-nsfw-gadget-ready {
position: relative !important;
}
.tmulti-nsfw .thumbimage.tmulti-nsfw-gadget-ready .tmulti-nsfw-media {
filter: none !important;
-webkit-filter: none !important;
clip-path: none !important;
-webkit-clip-path: none !important;
margin: 0 !important;
padding: 0 !important;
transform: none !important;
}
.tmulti-nsfw .thumbimage.tmulti-nsfw-gadget-ready .tmulti-nsfw-media > span,
.tmulti-nsfw .thumbimage.tmulti-nsfw-gadget-ready .tmulti-nsfw-media > span > a {
display: block !important;
}
.tmulti-nsfw .thumbimage.tmulti-nsfw-gadget-ready .tmulti-nsfw-media img {
display: block !important;
width: auto !important;
max-width: 100% !important;
margin: 0 !important;
transform: none !important;
}
.tmulti-nsfw .thumbimage.tmulti-nsfw-gadget-ready .tmulti-nsfw-warning {
display: none !important;
}
ps9qxz4iwfe6m04chv8o3qfzdw1b5e5
88903
88902
2026-08-06T23:10:46Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-NsfwOverride.css]]
88902
css
text/css
.tmulti-nsfw .thumbimage.tmulti-nsfw-gadget-ready {
position: relative !important;
}
.tmulti-nsfw .thumbimage.tmulti-nsfw-gadget-ready .tmulti-nsfw-media {
filter: none !important;
-webkit-filter: none !important;
clip-path: none !important;
-webkit-clip-path: none !important;
margin: 0 !important;
padding: 0 !important;
transform: none !important;
}
.tmulti-nsfw .thumbimage.tmulti-nsfw-gadget-ready .tmulti-nsfw-media > span,
.tmulti-nsfw .thumbimage.tmulti-nsfw-gadget-ready .tmulti-nsfw-media > span > a {
display: block !important;
}
.tmulti-nsfw .thumbimage.tmulti-nsfw-gadget-ready .tmulti-nsfw-media img {
display: block !important;
width: auto !important;
max-width: 100% !important;
margin: 0 !important;
transform: none !important;
}
.tmulti-nsfw .thumbimage.tmulti-nsfw-gadget-ready .tmulti-nsfw-warning {
display: none !important;
}
ps9qxz4iwfe6m04chv8o3qfzdw1b5e5
MediaWiki:Gadget-MobileCollapseCategories.js
8
17951
88904
2026-05-15T17:37:56Z
en>Surjection
0
Created page with "// <nowiki> /* jshint maxerr:1048576, strict:true, undef:true, latedef:true, esversion:6 */ /* global $, mw, window.VisibilityToggles */ /** * Makes the category list at the bottom of the page collapsible * (intended for use with the mobile Minerva skin). * * Author(s): Surjection * Last updated: 2026-05-15 */ function makeCategoriesCollapsible($el) { const $ul = $("#mw-normal-catlinks > ul, #mw-hidden-catlinks > ul", $el); if (!$(".category-list-toggle")..."
88904
javascript
text/javascript
// <nowiki>
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, esversion:6 */
/* global $, mw, window.VisibilityToggles */
/**
* Makes the category list at the bottom of the page collapsible
* (intended for use with the mobile Minerva skin).
*
* Author(s): Surjection
* Last updated: 2026-05-15
*/
function makeCategoriesCollapsible($el) {
const $ul = $("#mw-normal-catlinks > ul, #mw-hidden-catlinks > ul", $el);
if (!$(".category-list-toggle").length && $ul.length) {
const $toggleElement = $("<div>").addClass("category-list-toggle");
const $navToggle = $("<span>").addClass("NavToggle").attr("data-nosnippet", "");
const $toggleButton = $("<a>").attr("role", "button").attr("tabindex", "0");
const toggleCategory = "page categories";
const showButtonText = "show ▼";
const hideButtonText = "hide ▲";
$navToggle.append($toggleButton).appendTo($toggleElement);
$toggleElement.insertBefore($ul.parent()[0]);
$toggleElement.show();
$toggleButton.text(hideButtonText);
$toggleElement.on("click", window.VisibilityToggles.register(toggleCategory,
function show() {
$toggleButton.text(hideButtonText);
$ul.removeClass("category-list-hidden");
},
function hide() {
$toggleButton.text(showButtonText);
$ul.addClass("category-list-hidden");
})
);
}
}
mw.util.addCSS(`
/* Specificity hacks */
.category-list-hidden, .category-list-hidden.category-list-hidden.category-list-hidden,
.catlinks .category-list-hidden.category-list-hidden.category-list-hidden {
display: none;
}
`);
$(document).ready(function () {
if (window.VisibilityToggles) {
$("#catlinks").each(function () {
makeCategoriesCollapsible($(this));
});
}
});
// </nowiki>
sxz94g45qyu55ne1e3s6c7edsy0zoom
88905
88904
2026-08-06T23:10:55Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-MobileCollapseCategories.js]]
88904
javascript
text/javascript
// <nowiki>
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, esversion:6 */
/* global $, mw, window.VisibilityToggles */
/**
* Makes the category list at the bottom of the page collapsible
* (intended for use with the mobile Minerva skin).
*
* Author(s): Surjection
* Last updated: 2026-05-15
*/
function makeCategoriesCollapsible($el) {
const $ul = $("#mw-normal-catlinks > ul, #mw-hidden-catlinks > ul", $el);
if (!$(".category-list-toggle").length && $ul.length) {
const $toggleElement = $("<div>").addClass("category-list-toggle");
const $navToggle = $("<span>").addClass("NavToggle").attr("data-nosnippet", "");
const $toggleButton = $("<a>").attr("role", "button").attr("tabindex", "0");
const toggleCategory = "page categories";
const showButtonText = "show ▼";
const hideButtonText = "hide ▲";
$navToggle.append($toggleButton).appendTo($toggleElement);
$toggleElement.insertBefore($ul.parent()[0]);
$toggleElement.show();
$toggleButton.text(hideButtonText);
$toggleElement.on("click", window.VisibilityToggles.register(toggleCategory,
function show() {
$toggleButton.text(hideButtonText);
$ul.removeClass("category-list-hidden");
},
function hide() {
$toggleButton.text(showButtonText);
$ul.addClass("category-list-hidden");
})
);
}
}
mw.util.addCSS(`
/* Specificity hacks */
.category-list-hidden, .category-list-hidden.category-list-hidden.category-list-hidden,
.catlinks .category-list-hidden.category-list-hidden.category-list-hidden {
display: none;
}
`);
$(document).ready(function () {
if (window.VisibilityToggles) {
$("#catlinks").each(function () {
makeCategoriesCollapsible($(this));
});
}
});
// </nowiki>
sxz94g45qyu55ne1e3s6c7edsy0zoom
MediaWiki:Gadget-Palette.css
8
17952
88906
2026-07-23T16:12:33Z
en>Surjection
0
add scoped shorthand
88906
css
text/css
/* Wiktionary's palette of colors that work equally well in light and dark mode. */
/* Inspired by [[:ru:w:MediaWiki:Gadget-common-site.css]] */
:root, .skin-invert, .notheme {
--wikt-palette-white: #ffffff; /* Same as <code>--background-color-base</code> */
--wikt-palette-black: #202122; /* Body text; same as <code>--color-base</code> */
--wikt-palette-beige: #fffbf2;
--wikt-palette-darkgrey: #54595d;
--wikt-palette-grey: #9e9e9e;
--wikt-palette-lightgrey: #cccccc;
--wikt-palette-lightergrey: #eeeeee;
--wikt-palette-cream: #f8f8f0;
--wikt-palette-pink: #ffe0f0;
--wikt-palette-peach: #e4d4c0;
--wikt-palette-gold: #b88d00;
--wikt-palette-dullgold: #826f34;
--wikt-palette-yellow: #ebe8b9;
--wikt-palette-dullyellow: #dedbc1;
--wikt-palette-brightyellow: #ffee77;
--wikt-palette-palegreen: #e2f6e2;
--wikt-palette-palergreen: #f8fffa;
--wikt-palette-mint: #c0e4c0;
--wikt-palette-green: #33d733;
--wikt-palette-forestgreen: #235923;
--wikt-palette-luminousgreen: #dafdda;
--wikt-palette-paleblue: #f8f9fa;
--wikt-palette-cyan: #eefbf9;
--wikt-palette-palecyan: #f6f7f7;
--wikt-palette-lightblue: #d9ebff;
--wikt-palette-dulllightblue: #aabbdd;
--wikt-palette-blue: #3333ff;
--wikt-palette-dullblue: #49555f;
--wikt-palette-dullcyan: #eaecf0;
--wikt-palette-deepblue: #2f445c;
--wikt-palette-lavender: #f8f8ff;
--wikt-palette-lightyellow: #ffffe0;
--wikt-palette-red: #d73333; /* Same as <code>--background-color-destructive</code> */
--wikt-palette-darkred: #660000;
--wikt-palette-deepred: #bf0218;
--wikt-palette-orange: #f2caa4;
--wikt-palette-lighterblue: #ebf4ff;
--wikt-palette-dulllighterblue: #e2eaf9;
--wikt-palette-lightred: #fee7e6;
--wikt-palette-indigo: #aaaaee;
--wikt-palette-lightindigo: #e9e9ff;
--wikt-palette-palerpink: #fff3f5;
--wikt-palette-honey: #81540e;
/* Autogenerated with [[Wiktionary:Palette/numbered]] */
--wikt-palette-red-0: #fef9f9;
--wikt-palette-red-1: #fdeef0;
--wikt-palette-red-2: #fce4e6;
--wikt-palette-red-3: #fad9dc;
--wikt-palette-red-4: #f7c2c6;
--wikt-palette-red-5: #f5afb5;
--wikt-palette-red-6: #f29ba3;
--wikt-palette-red-7: #f0858e;
--wikt-palette-red-8: #ed6d78;
--wikt-palette-red-9: #ce4350;
--wikt-palette-scarlet-0: #fef9f7;
--wikt-palette-scarlet-1: #fcefeb;
--wikt-palette-scarlet-2: #fae5de;
--wikt-palette-scarlet-3: #f8dad1;
--wikt-palette-scarlet-4: #f4c4b5;
--wikt-palette-scarlet-5: #f1b3a0;
--wikt-palette-scarlet-6: #eea088;
--wikt-palette-scarlet-7: #ea8c70;
--wikt-palette-scarlet-8: #e67553;
--wikt-palette-scarlet-9: #c25433;
--wikt-palette-orange-0: #fdfaf5;
--wikt-palette-orange-1: #fbf0e4;
--wikt-palette-orange-2: #f8e7d4;
--wikt-palette-orange-3: #f5ddc3;
--wikt-palette-orange-4: #f0c89e;
--wikt-palette-orange-5: #ebb882;
--wikt-palette-orange-6: #e6a662;
--wikt-palette-orange-7: #e19341;
--wikt-palette-orange-8: #db7f1e;
--wikt-palette-orange-9: #b16210;
--wikt-palette-amber-0: #fdfaf3;
--wikt-palette-amber-1: #f9f1df;
--wikt-palette-amber-2: #f5e8cc;
--wikt-palette-amber-3: #f1dfb8;
--wikt-palette-amber-4: #e8cc8f;
--wikt-palette-amber-5: #e2bc6d;
--wikt-palette-amber-6: #dbac4a;
--wikt-palette-amber-7: #d39b24;
--wikt-palette-amber-8: #c38c17;
--wikt-palette-amber-9: #9b6f10;
--wikt-palette-yellow-0: #fbfbf4;
--wikt-palette-yellow-1: #f5f2e0;
--wikt-palette-yellow-2: #eeeacc;
--wikt-palette-yellow-3: #e8e2b9;
--wikt-palette-yellow-4: #dbd191;
--wikt-palette-yellow-5: #d0c370;
--wikt-palette-yellow-6: #c5b44f;
--wikt-palette-yellow-7: #b6a63f;
--wikt-palette-yellow-8: #a69737;
--wikt-palette-yellow-9: #847827;
--wikt-palette-lime-0: #f9fbf3;
--wikt-palette-lime-1: #eff4e1;
--wikt-palette-lime-2: #e5edce;
--wikt-palette-lime-3: #dbe5bc;
--wikt-palette-lime-4: #c5d693;
--wikt-palette-lime-5: #b5ca73;
--wikt-palette-lime-6: #a3be52;
--wikt-palette-lime-7: #97af4e;
--wikt-palette-lime-8: #889f43;
--wikt-palette-lime-9: #6b7f2f;
--wikt-palette-green-0: #f8fbf5;
--wikt-palette-green-1: #eaf5e4;
--wikt-palette-green-2: #ddefd3;
--wikt-palette-green-3: #cee8c1;
--wikt-palette-green-4: #b3da9d;
--wikt-palette-green-5: #9cd080;
--wikt-palette-green-6: #83c460;
--wikt-palette-green-7: #69b840;
--wikt-palette-green-8: #64a640;
--wikt-palette-green-9: #4b852d;
--wikt-palette-teal-0: #f6fcf9;
--wikt-palette-teal-1: #e5f6ee;
--wikt-palette-teal-2: #d5efe3;
--wikt-palette-teal-3: #c3e9d7;
--wikt-palette-teal-4: #9fdcc0;
--wikt-palette-teal-5: #82d1ac;
--wikt-palette-teal-6: #64c599;
--wikt-palette-teal-7: #41b982;
--wikt-palette-teal-8: #3fa878;
--wikt-palette-teal-9: #2c865d;
--wikt-palette-cyan-0: #f6fbfc;
--wikt-palette-cyan-1: #e7f4f7;
--wikt-palette-cyan-2: #d7edf1;
--wikt-palette-cyan-3: #c7e6ec;
--wikt-palette-cyan-4: #a6d7e0;
--wikt-palette-cyan-5: #8accd7;
--wikt-palette-cyan-6: #6ebfcd;
--wikt-palette-cyan-7: #50b2c3;
--wikt-palette-cyan-8: #2ea3b7;
--wikt-palette-cyan-9: #288191;
--wikt-palette-blue-0: #f8fafe;
--wikt-palette-blue-1: #ecf2fe;
--wikt-palette-blue-2: #e0eafd;
--wikt-palette-blue-3: #d3e1fc;
--wikt-palette-blue-4: #bbd0fa;
--wikt-palette-blue-5: #a6c2f9;
--wikt-palette-blue-6: #92b3f7;
--wikt-palette-blue-7: #7ca4f5;
--wikt-palette-blue-8: #6494f4;
--wikt-palette-blue-9: #3d72db;
--wikt-palette-indigo-0: #fafafe;
--wikt-palette-indigo-1: #f1f1fd;
--wikt-palette-indigo-2: #e9e7fc;
--wikt-palette-indigo-3: #e0defa;
--wikt-palette-indigo-4: #cecbf8;
--wikt-palette-indigo-5: #bfbcf5;
--wikt-palette-indigo-6: #b0acf3;
--wikt-palette-indigo-7: #a19cf1;
--wikt-palette-indigo-8: #918aef;
--wikt-palette-indigo-9: #6e66e0;
--wikt-palette-purple-0: #fcfafe;
--wikt-palette-purple-1: #f5f0fd;
--wikt-palette-purple-2: #efe6fc;
--wikt-palette-purple-3: #e8dbfb;
--wikt-palette-purple-4: #dbc6f8;
--wikt-palette-purple-5: #d0b6f7;
--wikt-palette-purple-6: #c5a5f5;
--wikt-palette-purple-7: #b993f2;
--wikt-palette-purple-8: #ad7ff0;
--wikt-palette-purple-9: #9053eb;
--wikt-palette-magenta-0: #fcf9fe;
--wikt-palette-magenta-1: #f7effc;
--wikt-palette-magenta-2: #f3e4fa;
--wikt-palette-magenta-3: #eedaf9;
--wikt-palette-magenta-4: #e3c4f5;
--wikt-palette-magenta-5: #dbb2f2;
--wikt-palette-magenta-6: #d3a0ef;
--wikt-palette-magenta-7: #ca8deb;
--wikt-palette-magenta-8: #c078e8;
--wikt-palette-magenta-9: #a844df;
--wikt-palette-rose-0: #fcf9fc;
--wikt-palette-rose-1: #f8f0f6;
--wikt-palette-rose-2: #f3e6ef;
--wikt-palette-rose-3: #eedbe9;
--wikt-palette-rose-4: #e4c7dc;
--wikt-palette-rose-5: #dcb6d2;
--wikt-palette-rose-6: #d4a5c8;
--wikt-palette-rose-7: #cc94bd;
--wikt-palette-rose-8: #c381b1;
--wikt-palette-rose-9: #af5797;
--wikt-palette-brown-0: #fbfaf9;
--wikt-palette-brown-1: #f4f1ee;
--wikt-palette-brown-2: #eee8e4;
--wikt-palette-brown-3: #e7dfd9;
--wikt-palette-brown-4: #d8cdc3;
--wikt-palette-brown-5: #cdbfb2;
--wikt-palette-brown-6: #c2b1a1;
--wikt-palette-brown-7: #b7a18f;
--wikt-palette-brown-8: #ab927c;
--wikt-palette-brown-9: #917155;
--wikt-palette-grey-red-0: #fef9f9;
--wikt-palette-grey-red-1: #fbeff0;
--wikt-palette-grey-red-2: #f9e5e6;
--wikt-palette-grey-red-3: #f7dadc;
--wikt-palette-grey-red-4: #f2c3c8;
--wikt-palette-grey-red-5: #eeb2b7;
--wikt-palette-grey-red-6: #dca5ab;
--wikt-palette-grey-red-7: #ce979c;
--wikt-palette-grey-red-8: #c0878c;
--wikt-palette-grey-red-9: #9f686d;
--wikt-palette-grey-scarlet-0: #fdfaf8;
--wikt-palette-grey-scarlet-1: #faf0ec;
--wikt-palette-grey-scarlet-2: #f7e6e0;
--wikt-palette-grey-scarlet-3: #f4dcd4;
--wikt-palette-grey-scarlet-4: #eec7bb;
--wikt-palette-grey-scarlet-5: #e8b6a7;
--wikt-palette-grey-scarlet-6: #d6a99c;
--wikt-palette-grey-scarlet-7: #c89b8d;
--wikt-palette-grey-scarlet-8: #b98c7e;
--wikt-palette-grey-scarlet-9: #986d5f;
--wikt-palette-grey-orange-0: #fdfaf6;
--wikt-palette-grey-orange-1: #f9f0e8;
--wikt-palette-grey-orange-2: #f4e7d9;
--wikt-palette-grey-orange-3: #f0deca;
--wikt-palette-grey-orange-4: #e8caaa;
--wikt-palette-grey-orange-5: #e1bb92;
--wikt-palette-grey-orange-6: #cfae8a;
--wikt-palette-grey-orange-7: #c19f7c;
--wikt-palette-grey-orange-8: #b2906d;
--wikt-palette-grey-orange-9: #917151;
--wikt-palette-grey-amber-0: #fcfaf5;
--wikt-palette-grey-amber-1: #f7f1e6;
--wikt-palette-grey-amber-2: #f1e9d6;
--wikt-palette-grey-amber-3: #ece0c6;
--wikt-palette-grey-amber-4: #e0cda3;
--wikt-palette-grey-amber-5: #d8bf8a;
--wikt-palette-grey-amber-6: #c6b185;
--wikt-palette-grey-amber-7: #b7a377;
--wikt-palette-grey-amber-8: #a89468;
--wikt-palette-grey-amber-9: #88754c;
--wikt-palette-grey-yellow-0: #fbfaf6;
--wikt-palette-grey-yellow-1: #f3f2e7;
--wikt-palette-grey-yellow-2: #ecead9;
--wikt-palette-grey-yellow-3: #e5e1ca;
--wikt-palette-grey-yellow-4: #d5d0ab;
--wikt-palette-grey-yellow-5: #cac394;
--wikt-palette-grey-yellow-6: #beb57c;
--wikt-palette-grey-yellow-7: #aca67f;
--wikt-palette-grey-yellow-8: #9e9770;
--wikt-palette-grey-yellow-9: #7e7851;
--wikt-palette-grey-lime-0: #fafbf7;
--wikt-palette-grey-lime-1: #f0f3e8;
--wikt-palette-grey-lime-2: #e7ebda;
--wikt-palette-grey-lime-3: #dee3cc;
--wikt-palette-grey-lime-4: #cad3ae;
--wikt-palette-grey-lime-5: #bbc798;
--wikt-palette-grey-lime-6: #abba81;
--wikt-palette-grey-lime-7: #a0a984;
--wikt-palette-grey-lime-8: #919b74;
--wikt-palette-grey-lime-9: #727c55;
--wikt-palette-grey-green-0: #f8fbf7;
--wikt-palette-grey-green-1: #edf4ea;
--wikt-palette-grey-green-2: #e2ecdd;
--wikt-palette-grey-green-3: #d6e5ce;
--wikt-palette-grey-green-4: #bfd6b3;
--wikt-palette-grey-green-5: #acca9d;
--wikt-palette-grey-green-6: #9abe87;
--wikt-palette-grey-green-7: #93ad86;
--wikt-palette-grey-green-8: #839e75;
--wikt-palette-grey-green-9: #647f56;
--wikt-palette-grey-teal-0: #f7fbf9;
--wikt-palette-grey-teal-1: #eaf4ef;
--wikt-palette-grey-teal-2: #ddede5;
--wikt-palette-grey-teal-3: #d0e6dc;
--wikt-palette-grey-teal-4: #b4d7c7;
--wikt-palette-grey-teal-5: #9ecbb6;
--wikt-palette-grey-teal-6: #88bfa5;
--wikt-palette-grey-teal-7: #86ae9b;
--wikt-palette-grey-teal-8: #769f8c;
--wikt-palette-grey-teal-9: #56806d;
--wikt-palette-grey-cyan-0: #f7fbfb;
--wikt-palette-grey-cyan-1: #ebf3f5;
--wikt-palette-grey-cyan-2: #deecee;
--wikt-palette-grey-cyan-3: #d1e4e7;
--wikt-palette-grey-cyan-4: #b6d4da;
--wikt-palette-grey-cyan-5: #a1c8cf;
--wikt-palette-grey-cyan-6: #8cbbc4;
--wikt-palette-grey-cyan-7: #86acb2;
--wikt-palette-grey-cyan-8: #769da3;
--wikt-palette-grey-cyan-9: #577e84;
--wikt-palette-grey-blue-0: #f8fafe;
--wikt-palette-grey-blue-1: #ecf2fc;
--wikt-palette-grey-blue-2: #e1eafb;
--wikt-palette-grey-blue-3: #d5e1f9;
--wikt-palette-grey-blue-4: #bdd0f6;
--wikt-palette-grey-blue-5: #aec2ea;
--wikt-palette-grey-blue-6: #9fb4dd;
--wikt-palette-grey-blue-7: #91a6cf;
--wikt-palette-grey-blue-8: #8297c0;
--wikt-palette-grey-blue-9: #6477a0;
--wikt-palette-grey-indigo-0: #fafafe;
--wikt-palette-grey-indigo-1: #f1f1fb;
--wikt-palette-grey-indigo-2: #e9e8f9;
--wikt-palette-grey-indigo-3: #e0def7;
--wikt-palette-grey-indigo-4: #cdcbf2;
--wikt-palette-grey-indigo-5: #c0bcee;
--wikt-palette-grey-indigo-6: #b2afdc;
--wikt-palette-grey-indigo-7: #a4a1d0;
--wikt-palette-grey-indigo-8: #9592c2;
--wikt-palette-grey-indigo-9: #7572a3;
--wikt-palette-grey-purple-0: #fbfafe;
--wikt-palette-grey-purple-1: #f5f0fc;
--wikt-palette-grey-purple-2: #eee6fa;
--wikt-palette-grey-purple-3: #e7dcf8;
--wikt-palette-grey-purple-4: #d9c8f3;
--wikt-palette-grey-purple-5: #ceb8f0;
--wikt-palette-grey-purple-6: #c0aae1;
--wikt-palette-grey-purple-7: #b19bd3;
--wikt-palette-grey-purple-8: #a38cc5;
--wikt-palette-grey-purple-9: #846da5;
--wikt-palette-grey-magenta-0: #fcf9fd;
--wikt-palette-grey-magenta-1: #f7effa;
--wikt-palette-grey-magenta-2: #f1e5f7;
--wikt-palette-grey-magenta-3: #ebdbf4;
--wikt-palette-grey-magenta-4: #e0c6ee;
--wikt-palette-grey-magenta-5: #d7b5e9;
--wikt-palette-grey-magenta-6: #cda4e4;
--wikt-palette-grey-magenta-7: #bc98d0;
--wikt-palette-grey-magenta-8: #ae89c2;
--wikt-palette-grey-magenta-9: #8e69a2;
--wikt-palette-grey-rose-0: #fcfafb;
--wikt-palette-grey-rose-1: #f6f0f4;
--wikt-palette-grey-rose-2: #efe7ed;
--wikt-palette-grey-rose-3: #e9dde6;
--wikt-palette-grey-rose-4: #ddcad8;
--wikt-palette-grey-rose-5: #d3bacc;
--wikt-palette-grey-rose-6: #c9abc1;
--wikt-palette-grey-rose-7: #be9bb5;
--wikt-palette-grey-rose-8: #b38aa8;
--wikt-palette-grey-rose-9: #8c6e84;
--wikt-palette-grey-brown-0: #fafaf9;
--wikt-palette-grey-brown-1: #f3f2f0;
--wikt-palette-grey-brown-2: #ebe9e6;
--wikt-palette-grey-brown-3: #e4e0dc;
--wikt-palette-grey-brown-4: #d4cec8;
--wikt-palette-grey-brown-5: #c8c0b9;
--wikt-palette-grey-brown-6: #bcb2a9;
--wikt-palette-grey-brown-7: #afa398;
--wikt-palette-grey-brown-8: #a19488;
--wikt-palette-grey-brown-9: #7f766d;
--wikt-palette-grey-0: #fafafa;
--wikt-palette-grey-1: #f2f2f2;
--wikt-palette-grey-2: #e9e9e9;
--wikt-palette-grey-3: #e1e1e1;
--wikt-palette-grey-4: #cfcfcf;
--wikt-palette-grey-5: #c2c2c2;
--wikt-palette-grey-6: #b4b4b4;
--wikt-palette-grey-7: #a5a5a5;
--wikt-palette-grey-8: #979797;
--wikt-palette-grey-9: #777777;
}
/* Styles need to be duplicated exactly between these two selectors. */
@media screen {
html.skin-theme-clientpref-night {
--wikt-palette-white: #101418;
--wikt-palette-black: #eaecf0;
--wikt-palette-beige: #2f1f10;
--wikt-palette-darkgrey: #b2bbc3;
--wikt-palette-grey: #4e4e4e;
--wikt-palette-lightgrey: #3d3d3d;
--wikt-palette-lightergrey: #242424;
--wikt-palette-cream: #2c2c21;
--wikt-palette-pink: #612042;
--wikt-palette-peach: #58230a;
--wikt-palette-gold: #c9b163;
--wikt-palette-dullgold: #ab995e;
--wikt-palette-yellow: #4a3912;
--wikt-palette-dullyellow: #3a3520;
--wikt-palette-brightyellow: #5e5300;
--wikt-palette-palegreen: #112511;
--wikt-palette-palergreen: #051f0c;
--wikt-palette-mint: #194819;
--wikt-palette-green: #aad7aa;
--wikt-palette-forestgreen: #499149;
--wikt-palette-luminousgreen: #103310;
--wikt-palette-paleblue: #1c2229;
--wikt-palette-cyan: #0a3239;
--wikt-palette-palecyan: #171c21;
--wikt-palette-lightblue: #092f58;
--wikt-palette-dulllightblue: #305070;
--wikt-palette-blue: #aaaaff;
--wikt-palette-dullblue: #b8c0c5;
--wikt-palette-dullcyan: #27292d;
--wikt-palette-deepblue: #a5c3e1;
--wikt-palette-lavender: #1a1a2a;
--wikt-palette-lightyellow: #24241a;
--wikt-palette-red: #d73333;
--wikt-palette-darkred: #ffcccc;
--wikt-palette-deepred: #e36a79;
--wikt-palette-orange: #593e02;
--wikt-palette-lighterblue: #0e1e33;
--wikt-palette-dulllighterblue: #102437;
--wikt-palette-lightred: #421211;
--wikt-palette-indigo: #30306f;
--wikt-palette-lightindigo: #242448;
--wikt-palette-palerpink: #251111;
--wikt-palette-honey: #feefb3;
/* Autogenerated with [[Wiktionary:Palette/numbered]] */
--wikt-palette-red-0: #28070a;
--wikt-palette-red-1: #3e0c11;
--wikt-palette-red-2: #5a131a;
--wikt-palette-red-3: #731b24;
--wikt-palette-red-4: #89242d;
--wikt-palette-red-5: #a12d38;
--wikt-palette-red-6: #bf3b48;
--wikt-palette-red-7: #e95462;
--wikt-palette-red-8: #ee7984;
--wikt-palette-red-9: #f3a2aa;
--wikt-palette-scarlet-0: #250c05;
--wikt-palette-scarlet-1: #3a1409;
--wikt-palette-scarlet-2: #521d0e;
--wikt-palette-scarlet-3: #692814;
--wikt-palette-scarlet-4: #7f311a;
--wikt-palette-scarlet-5: #953c22;
--wikt-palette-scarlet-6: #b24b2c;
--wikt-palette-scarlet-7: #e25e37;
--wikt-palette-scarlet-8: #e88062;
--wikt-palette-scarlet-9: #efa791;
--wikt-palette-orange-0: #211101;
--wikt-palette-orange-1: #331b03;
--wikt-palette-orange-2: #462604;
--wikt-palette-orange-3: #5c3206;
--wikt-palette-orange-4: #703d08;
--wikt-palette-orange-5: #85490a;
--wikt-palette-orange-6: #a0580e;
--wikt-palette-orange-7: #cb7114;
--wikt-palette-orange-8: #de8931;
--wikt-palette-orange-9: #e8ac6e;
--wikt-palette-amber-0: #1c1401;
--wikt-palette-amber-1: #2c1f03;
--wikt-palette-amber-2: #3d2a04;
--wikt-palette-amber-3: #503806;
--wikt-palette-amber-4: #614508;
--wikt-palette-amber-5: #74520a;
--wikt-palette-amber-6: #8c640d;
--wikt-palette-amber-7: #b17f13;
--wikt-palette-amber-8: #cc9218;
--wikt-palette-amber-9: #ddb258;
--wikt-palette-yellow-0: #181504;
--wikt-palette-yellow-1: #262107;
--wikt-palette-yellow-2: #342e0a;
--wikt-palette-yellow-3: #443c0f;
--wikt-palette-yellow-4: #534a13;
--wikt-palette-yellow-5: #625819;
--wikt-palette-yellow-6: #776b21;
--wikt-palette-yellow-7: #97882f;
--wikt-palette-yellow-8: #ae9e3b;
--wikt-palette-yellow-9: #c8b95b;
--wikt-palette-lime-0: #131705;
--wikt-palette-lime-1: #1e2409;
--wikt-palette-lime-2: #28310d;
--wikt-palette-lime-3: #354112;
--wikt-palette-lime-4: #424f18;
--wikt-palette-lime-5: #4f5e1f;
--wikt-palette-lime-6: #607228;
--wikt-palette-lime-7: #7b903a;
--wikt-palette-lime-8: #8fa648;
--wikt-palette-lime-9: #aac25f;
--wikt-palette-green-0: #0c1805;
--wikt-palette-green-1: #122608;
--wikt-palette-green-2: #1a340c;
--wikt-palette-green-3: #234411;
--wikt-palette-green-4: #2c5317;
--wikt-palette-green-5: #35631d;
--wikt-palette-green-6: #427726;
--wikt-palette-green-7: #599737;
--wikt-palette-green-8: #6aae45;
--wikt-palette-green-9: #8dc86d;
--wikt-palette-teal-0: #04180f;
--wikt-palette-teal-1: #082618;
--wikt-palette-teal-2: #0c3422;
--wikt-palette-teal-3: #11452d;
--wikt-palette-teal-4: #165438;
--wikt-palette-teal-5: #1c6443;
--wikt-palette-teal-6: #267953;
--wikt-palette-teal-7: #36986b;
--wikt-palette-teal-8: #43af7e;
--wikt-palette-teal-9: #6fc9a0;
--wikt-palette-cyan-0: #04171b;
--wikt-palette-cyan-1: #07242a;
--wikt-palette-cyan-2: #0a3239;
--wikt-palette-cyan-3: #0f424b;
--wikt-palette-cyan-4: #14515c;
--wikt-palette-cyan-5: #1a606d;
--wikt-palette-cyan-6: #227583;
--wikt-palette-cyan-7: #3194a6;
--wikt-palette-cyan-8: #40aabd;
--wikt-palette-cyan-9: #78c4d1;
--wikt-palette-blue-0: #07132d;
--wikt-palette-blue-1: #0b1f45;
--wikt-palette-blue-2: #122b60;
--wikt-palette-blue-3: #19397b;
--wikt-palette-blue-4: #214692;
--wikt-palette-blue-5: #2954ab;
--wikt-palette-blue-6: #3667c9;
--wikt-palette-blue-7: #4e84f2;
--wikt-palette-blue-8: #709cf5;
--wikt-palette-blue-9: #99b8f8;
--wikt-palette-indigo-0: #100e36;
--wikt-palette-indigo-1: #1b1751;
--wikt-palette-indigo-2: #27226c;
--wikt-palette-indigo-3: #352f88;
--wikt-palette-indigo-4: #423b9e;
--wikt-palette-indigo-5: #5049b4;
--wikt-palette-indigo-6: #5f55e7;
--wikt-palette-indigo-7: #8179ec;
--wikt-palette-indigo-8: #9992f0;
--wikt-palette-indigo-9: #b6b1f4;
--wikt-palette-purple-0: #1a0934;
--wikt-palette-purple-1: #29104e;
--wikt-palette-purple-2: #3a196d;
--wikt-palette-purple-3: #4c238a;
--wikt-palette-purple-4: #5c2da3;
--wikt-palette-purple-5: #6e38bc;
--wikt-palette-purple-6: #8541e9;
--wikt-palette-purple-7: #a16cee;
--wikt-palette-purple-8: #b389f1;
--wikt-palette-purple-9: #c9abf5;
--wikt-palette-magenta-0: #20062d;
--wikt-palette-magenta-1: #310b46;
--wikt-palette-magenta-2: #471363;
--wikt-palette-magenta-3: #5c1b7f;
--wikt-palette-magenta-4: #6e2398;
--wikt-palette-magenta-5: #822cb1;
--wikt-palette-magenta-6: #9f30dc;
--wikt-palette-magenta-7: #b663e4;
--wikt-palette-magenta-8: #c581ea;
--wikt-palette-magenta-9: #d6a6f0;
--wikt-palette-rose-0: #240b1d;
--wikt-palette-rose-1: #37122d;
--wikt-palette-rose-2: #4b1c3f;
--wikt-palette-rose-3: #5f2751;
--wikt-palette-rose-4: #723362;
--wikt-palette-rose-5: #844072;
--wikt-palette-rose-6: #a7478e;
--wikt-palette-rose-7: #ba6fa6;
--wikt-palette-rose-8: #c78ab7;
--wikt-palette-rose-9: #d7abcc;
--wikt-palette-brown-0: #1d130b;
--wikt-palette-brown-1: #2d1e12;
--wikt-palette-brown-2: #3c2a1b;
--wikt-palette-brown-3: #4d3826;
--wikt-palette-brown-4: #5d4532;
--wikt-palette-brown-5: #6c533e;
--wikt-palette-brown-6: #876446;
--wikt-palette-brown-7: #9f826a;
--wikt-palette-brown-8: #b19986;
--wikt-palette-brown-9: #c6b5a7;
--wikt-palette-grey-red-0: #230f11;
--wikt-palette-grey-red-1: #34191b;
--wikt-palette-grey-red-2: #462327;
--wikt-palette-grey-red-3: #5a3034;
--wikt-palette-grey-red-4: #6b3c41;
--wikt-palette-grey-red-5: #7c4a4f;
--wikt-palette-grey-red-6: #925c61;
--wikt-palette-grey-red-7: #b1797e;
--wikt-palette-grey-red-8: #c78e94;
--wikt-palette-grey-red-9: #eba6ad;
--wikt-palette-grey-scarlet-0: #20110d;
--wikt-palette-grey-scarlet-1: #311c16;
--wikt-palette-grey-scarlet-2: #422720;
--wikt-palette-grey-scarlet-3: #54342b;
--wikt-palette-grey-scarlet-4: #654137;
--wikt-palette-grey-scarlet-5: #764e43;
--wikt-palette-grey-scarlet-6: #8b6154;
--wikt-palette-grey-scarlet-7: #aa7d70;
--wikt-palette-grey-scarlet-8: #c19386;
--wikt-palette-grey-scarlet-9: #e5ab9a;
--wikt-palette-grey-orange-0: #1c140a;
--wikt-palette-grey-orange-1: #2c1f11;
--wikt-palette-grey-orange-2: #3b2a19;
--wikt-palette-grey-orange-3: #4d3823;
--wikt-palette-grey-orange-4: #5d452c;
--wikt-palette-grey-orange-5: #6e5337;
--wikt-palette-grey-orange-6: #836547;
--wikt-palette-grey-orange-7: #a28260;
--wikt-palette-grey-orange-8: #b99774;
--wikt-palette-grey-orange-9: #ddb082;
--wikt-palette-grey-amber-0: #1a150a;
--wikt-palette-grey-amber-1: #282010;
--wikt-palette-grey-amber-2: #372c17;
--wikt-palette-grey-amber-3: #473b20;
--wikt-palette-grey-amber-4: #57482a;
--wikt-palette-grey-amber-5: #675634;
--wikt-palette-grey-amber-6: #7b6943;
--wikt-palette-grey-amber-7: #99855b;
--wikt-palette-grey-amber-8: #b09b70;
--wikt-palette-grey-amber-9: #d2b579;
--wikt-palette-grey-yellow-0: #17160a;
--wikt-palette-grey-yellow-1: #242211;
--wikt-palette-grey-yellow-2: #322e19;
--wikt-palette-grey-yellow-3: #413d22;
--wikt-palette-grey-yellow-4: #4f4a2c;
--wikt-palette-grey-yellow-5: #5e5837;
--wikt-palette-grey-yellow-6: #716b47;
--wikt-palette-grey-yellow-7: #8f8861;
--wikt-palette-grey-yellow-8: #a59e78;
--wikt-palette-grey-yellow-9: #c2b984;
--wikt-palette-grey-lime-0: #13160b;
--wikt-palette-grey-lime-1: #1f2312;
--wikt-palette-grey-lime-2: #2b3019;
--wikt-palette-grey-lime-3: #383f23;
--wikt-palette-grey-lime-4: #454d2d;
--wikt-palette-grey-lime-5: #535c39;
--wikt-palette-grey-lime-6: #666f49;
--wikt-palette-grey-lime-7: #838c65;
--wikt-palette-grey-lime-8: #98a27c;
--wikt-palette-grey-lime-9: #b1be8a;
--wikt-palette-grey-green-0: #0f170b;
--wikt-palette-grey-green-1: #182512;
--wikt-palette-grey-green-2: #22321a;
--wikt-palette-grey-green-3: #2e4124;
--wikt-palette-grey-green-4: #3a502e;
--wikt-palette-grey-green-5: #465f3a;
--wikt-palette-grey-green-6: #58724a;
--wikt-palette-grey-green-7: #748f66;
--wikt-palette-grey-green-8: #8ba57e;
--wikt-palette-grey-green-9: #a1c290;
--wikt-palette-grey-teal-0: #0b1812;
--wikt-palette-grey-teal-1: #12251d;
--wikt-palette-grey-teal-2: #1a3227;
--wikt-palette-grey-teal-3: #244235;
--wikt-palette-grey-teal-4: #2e5041;
--wikt-palette-grey-teal-5: #3a5f4e;
--wikt-palette-grey-teal-6: #4b7361;
--wikt-palette-grey-teal-7: #66907d;
--wikt-palette-grey-teal-8: #7ea694;
--wikt-palette-grey-teal-9: #91c3ac;
--wikt-palette-grey-cyan-0: #0b171a;
--wikt-palette-grey-cyan-1: #122428;
--wikt-palette-grey-cyan-2: #1a3136;
--wikt-palette-grey-cyan-3: #244145;
--wikt-palette-grey-cyan-4: #2f4f54;
--wikt-palette-grey-cyan-5: #3a5d64;
--wikt-palette-grey-cyan-6: #4b7178;
--wikt-palette-grey-cyan-7: #678e95;
--wikt-palette-grey-cyan-8: #7ea4ab;
--wikt-palette-grey-cyan-9: #94c0c8;
--wikt-palette-grey-blue-0: #0f1523;
--wikt-palette-grey-blue-1: #182134;
--wikt-palette-grey-blue-2: #212d46;
--wikt-palette-grey-blue-3: #2e3c5a;
--wikt-palette-grey-blue-4: #394a6a;
--wikt-palette-grey-blue-5: #47587c;
--wikt-palette-grey-blue-6: #586b92;
--wikt-palette-grey-blue-7: #7488b2;
--wikt-palette-grey-blue-8: #899ec8;
--wikt-palette-grey-blue-9: #9cb8f2;
--wikt-palette-grey-indigo-0: #141327;
--wikt-palette-grey-indigo-1: #201e3a;
--wikt-palette-grey-indigo-2: #2c2a4c;
--wikt-palette-grey-indigo-3: #3a385f;
--wikt-palette-grey-indigo-4: #484571;
--wikt-palette-grey-indigo-5: #565382;
--wikt-palette-grey-indigo-6: #696696;
--wikt-palette-grey-indigo-7: #8682b4;
--wikt-palette-grey-indigo-8: #9c98c8;
--wikt-palette-grey-indigo-9: #b6b2ec;
--wikt-palette-grey-purple-0: #1a1126;
--wikt-palette-grey-purple-1: #271b39;
--wikt-palette-grey-purple-2: #35264b;
--wikt-palette-grey-purple-3: #453460;
--wikt-palette-grey-purple-4: #544071;
--wikt-palette-grey-purple-5: #634e83;
--wikt-palette-grey-purple-6: #776098;
--wikt-palette-grey-purple-7: #957db7;
--wikt-palette-grey-purple-8: #aa93cc;
--wikt-palette-grey-purple-9: #c7acee;
--wikt-palette-grey-magenta-0: #1d0f24;
--wikt-palette-grey-magenta-1: #2c1937;
--wikt-palette-grey-magenta-2: #3c244a;
--wikt-palette-grey-magenta-3: #4d315d;
--wikt-palette-grey-magenta-4: #5d3d6e;
--wikt-palette-grey-magenta-5: #6c4b7f;
--wikt-palette-grey-magenta-6: #815d95;
--wikt-palette-grey-magenta-7: #9f7ab4;
--wikt-palette-grey-magenta-8: #be88dc;
--wikt-palette-grey-magenta-9: #d1aae6;
--wikt-palette-grey-rose-0: #1f101b;
--wikt-palette-grey-rose-1: #2f1a29;
--wikt-palette-grey-rose-2: #3e2538;
--wikt-palette-grey-rose-3: #503348;
--wikt-palette-grey-rose-4: #5f4057;
--wikt-palette-grey-rose-5: #6e4e66;
--wikt-palette-grey-rose-6: #816179;
--wikt-palette-grey-rose-7: #a9799c;
--wikt-palette-grey-rose-8: #b992ae;
--wikt-palette-grey-rose-9: #ccb0c5;
--wikt-palette-grey-brown-0: #1a140f;
--wikt-palette-grey-brown-1: #282019;
--wikt-palette-grey-brown-2: #362c23;
--wikt-palette-grey-brown-3: #463a31;
--wikt-palette-grey-brown-4: #53473d;
--wikt-palette-grey-brown-5: #62564c;
--wikt-palette-grey-brown-6: #746960;
--wikt-palette-grey-brown-7: #958578;
--wikt-palette-grey-brown-8: #a89b90;
--wikt-palette-grey-brown-9: #c0b7af;
--wikt-palette-grey-0: #151515;
--wikt-palette-grey-1: #212121;
--wikt-palette-grey-2: #2d2d2d;
--wikt-palette-grey-3: #3c3c3c;
--wikt-palette-grey-4: #494949;
--wikt-palette-grey-5: #585858;
--wikt-palette-grey-6: #6b6b6b;
--wikt-palette-grey-7: #878787;
--wikt-palette-grey-8: #9d9d9d;
--wikt-palette-grey-9: #b8b8b8;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os {
--wikt-palette-white: #101418;
--wikt-palette-black: #eaecf0;
--wikt-palette-beige: #2f1f10;
--wikt-palette-darkgrey: #b2bbc3;
--wikt-palette-grey: #4e4e4e;
--wikt-palette-lightgrey: #3d3d3d;
--wikt-palette-lightergrey: #242424;
--wikt-palette-cream: #2c2c21;
--wikt-palette-pink: #612042;
--wikt-palette-peach: #58230a;
--wikt-palette-gold: #c9b163;
--wikt-palette-dullgold: #ab995e;
--wikt-palette-yellow: #4a3912;
--wikt-palette-dullyellow: #3a3520;
--wikt-palette-brightyellow: #5e5300;
--wikt-palette-palegreen: #112511;
--wikt-palette-palergreen: #051f0c;
--wikt-palette-mint: #194819;
--wikt-palette-green: #aad7aa;
--wikt-palette-forestgreen: #499149;
--wikt-palette-luminousgreen: #103310;
--wikt-palette-paleblue: #1c2229;
--wikt-palette-cyan: #0a3239;
--wikt-palette-palecyan: #171c21;
--wikt-palette-lightblue: #092f58;
--wikt-palette-dulllightblue: #305070;
--wikt-palette-blue: #aaaaff;
--wikt-palette-dullblue: #b8c0c5;
--wikt-palette-dullcyan: #27292d;
--wikt-palette-deepblue: #a5c3e1;
--wikt-palette-lavender: #1a1a2a;
--wikt-palette-lightyellow: #24241a;
--wikt-palette-red: #d73333;
--wikt-palette-darkred: #ffcccc;
--wikt-palette-deepred: #e36a79;
--wikt-palette-orange: #593e02;
--wikt-palette-lighterblue: #0e1e33;
--wikt-palette-dulllighterblue: #102437;
--wikt-palette-lightred: #421211;
--wikt-palette-indigo: #30306f;
--wikt-palette-lightindigo: #242448;
--wikt-palette-palerpink: #251111;
--wikt-palette-honey: #feefb3;
/* Autogenerated with [[Wiktionary:Palette/numbered]] */
--wikt-palette-red-0: #28070a;
--wikt-palette-red-1: #3e0c11;
--wikt-palette-red-2: #5a131a;
--wikt-palette-red-3: #731b24;
--wikt-palette-red-4: #89242d;
--wikt-palette-red-5: #a12d38;
--wikt-palette-red-6: #bf3b48;
--wikt-palette-red-7: #e95462;
--wikt-palette-red-8: #ee7984;
--wikt-palette-red-9: #f3a2aa;
--wikt-palette-scarlet-0: #250c05;
--wikt-palette-scarlet-1: #3a1409;
--wikt-palette-scarlet-2: #521d0e;
--wikt-palette-scarlet-3: #692814;
--wikt-palette-scarlet-4: #7f311a;
--wikt-palette-scarlet-5: #953c22;
--wikt-palette-scarlet-6: #b24b2c;
--wikt-palette-scarlet-7: #e25e37;
--wikt-palette-scarlet-8: #e88062;
--wikt-palette-scarlet-9: #efa791;
--wikt-palette-orange-0: #211101;
--wikt-palette-orange-1: #331b03;
--wikt-palette-orange-2: #462604;
--wikt-palette-orange-3: #5c3206;
--wikt-palette-orange-4: #703d08;
--wikt-palette-orange-5: #85490a;
--wikt-palette-orange-6: #a0580e;
--wikt-palette-orange-7: #cb7114;
--wikt-palette-orange-8: #de8931;
--wikt-palette-orange-9: #e8ac6e;
--wikt-palette-amber-0: #1c1401;
--wikt-palette-amber-1: #2c1f03;
--wikt-palette-amber-2: #3d2a04;
--wikt-palette-amber-3: #503806;
--wikt-palette-amber-4: #614508;
--wikt-palette-amber-5: #74520a;
--wikt-palette-amber-6: #8c640d;
--wikt-palette-amber-7: #b17f13;
--wikt-palette-amber-8: #cc9218;
--wikt-palette-amber-9: #ddb258;
--wikt-palette-yellow-0: #181504;
--wikt-palette-yellow-1: #262107;
--wikt-palette-yellow-2: #342e0a;
--wikt-palette-yellow-3: #443c0f;
--wikt-palette-yellow-4: #534a13;
--wikt-palette-yellow-5: #625819;
--wikt-palette-yellow-6: #776b21;
--wikt-palette-yellow-7: #97882f;
--wikt-palette-yellow-8: #ae9e3b;
--wikt-palette-yellow-9: #c8b95b;
--wikt-palette-lime-0: #131705;
--wikt-palette-lime-1: #1e2409;
--wikt-palette-lime-2: #28310d;
--wikt-palette-lime-3: #354112;
--wikt-palette-lime-4: #424f18;
--wikt-palette-lime-5: #4f5e1f;
--wikt-palette-lime-6: #607228;
--wikt-palette-lime-7: #7b903a;
--wikt-palette-lime-8: #8fa648;
--wikt-palette-lime-9: #aac25f;
--wikt-palette-green-0: #0c1805;
--wikt-palette-green-1: #122608;
--wikt-palette-green-2: #1a340c;
--wikt-palette-green-3: #234411;
--wikt-palette-green-4: #2c5317;
--wikt-palette-green-5: #35631d;
--wikt-palette-green-6: #427726;
--wikt-palette-green-7: #599737;
--wikt-palette-green-8: #6aae45;
--wikt-palette-green-9: #8dc86d;
--wikt-palette-teal-0: #04180f;
--wikt-palette-teal-1: #082618;
--wikt-palette-teal-2: #0c3422;
--wikt-palette-teal-3: #11452d;
--wikt-palette-teal-4: #165438;
--wikt-palette-teal-5: #1c6443;
--wikt-palette-teal-6: #267953;
--wikt-palette-teal-7: #36986b;
--wikt-palette-teal-8: #43af7e;
--wikt-palette-teal-9: #6fc9a0;
--wikt-palette-cyan-0: #04171b;
--wikt-palette-cyan-1: #07242a;
--wikt-palette-cyan-2: #0a3239;
--wikt-palette-cyan-3: #0f424b;
--wikt-palette-cyan-4: #14515c;
--wikt-palette-cyan-5: #1a606d;
--wikt-palette-cyan-6: #227583;
--wikt-palette-cyan-7: #3194a6;
--wikt-palette-cyan-8: #40aabd;
--wikt-palette-cyan-9: #78c4d1;
--wikt-palette-blue-0: #07132d;
--wikt-palette-blue-1: #0b1f45;
--wikt-palette-blue-2: #122b60;
--wikt-palette-blue-3: #19397b;
--wikt-palette-blue-4: #214692;
--wikt-palette-blue-5: #2954ab;
--wikt-palette-blue-6: #3667c9;
--wikt-palette-blue-7: #4e84f2;
--wikt-palette-blue-8: #709cf5;
--wikt-palette-blue-9: #99b8f8;
--wikt-palette-indigo-0: #100e36;
--wikt-palette-indigo-1: #1b1751;
--wikt-palette-indigo-2: #27226c;
--wikt-palette-indigo-3: #352f88;
--wikt-palette-indigo-4: #423b9e;
--wikt-palette-indigo-5: #5049b4;
--wikt-palette-indigo-6: #5f55e7;
--wikt-palette-indigo-7: #8179ec;
--wikt-palette-indigo-8: #9992f0;
--wikt-palette-indigo-9: #b6b1f4;
--wikt-palette-purple-0: #1a0934;
--wikt-palette-purple-1: #29104e;
--wikt-palette-purple-2: #3a196d;
--wikt-palette-purple-3: #4c238a;
--wikt-palette-purple-4: #5c2da3;
--wikt-palette-purple-5: #6e38bc;
--wikt-palette-purple-6: #8541e9;
--wikt-palette-purple-7: #a16cee;
--wikt-palette-purple-8: #b389f1;
--wikt-palette-purple-9: #c9abf5;
--wikt-palette-magenta-0: #20062d;
--wikt-palette-magenta-1: #310b46;
--wikt-palette-magenta-2: #471363;
--wikt-palette-magenta-3: #5c1b7f;
--wikt-palette-magenta-4: #6e2398;
--wikt-palette-magenta-5: #822cb1;
--wikt-palette-magenta-6: #9f30dc;
--wikt-palette-magenta-7: #b663e4;
--wikt-palette-magenta-8: #c581ea;
--wikt-palette-magenta-9: #d6a6f0;
--wikt-palette-rose-0: #240b1d;
--wikt-palette-rose-1: #37122d;
--wikt-palette-rose-2: #4b1c3f;
--wikt-palette-rose-3: #5f2751;
--wikt-palette-rose-4: #723362;
--wikt-palette-rose-5: #844072;
--wikt-palette-rose-6: #a7478e;
--wikt-palette-rose-7: #ba6fa6;
--wikt-palette-rose-8: #c78ab7;
--wikt-palette-rose-9: #d7abcc;
--wikt-palette-brown-0: #1d130b;
--wikt-palette-brown-1: #2d1e12;
--wikt-palette-brown-2: #3c2a1b;
--wikt-palette-brown-3: #4d3826;
--wikt-palette-brown-4: #5d4532;
--wikt-palette-brown-5: #6c533e;
--wikt-palette-brown-6: #876446;
--wikt-palette-brown-7: #9f826a;
--wikt-palette-brown-8: #b19986;
--wikt-palette-brown-9: #c6b5a7;
--wikt-palette-grey-red-0: #230f11;
--wikt-palette-grey-red-1: #34191b;
--wikt-palette-grey-red-2: #462327;
--wikt-palette-grey-red-3: #5a3034;
--wikt-palette-grey-red-4: #6b3c41;
--wikt-palette-grey-red-5: #7c4a4f;
--wikt-palette-grey-red-6: #925c61;
--wikt-palette-grey-red-7: #b1797e;
--wikt-palette-grey-red-8: #c78e94;
--wikt-palette-grey-red-9: #eba6ad;
--wikt-palette-grey-scarlet-0: #20110d;
--wikt-palette-grey-scarlet-1: #311c16;
--wikt-palette-grey-scarlet-2: #422720;
--wikt-palette-grey-scarlet-3: #54342b;
--wikt-palette-grey-scarlet-4: #654137;
--wikt-palette-grey-scarlet-5: #764e43;
--wikt-palette-grey-scarlet-6: #8b6154;
--wikt-palette-grey-scarlet-7: #aa7d70;
--wikt-palette-grey-scarlet-8: #c19386;
--wikt-palette-grey-scarlet-9: #e5ab9a;
--wikt-palette-grey-orange-0: #1c140a;
--wikt-palette-grey-orange-1: #2c1f11;
--wikt-palette-grey-orange-2: #3b2a19;
--wikt-palette-grey-orange-3: #4d3823;
--wikt-palette-grey-orange-4: #5d452c;
--wikt-palette-grey-orange-5: #6e5337;
--wikt-palette-grey-orange-6: #836547;
--wikt-palette-grey-orange-7: #a28260;
--wikt-palette-grey-orange-8: #b99774;
--wikt-palette-grey-orange-9: #ddb082;
--wikt-palette-grey-amber-0: #1a150a;
--wikt-palette-grey-amber-1: #282010;
--wikt-palette-grey-amber-2: #372c17;
--wikt-palette-grey-amber-3: #473b20;
--wikt-palette-grey-amber-4: #57482a;
--wikt-palette-grey-amber-5: #675634;
--wikt-palette-grey-amber-6: #7b6943;
--wikt-palette-grey-amber-7: #99855b;
--wikt-palette-grey-amber-8: #b09b70;
--wikt-palette-grey-amber-9: #d2b579;
--wikt-palette-grey-yellow-0: #17160a;
--wikt-palette-grey-yellow-1: #242211;
--wikt-palette-grey-yellow-2: #322e19;
--wikt-palette-grey-yellow-3: #413d22;
--wikt-palette-grey-yellow-4: #4f4a2c;
--wikt-palette-grey-yellow-5: #5e5837;
--wikt-palette-grey-yellow-6: #716b47;
--wikt-palette-grey-yellow-7: #8f8861;
--wikt-palette-grey-yellow-8: #a59e78;
--wikt-palette-grey-yellow-9: #c2b984;
--wikt-palette-grey-lime-0: #13160b;
--wikt-palette-grey-lime-1: #1f2312;
--wikt-palette-grey-lime-2: #2b3019;
--wikt-palette-grey-lime-3: #383f23;
--wikt-palette-grey-lime-4: #454d2d;
--wikt-palette-grey-lime-5: #535c39;
--wikt-palette-grey-lime-6: #666f49;
--wikt-palette-grey-lime-7: #838c65;
--wikt-palette-grey-lime-8: #98a27c;
--wikt-palette-grey-lime-9: #b1be8a;
--wikt-palette-grey-green-0: #0f170b;
--wikt-palette-grey-green-1: #182512;
--wikt-palette-grey-green-2: #22321a;
--wikt-palette-grey-green-3: #2e4124;
--wikt-palette-grey-green-4: #3a502e;
--wikt-palette-grey-green-5: #465f3a;
--wikt-palette-grey-green-6: #58724a;
--wikt-palette-grey-green-7: #748f66;
--wikt-palette-grey-green-8: #8ba57e;
--wikt-palette-grey-green-9: #a1c290;
--wikt-palette-grey-teal-0: #0b1812;
--wikt-palette-grey-teal-1: #12251d;
--wikt-palette-grey-teal-2: #1a3227;
--wikt-palette-grey-teal-3: #244235;
--wikt-palette-grey-teal-4: #2e5041;
--wikt-palette-grey-teal-5: #3a5f4e;
--wikt-palette-grey-teal-6: #4b7361;
--wikt-palette-grey-teal-7: #66907d;
--wikt-palette-grey-teal-8: #7ea694;
--wikt-palette-grey-teal-9: #91c3ac;
--wikt-palette-grey-cyan-0: #0b171a;
--wikt-palette-grey-cyan-1: #122428;
--wikt-palette-grey-cyan-2: #1a3136;
--wikt-palette-grey-cyan-3: #244145;
--wikt-palette-grey-cyan-4: #2f4f54;
--wikt-palette-grey-cyan-5: #3a5d64;
--wikt-palette-grey-cyan-6: #4b7178;
--wikt-palette-grey-cyan-7: #678e95;
--wikt-palette-grey-cyan-8: #7ea4ab;
--wikt-palette-grey-cyan-9: #94c0c8;
--wikt-palette-grey-blue-0: #0f1523;
--wikt-palette-grey-blue-1: #182134;
--wikt-palette-grey-blue-2: #212d46;
--wikt-palette-grey-blue-3: #2e3c5a;
--wikt-palette-grey-blue-4: #394a6a;
--wikt-palette-grey-blue-5: #47587c;
--wikt-palette-grey-blue-6: #586b92;
--wikt-palette-grey-blue-7: #7488b2;
--wikt-palette-grey-blue-8: #899ec8;
--wikt-palette-grey-blue-9: #9cb8f2;
--wikt-palette-grey-indigo-0: #141327;
--wikt-palette-grey-indigo-1: #201e3a;
--wikt-palette-grey-indigo-2: #2c2a4c;
--wikt-palette-grey-indigo-3: #3a385f;
--wikt-palette-grey-indigo-4: #484571;
--wikt-palette-grey-indigo-5: #565382;
--wikt-palette-grey-indigo-6: #696696;
--wikt-palette-grey-indigo-7: #8682b4;
--wikt-palette-grey-indigo-8: #9c98c8;
--wikt-palette-grey-indigo-9: #b6b2ec;
--wikt-palette-grey-purple-0: #1a1126;
--wikt-palette-grey-purple-1: #271b39;
--wikt-palette-grey-purple-2: #35264b;
--wikt-palette-grey-purple-3: #453460;
--wikt-palette-grey-purple-4: #544071;
--wikt-palette-grey-purple-5: #634e83;
--wikt-palette-grey-purple-6: #776098;
--wikt-palette-grey-purple-7: #957db7;
--wikt-palette-grey-purple-8: #aa93cc;
--wikt-palette-grey-purple-9: #c7acee;
--wikt-palette-grey-magenta-0: #1d0f24;
--wikt-palette-grey-magenta-1: #2c1937;
--wikt-palette-grey-magenta-2: #3c244a;
--wikt-palette-grey-magenta-3: #4d315d;
--wikt-palette-grey-magenta-4: #5d3d6e;
--wikt-palette-grey-magenta-5: #6c4b7f;
--wikt-palette-grey-magenta-6: #815d95;
--wikt-palette-grey-magenta-7: #9f7ab4;
--wikt-palette-grey-magenta-8: #be88dc;
--wikt-palette-grey-magenta-9: #d1aae6;
--wikt-palette-grey-rose-0: #1f101b;
--wikt-palette-grey-rose-1: #2f1a29;
--wikt-palette-grey-rose-2: #3e2538;
--wikt-palette-grey-rose-3: #503348;
--wikt-palette-grey-rose-4: #5f4057;
--wikt-palette-grey-rose-5: #6e4e66;
--wikt-palette-grey-rose-6: #816179;
--wikt-palette-grey-rose-7: #a9799c;
--wikt-palette-grey-rose-8: #b992ae;
--wikt-palette-grey-rose-9: #ccb0c5;
--wikt-palette-grey-brown-0: #1a140f;
--wikt-palette-grey-brown-1: #282019;
--wikt-palette-grey-brown-2: #362c23;
--wikt-palette-grey-brown-3: #463a31;
--wikt-palette-grey-brown-4: #53473d;
--wikt-palette-grey-brown-5: #62564c;
--wikt-palette-grey-brown-6: #746960;
--wikt-palette-grey-brown-7: #958578;
--wikt-palette-grey-brown-8: #a89b90;
--wikt-palette-grey-brown-9: #c0b7af;
--wikt-palette-grey-0: #151515;
--wikt-palette-grey-1: #212121;
--wikt-palette-grey-2: #2d2d2d;
--wikt-palette-grey-3: #3c3c3c;
--wikt-palette-grey-4: #494949;
--wikt-palette-grey-5: #585858;
--wikt-palette-grey-6: #6b6b6b;
--wikt-palette-grey-7: #878787;
--wikt-palette-grey-8: #9d9d9d;
--wikt-palette-grey-9: #b8b8b8;
}
}
/* Shorthand available with data-wikt-palette-p and scoping */
[data-wikt-palette-p="red"] {
--wikt-p-0: var(--wikt-palette-red-0);
--wikt-p-1: var(--wikt-palette-red-1);
--wikt-p-2: var(--wikt-palette-red-2);
--wikt-p-3: var(--wikt-palette-red-3);
--wikt-p-4: var(--wikt-palette-red-4);
--wikt-p-5: var(--wikt-palette-red-5);
--wikt-p-6: var(--wikt-palette-red-6);
--wikt-p-7: var(--wikt-palette-red-7);
--wikt-p-8: var(--wikt-palette-red-8);
--wikt-p-9: var(--wikt-palette-red-9);
--wikt-p-g-0: var(--wikt-palette-grey-red-0);
--wikt-p-g-1: var(--wikt-palette-grey-red-1);
--wikt-p-g-2: var(--wikt-palette-grey-red-2);
--wikt-p-g-3: var(--wikt-palette-grey-red-3);
--wikt-p-g-4: var(--wikt-palette-grey-red-4);
--wikt-p-g-5: var(--wikt-palette-grey-red-5);
--wikt-p-g-6: var(--wikt-palette-grey-red-6);
--wikt-p-g-7: var(--wikt-palette-grey-red-7);
--wikt-p-g-8: var(--wikt-palette-grey-red-8);
--wikt-p-g-9: var(--wikt-palette-grey-red-9);
}
[data-wikt-palette-p="scarlet"] {
--wikt-p-0: var(--wikt-palette-scarlet-0);
--wikt-p-1: var(--wikt-palette-scarlet-1);
--wikt-p-2: var(--wikt-palette-scarlet-2);
--wikt-p-3: var(--wikt-palette-scarlet-3);
--wikt-p-4: var(--wikt-palette-scarlet-4);
--wikt-p-5: var(--wikt-palette-scarlet-5);
--wikt-p-6: var(--wikt-palette-scarlet-6);
--wikt-p-7: var(--wikt-palette-scarlet-7);
--wikt-p-8: var(--wikt-palette-scarlet-8);
--wikt-p-9: var(--wikt-palette-scarlet-9);
--wikt-p-g-0: var(--wikt-palette-grey-scarlet-0);
--wikt-p-g-1: var(--wikt-palette-grey-scarlet-1);
--wikt-p-g-2: var(--wikt-palette-grey-scarlet-2);
--wikt-p-g-3: var(--wikt-palette-grey-scarlet-3);
--wikt-p-g-4: var(--wikt-palette-grey-scarlet-4);
--wikt-p-g-5: var(--wikt-palette-grey-scarlet-5);
--wikt-p-g-6: var(--wikt-palette-grey-scarlet-6);
--wikt-p-g-7: var(--wikt-palette-grey-scarlet-7);
--wikt-p-g-8: var(--wikt-palette-grey-scarlet-8);
--wikt-p-g-9: var(--wikt-palette-grey-scarlet-9);
}
[data-wikt-palette-p="orange"] {
--wikt-p-0: var(--wikt-palette-orange-0);
--wikt-p-1: var(--wikt-palette-orange-1);
--wikt-p-2: var(--wikt-palette-orange-2);
--wikt-p-3: var(--wikt-palette-orange-3);
--wikt-p-4: var(--wikt-palette-orange-4);
--wikt-p-5: var(--wikt-palette-orange-5);
--wikt-p-6: var(--wikt-palette-orange-6);
--wikt-p-7: var(--wikt-palette-orange-7);
--wikt-p-8: var(--wikt-palette-orange-8);
--wikt-p-9: var(--wikt-palette-orange-9);
--wikt-p-g-0: var(--wikt-palette-grey-orange-0);
--wikt-p-g-1: var(--wikt-palette-grey-orange-1);
--wikt-p-g-2: var(--wikt-palette-grey-orange-2);
--wikt-p-g-3: var(--wikt-palette-grey-orange-3);
--wikt-p-g-4: var(--wikt-palette-grey-orange-4);
--wikt-p-g-5: var(--wikt-palette-grey-orange-5);
--wikt-p-g-6: var(--wikt-palette-grey-orange-6);
--wikt-p-g-7: var(--wikt-palette-grey-orange-7);
--wikt-p-g-8: var(--wikt-palette-grey-orange-8);
--wikt-p-g-9: var(--wikt-palette-grey-orange-9);
}
[data-wikt-palette-p="amber"] {
--wikt-p-0: var(--wikt-palette-amber-0);
--wikt-p-1: var(--wikt-palette-amber-1);
--wikt-p-2: var(--wikt-palette-amber-2);
--wikt-p-3: var(--wikt-palette-amber-3);
--wikt-p-4: var(--wikt-palette-amber-4);
--wikt-p-5: var(--wikt-palette-amber-5);
--wikt-p-6: var(--wikt-palette-amber-6);
--wikt-p-7: var(--wikt-palette-amber-7);
--wikt-p-8: var(--wikt-palette-amber-8);
--wikt-p-9: var(--wikt-palette-amber-9);
--wikt-p-g-0: var(--wikt-palette-grey-amber-0);
--wikt-p-g-1: var(--wikt-palette-grey-amber-1);
--wikt-p-g-2: var(--wikt-palette-grey-amber-2);
--wikt-p-g-3: var(--wikt-palette-grey-amber-3);
--wikt-p-g-4: var(--wikt-palette-grey-amber-4);
--wikt-p-g-5: var(--wikt-palette-grey-amber-5);
--wikt-p-g-6: var(--wikt-palette-grey-amber-6);
--wikt-p-g-7: var(--wikt-palette-grey-amber-7);
--wikt-p-g-8: var(--wikt-palette-grey-amber-8);
--wikt-p-g-9: var(--wikt-palette-grey-amber-9);
}
[data-wikt-palette-p="yellow"] {
--wikt-p-0: var(--wikt-palette-yellow-0);
--wikt-p-1: var(--wikt-palette-yellow-1);
--wikt-p-2: var(--wikt-palette-yellow-2);
--wikt-p-3: var(--wikt-palette-yellow-3);
--wikt-p-4: var(--wikt-palette-yellow-4);
--wikt-p-5: var(--wikt-palette-yellow-5);
--wikt-p-6: var(--wikt-palette-yellow-6);
--wikt-p-7: var(--wikt-palette-yellow-7);
--wikt-p-8: var(--wikt-palette-yellow-8);
--wikt-p-9: var(--wikt-palette-yellow-9);
--wikt-p-g-0: var(--wikt-palette-grey-yellow-0);
--wikt-p-g-1: var(--wikt-palette-grey-yellow-1);
--wikt-p-g-2: var(--wikt-palette-grey-yellow-2);
--wikt-p-g-3: var(--wikt-palette-grey-yellow-3);
--wikt-p-g-4: var(--wikt-palette-grey-yellow-4);
--wikt-p-g-5: var(--wikt-palette-grey-yellow-5);
--wikt-p-g-6: var(--wikt-palette-grey-yellow-6);
--wikt-p-g-7: var(--wikt-palette-grey-yellow-7);
--wikt-p-g-8: var(--wikt-palette-grey-yellow-8);
--wikt-p-g-9: var(--wikt-palette-grey-yellow-9);
}
[data-wikt-palette-p="lime"] {
--wikt-p-0: var(--wikt-palette-lime-0);
--wikt-p-1: var(--wikt-palette-lime-1);
--wikt-p-2: var(--wikt-palette-lime-2);
--wikt-p-3: var(--wikt-palette-lime-3);
--wikt-p-4: var(--wikt-palette-lime-4);
--wikt-p-5: var(--wikt-palette-lime-5);
--wikt-p-6: var(--wikt-palette-lime-6);
--wikt-p-7: var(--wikt-palette-lime-7);
--wikt-p-8: var(--wikt-palette-lime-8);
--wikt-p-9: var(--wikt-palette-lime-9);
--wikt-p-g-0: var(--wikt-palette-grey-lime-0);
--wikt-p-g-1: var(--wikt-palette-grey-lime-1);
--wikt-p-g-2: var(--wikt-palette-grey-lime-2);
--wikt-p-g-3: var(--wikt-palette-grey-lime-3);
--wikt-p-g-4: var(--wikt-palette-grey-lime-4);
--wikt-p-g-5: var(--wikt-palette-grey-lime-5);
--wikt-p-g-6: var(--wikt-palette-grey-lime-6);
--wikt-p-g-7: var(--wikt-palette-grey-lime-7);
--wikt-p-g-8: var(--wikt-palette-grey-lime-8);
--wikt-p-g-9: var(--wikt-palette-grey-lime-9);
}
[data-wikt-palette-p="green"] {
--wikt-p-0: var(--wikt-palette-green-0);
--wikt-p-1: var(--wikt-palette-green-1);
--wikt-p-2: var(--wikt-palette-green-2);
--wikt-p-3: var(--wikt-palette-green-3);
--wikt-p-4: var(--wikt-palette-green-4);
--wikt-p-5: var(--wikt-palette-green-5);
--wikt-p-6: var(--wikt-palette-green-6);
--wikt-p-7: var(--wikt-palette-green-7);
--wikt-p-8: var(--wikt-palette-green-8);
--wikt-p-9: var(--wikt-palette-green-9);
--wikt-p-g-0: var(--wikt-palette-grey-green-0);
--wikt-p-g-1: var(--wikt-palette-grey-green-1);
--wikt-p-g-2: var(--wikt-palette-grey-green-2);
--wikt-p-g-3: var(--wikt-palette-grey-green-3);
--wikt-p-g-4: var(--wikt-palette-grey-green-4);
--wikt-p-g-5: var(--wikt-palette-grey-green-5);
--wikt-p-g-6: var(--wikt-palette-grey-green-6);
--wikt-p-g-7: var(--wikt-palette-grey-green-7);
--wikt-p-g-8: var(--wikt-palette-grey-green-8);
--wikt-p-g-9: var(--wikt-palette-grey-green-9);
}
[data-wikt-palette-p="teal"] {
--wikt-p-0: var(--wikt-palette-teal-0);
--wikt-p-1: var(--wikt-palette-teal-1);
--wikt-p-2: var(--wikt-palette-teal-2);
--wikt-p-3: var(--wikt-palette-teal-3);
--wikt-p-4: var(--wikt-palette-teal-4);
--wikt-p-5: var(--wikt-palette-teal-5);
--wikt-p-6: var(--wikt-palette-teal-6);
--wikt-p-7: var(--wikt-palette-teal-7);
--wikt-p-8: var(--wikt-palette-teal-8);
--wikt-p-9: var(--wikt-palette-teal-9);
--wikt-p-g-0: var(--wikt-palette-grey-teal-0);
--wikt-p-g-1: var(--wikt-palette-grey-teal-1);
--wikt-p-g-2: var(--wikt-palette-grey-teal-2);
--wikt-p-g-3: var(--wikt-palette-grey-teal-3);
--wikt-p-g-4: var(--wikt-palette-grey-teal-4);
--wikt-p-g-5: var(--wikt-palette-grey-teal-5);
--wikt-p-g-6: var(--wikt-palette-grey-teal-6);
--wikt-p-g-7: var(--wikt-palette-grey-teal-7);
--wikt-p-g-8: var(--wikt-palette-grey-teal-8);
--wikt-p-g-9: var(--wikt-palette-grey-teal-9);
}
[data-wikt-palette-p="cyan"] {
--wikt-p-0: var(--wikt-palette-cyan-0);
--wikt-p-1: var(--wikt-palette-cyan-1);
--wikt-p-2: var(--wikt-palette-cyan-2);
--wikt-p-3: var(--wikt-palette-cyan-3);
--wikt-p-4: var(--wikt-palette-cyan-4);
--wikt-p-5: var(--wikt-palette-cyan-5);
--wikt-p-6: var(--wikt-palette-cyan-6);
--wikt-p-7: var(--wikt-palette-cyan-7);
--wikt-p-8: var(--wikt-palette-cyan-8);
--wikt-p-9: var(--wikt-palette-cyan-9);
--wikt-p-g-0: var(--wikt-palette-grey-cyan-0);
--wikt-p-g-1: var(--wikt-palette-grey-cyan-1);
--wikt-p-g-2: var(--wikt-palette-grey-cyan-2);
--wikt-p-g-3: var(--wikt-palette-grey-cyan-3);
--wikt-p-g-4: var(--wikt-palette-grey-cyan-4);
--wikt-p-g-5: var(--wikt-palette-grey-cyan-5);
--wikt-p-g-6: var(--wikt-palette-grey-cyan-6);
--wikt-p-g-7: var(--wikt-palette-grey-cyan-7);
--wikt-p-g-8: var(--wikt-palette-grey-cyan-8);
--wikt-p-g-9: var(--wikt-palette-grey-cyan-9);
}
[data-wikt-palette-p="blue"] {
--wikt-p-0: var(--wikt-palette-blue-0);
--wikt-p-1: var(--wikt-palette-blue-1);
--wikt-p-2: var(--wikt-palette-blue-2);
--wikt-p-3: var(--wikt-palette-blue-3);
--wikt-p-4: var(--wikt-palette-blue-4);
--wikt-p-5: var(--wikt-palette-blue-5);
--wikt-p-6: var(--wikt-palette-blue-6);
--wikt-p-7: var(--wikt-palette-blue-7);
--wikt-p-8: var(--wikt-palette-blue-8);
--wikt-p-9: var(--wikt-palette-blue-9);
--wikt-p-g-0: var(--wikt-palette-grey-blue-0);
--wikt-p-g-1: var(--wikt-palette-grey-blue-1);
--wikt-p-g-2: var(--wikt-palette-grey-blue-2);
--wikt-p-g-3: var(--wikt-palette-grey-blue-3);
--wikt-p-g-4: var(--wikt-palette-grey-blue-4);
--wikt-p-g-5: var(--wikt-palette-grey-blue-5);
--wikt-p-g-6: var(--wikt-palette-grey-blue-6);
--wikt-p-g-7: var(--wikt-palette-grey-blue-7);
--wikt-p-g-8: var(--wikt-palette-grey-blue-8);
--wikt-p-g-9: var(--wikt-palette-grey-blue-9);
}
[data-wikt-palette-p="indigo"] {
--wikt-p-0: var(--wikt-palette-indigo-0);
--wikt-p-1: var(--wikt-palette-indigo-1);
--wikt-p-2: var(--wikt-palette-indigo-2);
--wikt-p-3: var(--wikt-palette-indigo-3);
--wikt-p-4: var(--wikt-palette-indigo-4);
--wikt-p-5: var(--wikt-palette-indigo-5);
--wikt-p-6: var(--wikt-palette-indigo-6);
--wikt-p-7: var(--wikt-palette-indigo-7);
--wikt-p-8: var(--wikt-palette-indigo-8);
--wikt-p-9: var(--wikt-palette-indigo-9);
--wikt-p-g-0: var(--wikt-palette-grey-indigo-0);
--wikt-p-g-1: var(--wikt-palette-grey-indigo-1);
--wikt-p-g-2: var(--wikt-palette-grey-indigo-2);
--wikt-p-g-3: var(--wikt-palette-grey-indigo-3);
--wikt-p-g-4: var(--wikt-palette-grey-indigo-4);
--wikt-p-g-5: var(--wikt-palette-grey-indigo-5);
--wikt-p-g-6: var(--wikt-palette-grey-indigo-6);
--wikt-p-g-7: var(--wikt-palette-grey-indigo-7);
--wikt-p-g-8: var(--wikt-palette-grey-indigo-8);
--wikt-p-g-9: var(--wikt-palette-grey-indigo-9);
}
[data-wikt-palette-p="purple"] {
--wikt-p-0: var(--wikt-palette-purple-0);
--wikt-p-1: var(--wikt-palette-purple-1);
--wikt-p-2: var(--wikt-palette-purple-2);
--wikt-p-3: var(--wikt-palette-purple-3);
--wikt-p-4: var(--wikt-palette-purple-4);
--wikt-p-5: var(--wikt-palette-purple-5);
--wikt-p-6: var(--wikt-palette-purple-6);
--wikt-p-7: var(--wikt-palette-purple-7);
--wikt-p-8: var(--wikt-palette-purple-8);
--wikt-p-9: var(--wikt-palette-purple-9);
--wikt-p-g-0: var(--wikt-palette-grey-purple-0);
--wikt-p-g-1: var(--wikt-palette-grey-purple-1);
--wikt-p-g-2: var(--wikt-palette-grey-purple-2);
--wikt-p-g-3: var(--wikt-palette-grey-purple-3);
--wikt-p-g-4: var(--wikt-palette-grey-purple-4);
--wikt-p-g-5: var(--wikt-palette-grey-purple-5);
--wikt-p-g-6: var(--wikt-palette-grey-purple-6);
--wikt-p-g-7: var(--wikt-palette-grey-purple-7);
--wikt-p-g-8: var(--wikt-palette-grey-purple-8);
--wikt-p-g-9: var(--wikt-palette-grey-purple-9);
}
[data-wikt-palette-p="magenta"] {
--wikt-p-0: var(--wikt-palette-magenta-0);
--wikt-p-1: var(--wikt-palette-magenta-1);
--wikt-p-2: var(--wikt-palette-magenta-2);
--wikt-p-3: var(--wikt-palette-magenta-3);
--wikt-p-4: var(--wikt-palette-magenta-4);
--wikt-p-5: var(--wikt-palette-magenta-5);
--wikt-p-6: var(--wikt-palette-magenta-6);
--wikt-p-7: var(--wikt-palette-magenta-7);
--wikt-p-8: var(--wikt-palette-magenta-8);
--wikt-p-9: var(--wikt-palette-magenta-9);
--wikt-p-g-0: var(--wikt-palette-grey-magenta-0);
--wikt-p-g-1: var(--wikt-palette-grey-magenta-1);
--wikt-p-g-2: var(--wikt-palette-grey-magenta-2);
--wikt-p-g-3: var(--wikt-palette-grey-magenta-3);
--wikt-p-g-4: var(--wikt-palette-grey-magenta-4);
--wikt-p-g-5: var(--wikt-palette-grey-magenta-5);
--wikt-p-g-6: var(--wikt-palette-grey-magenta-6);
--wikt-p-g-7: var(--wikt-palette-grey-magenta-7);
--wikt-p-g-8: var(--wikt-palette-grey-magenta-8);
--wikt-p-g-9: var(--wikt-palette-grey-magenta-9);
}
[data-wikt-palette-p="rose"] {
--wikt-p-0: var(--wikt-palette-rose-0);
--wikt-p-1: var(--wikt-palette-rose-1);
--wikt-p-2: var(--wikt-palette-rose-2);
--wikt-p-3: var(--wikt-palette-rose-3);
--wikt-p-4: var(--wikt-palette-rose-4);
--wikt-p-5: var(--wikt-palette-rose-5);
--wikt-p-6: var(--wikt-palette-rose-6);
--wikt-p-7: var(--wikt-palette-rose-7);
--wikt-p-8: var(--wikt-palette-rose-8);
--wikt-p-9: var(--wikt-palette-rose-9);
--wikt-p-g-0: var(--wikt-palette-grey-rose-0);
--wikt-p-g-1: var(--wikt-palette-grey-rose-1);
--wikt-p-g-2: var(--wikt-palette-grey-rose-2);
--wikt-p-g-3: var(--wikt-palette-grey-rose-3);
--wikt-p-g-4: var(--wikt-palette-grey-rose-4);
--wikt-p-g-5: var(--wikt-palette-grey-rose-5);
--wikt-p-g-6: var(--wikt-palette-grey-rose-6);
--wikt-p-g-7: var(--wikt-palette-grey-rose-7);
--wikt-p-g-8: var(--wikt-palette-grey-rose-8);
--wikt-p-g-9: var(--wikt-palette-grey-rose-9);
}
[data-wikt-palette-p="brown"] {
--wikt-p-0: var(--wikt-palette-brown-0);
--wikt-p-1: var(--wikt-palette-brown-1);
--wikt-p-2: var(--wikt-palette-brown-2);
--wikt-p-3: var(--wikt-palette-brown-3);
--wikt-p-4: var(--wikt-palette-brown-4);
--wikt-p-5: var(--wikt-palette-brown-5);
--wikt-p-6: var(--wikt-palette-brown-6);
--wikt-p-7: var(--wikt-palette-brown-7);
--wikt-p-8: var(--wikt-palette-brown-8);
--wikt-p-9: var(--wikt-palette-brown-9);
--wikt-p-g-0: var(--wikt-palette-grey-brown-0);
--wikt-p-g-1: var(--wikt-palette-grey-brown-1);
--wikt-p-g-2: var(--wikt-palette-grey-brown-2);
--wikt-p-g-3: var(--wikt-palette-grey-brown-3);
--wikt-p-g-4: var(--wikt-palette-grey-brown-4);
--wikt-p-g-5: var(--wikt-palette-grey-brown-5);
--wikt-p-g-6: var(--wikt-palette-grey-brown-6);
--wikt-p-g-7: var(--wikt-palette-grey-brown-7);
--wikt-p-g-8: var(--wikt-palette-grey-brown-8);
--wikt-p-g-9: var(--wikt-palette-grey-brown-9);
}
[data-wikt-palette-p="grey"] {
--wikt-p-0: var(--wikt-palette-grey-0);
--wikt-p-1: var(--wikt-palette-grey-1);
--wikt-p-2: var(--wikt-palette-grey-2);
--wikt-p-3: var(--wikt-palette-grey-3);
--wikt-p-4: var(--wikt-palette-grey-4);
--wikt-p-5: var(--wikt-palette-grey-5);
--wikt-p-6: var(--wikt-palette-grey-6);
--wikt-p-7: var(--wikt-palette-grey-7);
--wikt-p-8: var(--wikt-palette-grey-8);
--wikt-p-9: var(--wikt-palette-grey-9);
--wikt-p-g-0: var(--wikt-palette-grey-0);
--wikt-p-g-1: var(--wikt-palette-grey-1);
--wikt-p-g-2: var(--wikt-palette-grey-2);
--wikt-p-g-3: var(--wikt-palette-grey-3);
--wikt-p-g-4: var(--wikt-palette-grey-4);
--wikt-p-g-5: var(--wikt-palette-grey-5);
--wikt-p-g-6: var(--wikt-palette-grey-6);
--wikt-p-g-7: var(--wikt-palette-grey-7);
--wikt-p-g-8: var(--wikt-palette-grey-8);
--wikt-p-g-9: var(--wikt-palette-grey-9);
}
t46x6el1qmhny2nrq55ifhccynp1qdz
88907
88906
2026-08-06T23:11:29Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-Palette.css]]
88906
css
text/css
/* Wiktionary's palette of colors that work equally well in light and dark mode. */
/* Inspired by [[:ru:w:MediaWiki:Gadget-common-site.css]] */
:root, .skin-invert, .notheme {
--wikt-palette-white: #ffffff; /* Same as <code>--background-color-base</code> */
--wikt-palette-black: #202122; /* Body text; same as <code>--color-base</code> */
--wikt-palette-beige: #fffbf2;
--wikt-palette-darkgrey: #54595d;
--wikt-palette-grey: #9e9e9e;
--wikt-palette-lightgrey: #cccccc;
--wikt-palette-lightergrey: #eeeeee;
--wikt-palette-cream: #f8f8f0;
--wikt-palette-pink: #ffe0f0;
--wikt-palette-peach: #e4d4c0;
--wikt-palette-gold: #b88d00;
--wikt-palette-dullgold: #826f34;
--wikt-palette-yellow: #ebe8b9;
--wikt-palette-dullyellow: #dedbc1;
--wikt-palette-brightyellow: #ffee77;
--wikt-palette-palegreen: #e2f6e2;
--wikt-palette-palergreen: #f8fffa;
--wikt-palette-mint: #c0e4c0;
--wikt-palette-green: #33d733;
--wikt-palette-forestgreen: #235923;
--wikt-palette-luminousgreen: #dafdda;
--wikt-palette-paleblue: #f8f9fa;
--wikt-palette-cyan: #eefbf9;
--wikt-palette-palecyan: #f6f7f7;
--wikt-palette-lightblue: #d9ebff;
--wikt-palette-dulllightblue: #aabbdd;
--wikt-palette-blue: #3333ff;
--wikt-palette-dullblue: #49555f;
--wikt-palette-dullcyan: #eaecf0;
--wikt-palette-deepblue: #2f445c;
--wikt-palette-lavender: #f8f8ff;
--wikt-palette-lightyellow: #ffffe0;
--wikt-palette-red: #d73333; /* Same as <code>--background-color-destructive</code> */
--wikt-palette-darkred: #660000;
--wikt-palette-deepred: #bf0218;
--wikt-palette-orange: #f2caa4;
--wikt-palette-lighterblue: #ebf4ff;
--wikt-palette-dulllighterblue: #e2eaf9;
--wikt-palette-lightred: #fee7e6;
--wikt-palette-indigo: #aaaaee;
--wikt-palette-lightindigo: #e9e9ff;
--wikt-palette-palerpink: #fff3f5;
--wikt-palette-honey: #81540e;
/* Autogenerated with [[Wiktionary:Palette/numbered]] */
--wikt-palette-red-0: #fef9f9;
--wikt-palette-red-1: #fdeef0;
--wikt-palette-red-2: #fce4e6;
--wikt-palette-red-3: #fad9dc;
--wikt-palette-red-4: #f7c2c6;
--wikt-palette-red-5: #f5afb5;
--wikt-palette-red-6: #f29ba3;
--wikt-palette-red-7: #f0858e;
--wikt-palette-red-8: #ed6d78;
--wikt-palette-red-9: #ce4350;
--wikt-palette-scarlet-0: #fef9f7;
--wikt-palette-scarlet-1: #fcefeb;
--wikt-palette-scarlet-2: #fae5de;
--wikt-palette-scarlet-3: #f8dad1;
--wikt-palette-scarlet-4: #f4c4b5;
--wikt-palette-scarlet-5: #f1b3a0;
--wikt-palette-scarlet-6: #eea088;
--wikt-palette-scarlet-7: #ea8c70;
--wikt-palette-scarlet-8: #e67553;
--wikt-palette-scarlet-9: #c25433;
--wikt-palette-orange-0: #fdfaf5;
--wikt-palette-orange-1: #fbf0e4;
--wikt-palette-orange-2: #f8e7d4;
--wikt-palette-orange-3: #f5ddc3;
--wikt-palette-orange-4: #f0c89e;
--wikt-palette-orange-5: #ebb882;
--wikt-palette-orange-6: #e6a662;
--wikt-palette-orange-7: #e19341;
--wikt-palette-orange-8: #db7f1e;
--wikt-palette-orange-9: #b16210;
--wikt-palette-amber-0: #fdfaf3;
--wikt-palette-amber-1: #f9f1df;
--wikt-palette-amber-2: #f5e8cc;
--wikt-palette-amber-3: #f1dfb8;
--wikt-palette-amber-4: #e8cc8f;
--wikt-palette-amber-5: #e2bc6d;
--wikt-palette-amber-6: #dbac4a;
--wikt-palette-amber-7: #d39b24;
--wikt-palette-amber-8: #c38c17;
--wikt-palette-amber-9: #9b6f10;
--wikt-palette-yellow-0: #fbfbf4;
--wikt-palette-yellow-1: #f5f2e0;
--wikt-palette-yellow-2: #eeeacc;
--wikt-palette-yellow-3: #e8e2b9;
--wikt-palette-yellow-4: #dbd191;
--wikt-palette-yellow-5: #d0c370;
--wikt-palette-yellow-6: #c5b44f;
--wikt-palette-yellow-7: #b6a63f;
--wikt-palette-yellow-8: #a69737;
--wikt-palette-yellow-9: #847827;
--wikt-palette-lime-0: #f9fbf3;
--wikt-palette-lime-1: #eff4e1;
--wikt-palette-lime-2: #e5edce;
--wikt-palette-lime-3: #dbe5bc;
--wikt-palette-lime-4: #c5d693;
--wikt-palette-lime-5: #b5ca73;
--wikt-palette-lime-6: #a3be52;
--wikt-palette-lime-7: #97af4e;
--wikt-palette-lime-8: #889f43;
--wikt-palette-lime-9: #6b7f2f;
--wikt-palette-green-0: #f8fbf5;
--wikt-palette-green-1: #eaf5e4;
--wikt-palette-green-2: #ddefd3;
--wikt-palette-green-3: #cee8c1;
--wikt-palette-green-4: #b3da9d;
--wikt-palette-green-5: #9cd080;
--wikt-palette-green-6: #83c460;
--wikt-palette-green-7: #69b840;
--wikt-palette-green-8: #64a640;
--wikt-palette-green-9: #4b852d;
--wikt-palette-teal-0: #f6fcf9;
--wikt-palette-teal-1: #e5f6ee;
--wikt-palette-teal-2: #d5efe3;
--wikt-palette-teal-3: #c3e9d7;
--wikt-palette-teal-4: #9fdcc0;
--wikt-palette-teal-5: #82d1ac;
--wikt-palette-teal-6: #64c599;
--wikt-palette-teal-7: #41b982;
--wikt-palette-teal-8: #3fa878;
--wikt-palette-teal-9: #2c865d;
--wikt-palette-cyan-0: #f6fbfc;
--wikt-palette-cyan-1: #e7f4f7;
--wikt-palette-cyan-2: #d7edf1;
--wikt-palette-cyan-3: #c7e6ec;
--wikt-palette-cyan-4: #a6d7e0;
--wikt-palette-cyan-5: #8accd7;
--wikt-palette-cyan-6: #6ebfcd;
--wikt-palette-cyan-7: #50b2c3;
--wikt-palette-cyan-8: #2ea3b7;
--wikt-palette-cyan-9: #288191;
--wikt-palette-blue-0: #f8fafe;
--wikt-palette-blue-1: #ecf2fe;
--wikt-palette-blue-2: #e0eafd;
--wikt-palette-blue-3: #d3e1fc;
--wikt-palette-blue-4: #bbd0fa;
--wikt-palette-blue-5: #a6c2f9;
--wikt-palette-blue-6: #92b3f7;
--wikt-palette-blue-7: #7ca4f5;
--wikt-palette-blue-8: #6494f4;
--wikt-palette-blue-9: #3d72db;
--wikt-palette-indigo-0: #fafafe;
--wikt-palette-indigo-1: #f1f1fd;
--wikt-palette-indigo-2: #e9e7fc;
--wikt-palette-indigo-3: #e0defa;
--wikt-palette-indigo-4: #cecbf8;
--wikt-palette-indigo-5: #bfbcf5;
--wikt-palette-indigo-6: #b0acf3;
--wikt-palette-indigo-7: #a19cf1;
--wikt-palette-indigo-8: #918aef;
--wikt-palette-indigo-9: #6e66e0;
--wikt-palette-purple-0: #fcfafe;
--wikt-palette-purple-1: #f5f0fd;
--wikt-palette-purple-2: #efe6fc;
--wikt-palette-purple-3: #e8dbfb;
--wikt-palette-purple-4: #dbc6f8;
--wikt-palette-purple-5: #d0b6f7;
--wikt-palette-purple-6: #c5a5f5;
--wikt-palette-purple-7: #b993f2;
--wikt-palette-purple-8: #ad7ff0;
--wikt-palette-purple-9: #9053eb;
--wikt-palette-magenta-0: #fcf9fe;
--wikt-palette-magenta-1: #f7effc;
--wikt-palette-magenta-2: #f3e4fa;
--wikt-palette-magenta-3: #eedaf9;
--wikt-palette-magenta-4: #e3c4f5;
--wikt-palette-magenta-5: #dbb2f2;
--wikt-palette-magenta-6: #d3a0ef;
--wikt-palette-magenta-7: #ca8deb;
--wikt-palette-magenta-8: #c078e8;
--wikt-palette-magenta-9: #a844df;
--wikt-palette-rose-0: #fcf9fc;
--wikt-palette-rose-1: #f8f0f6;
--wikt-palette-rose-2: #f3e6ef;
--wikt-palette-rose-3: #eedbe9;
--wikt-palette-rose-4: #e4c7dc;
--wikt-palette-rose-5: #dcb6d2;
--wikt-palette-rose-6: #d4a5c8;
--wikt-palette-rose-7: #cc94bd;
--wikt-palette-rose-8: #c381b1;
--wikt-palette-rose-9: #af5797;
--wikt-palette-brown-0: #fbfaf9;
--wikt-palette-brown-1: #f4f1ee;
--wikt-palette-brown-2: #eee8e4;
--wikt-palette-brown-3: #e7dfd9;
--wikt-palette-brown-4: #d8cdc3;
--wikt-palette-brown-5: #cdbfb2;
--wikt-palette-brown-6: #c2b1a1;
--wikt-palette-brown-7: #b7a18f;
--wikt-palette-brown-8: #ab927c;
--wikt-palette-brown-9: #917155;
--wikt-palette-grey-red-0: #fef9f9;
--wikt-palette-grey-red-1: #fbeff0;
--wikt-palette-grey-red-2: #f9e5e6;
--wikt-palette-grey-red-3: #f7dadc;
--wikt-palette-grey-red-4: #f2c3c8;
--wikt-palette-grey-red-5: #eeb2b7;
--wikt-palette-grey-red-6: #dca5ab;
--wikt-palette-grey-red-7: #ce979c;
--wikt-palette-grey-red-8: #c0878c;
--wikt-palette-grey-red-9: #9f686d;
--wikt-palette-grey-scarlet-0: #fdfaf8;
--wikt-palette-grey-scarlet-1: #faf0ec;
--wikt-palette-grey-scarlet-2: #f7e6e0;
--wikt-palette-grey-scarlet-3: #f4dcd4;
--wikt-palette-grey-scarlet-4: #eec7bb;
--wikt-palette-grey-scarlet-5: #e8b6a7;
--wikt-palette-grey-scarlet-6: #d6a99c;
--wikt-palette-grey-scarlet-7: #c89b8d;
--wikt-palette-grey-scarlet-8: #b98c7e;
--wikt-palette-grey-scarlet-9: #986d5f;
--wikt-palette-grey-orange-0: #fdfaf6;
--wikt-palette-grey-orange-1: #f9f0e8;
--wikt-palette-grey-orange-2: #f4e7d9;
--wikt-palette-grey-orange-3: #f0deca;
--wikt-palette-grey-orange-4: #e8caaa;
--wikt-palette-grey-orange-5: #e1bb92;
--wikt-palette-grey-orange-6: #cfae8a;
--wikt-palette-grey-orange-7: #c19f7c;
--wikt-palette-grey-orange-8: #b2906d;
--wikt-palette-grey-orange-9: #917151;
--wikt-palette-grey-amber-0: #fcfaf5;
--wikt-palette-grey-amber-1: #f7f1e6;
--wikt-palette-grey-amber-2: #f1e9d6;
--wikt-palette-grey-amber-3: #ece0c6;
--wikt-palette-grey-amber-4: #e0cda3;
--wikt-palette-grey-amber-5: #d8bf8a;
--wikt-palette-grey-amber-6: #c6b185;
--wikt-palette-grey-amber-7: #b7a377;
--wikt-palette-grey-amber-8: #a89468;
--wikt-palette-grey-amber-9: #88754c;
--wikt-palette-grey-yellow-0: #fbfaf6;
--wikt-palette-grey-yellow-1: #f3f2e7;
--wikt-palette-grey-yellow-2: #ecead9;
--wikt-palette-grey-yellow-3: #e5e1ca;
--wikt-palette-grey-yellow-4: #d5d0ab;
--wikt-palette-grey-yellow-5: #cac394;
--wikt-palette-grey-yellow-6: #beb57c;
--wikt-palette-grey-yellow-7: #aca67f;
--wikt-palette-grey-yellow-8: #9e9770;
--wikt-palette-grey-yellow-9: #7e7851;
--wikt-palette-grey-lime-0: #fafbf7;
--wikt-palette-grey-lime-1: #f0f3e8;
--wikt-palette-grey-lime-2: #e7ebda;
--wikt-palette-grey-lime-3: #dee3cc;
--wikt-palette-grey-lime-4: #cad3ae;
--wikt-palette-grey-lime-5: #bbc798;
--wikt-palette-grey-lime-6: #abba81;
--wikt-palette-grey-lime-7: #a0a984;
--wikt-palette-grey-lime-8: #919b74;
--wikt-palette-grey-lime-9: #727c55;
--wikt-palette-grey-green-0: #f8fbf7;
--wikt-palette-grey-green-1: #edf4ea;
--wikt-palette-grey-green-2: #e2ecdd;
--wikt-palette-grey-green-3: #d6e5ce;
--wikt-palette-grey-green-4: #bfd6b3;
--wikt-palette-grey-green-5: #acca9d;
--wikt-palette-grey-green-6: #9abe87;
--wikt-palette-grey-green-7: #93ad86;
--wikt-palette-grey-green-8: #839e75;
--wikt-palette-grey-green-9: #647f56;
--wikt-palette-grey-teal-0: #f7fbf9;
--wikt-palette-grey-teal-1: #eaf4ef;
--wikt-palette-grey-teal-2: #ddede5;
--wikt-palette-grey-teal-3: #d0e6dc;
--wikt-palette-grey-teal-4: #b4d7c7;
--wikt-palette-grey-teal-5: #9ecbb6;
--wikt-palette-grey-teal-6: #88bfa5;
--wikt-palette-grey-teal-7: #86ae9b;
--wikt-palette-grey-teal-8: #769f8c;
--wikt-palette-grey-teal-9: #56806d;
--wikt-palette-grey-cyan-0: #f7fbfb;
--wikt-palette-grey-cyan-1: #ebf3f5;
--wikt-palette-grey-cyan-2: #deecee;
--wikt-palette-grey-cyan-3: #d1e4e7;
--wikt-palette-grey-cyan-4: #b6d4da;
--wikt-palette-grey-cyan-5: #a1c8cf;
--wikt-palette-grey-cyan-6: #8cbbc4;
--wikt-palette-grey-cyan-7: #86acb2;
--wikt-palette-grey-cyan-8: #769da3;
--wikt-palette-grey-cyan-9: #577e84;
--wikt-palette-grey-blue-0: #f8fafe;
--wikt-palette-grey-blue-1: #ecf2fc;
--wikt-palette-grey-blue-2: #e1eafb;
--wikt-palette-grey-blue-3: #d5e1f9;
--wikt-palette-grey-blue-4: #bdd0f6;
--wikt-palette-grey-blue-5: #aec2ea;
--wikt-palette-grey-blue-6: #9fb4dd;
--wikt-palette-grey-blue-7: #91a6cf;
--wikt-palette-grey-blue-8: #8297c0;
--wikt-palette-grey-blue-9: #6477a0;
--wikt-palette-grey-indigo-0: #fafafe;
--wikt-palette-grey-indigo-1: #f1f1fb;
--wikt-palette-grey-indigo-2: #e9e8f9;
--wikt-palette-grey-indigo-3: #e0def7;
--wikt-palette-grey-indigo-4: #cdcbf2;
--wikt-palette-grey-indigo-5: #c0bcee;
--wikt-palette-grey-indigo-6: #b2afdc;
--wikt-palette-grey-indigo-7: #a4a1d0;
--wikt-palette-grey-indigo-8: #9592c2;
--wikt-palette-grey-indigo-9: #7572a3;
--wikt-palette-grey-purple-0: #fbfafe;
--wikt-palette-grey-purple-1: #f5f0fc;
--wikt-palette-grey-purple-2: #eee6fa;
--wikt-palette-grey-purple-3: #e7dcf8;
--wikt-palette-grey-purple-4: #d9c8f3;
--wikt-palette-grey-purple-5: #ceb8f0;
--wikt-palette-grey-purple-6: #c0aae1;
--wikt-palette-grey-purple-7: #b19bd3;
--wikt-palette-grey-purple-8: #a38cc5;
--wikt-palette-grey-purple-9: #846da5;
--wikt-palette-grey-magenta-0: #fcf9fd;
--wikt-palette-grey-magenta-1: #f7effa;
--wikt-palette-grey-magenta-2: #f1e5f7;
--wikt-palette-grey-magenta-3: #ebdbf4;
--wikt-palette-grey-magenta-4: #e0c6ee;
--wikt-palette-grey-magenta-5: #d7b5e9;
--wikt-palette-grey-magenta-6: #cda4e4;
--wikt-palette-grey-magenta-7: #bc98d0;
--wikt-palette-grey-magenta-8: #ae89c2;
--wikt-palette-grey-magenta-9: #8e69a2;
--wikt-palette-grey-rose-0: #fcfafb;
--wikt-palette-grey-rose-1: #f6f0f4;
--wikt-palette-grey-rose-2: #efe7ed;
--wikt-palette-grey-rose-3: #e9dde6;
--wikt-palette-grey-rose-4: #ddcad8;
--wikt-palette-grey-rose-5: #d3bacc;
--wikt-palette-grey-rose-6: #c9abc1;
--wikt-palette-grey-rose-7: #be9bb5;
--wikt-palette-grey-rose-8: #b38aa8;
--wikt-palette-grey-rose-9: #8c6e84;
--wikt-palette-grey-brown-0: #fafaf9;
--wikt-palette-grey-brown-1: #f3f2f0;
--wikt-palette-grey-brown-2: #ebe9e6;
--wikt-palette-grey-brown-3: #e4e0dc;
--wikt-palette-grey-brown-4: #d4cec8;
--wikt-palette-grey-brown-5: #c8c0b9;
--wikt-palette-grey-brown-6: #bcb2a9;
--wikt-palette-grey-brown-7: #afa398;
--wikt-palette-grey-brown-8: #a19488;
--wikt-palette-grey-brown-9: #7f766d;
--wikt-palette-grey-0: #fafafa;
--wikt-palette-grey-1: #f2f2f2;
--wikt-palette-grey-2: #e9e9e9;
--wikt-palette-grey-3: #e1e1e1;
--wikt-palette-grey-4: #cfcfcf;
--wikt-palette-grey-5: #c2c2c2;
--wikt-palette-grey-6: #b4b4b4;
--wikt-palette-grey-7: #a5a5a5;
--wikt-palette-grey-8: #979797;
--wikt-palette-grey-9: #777777;
}
/* Styles need to be duplicated exactly between these two selectors. */
@media screen {
html.skin-theme-clientpref-night {
--wikt-palette-white: #101418;
--wikt-palette-black: #eaecf0;
--wikt-palette-beige: #2f1f10;
--wikt-palette-darkgrey: #b2bbc3;
--wikt-palette-grey: #4e4e4e;
--wikt-palette-lightgrey: #3d3d3d;
--wikt-palette-lightergrey: #242424;
--wikt-palette-cream: #2c2c21;
--wikt-palette-pink: #612042;
--wikt-palette-peach: #58230a;
--wikt-palette-gold: #c9b163;
--wikt-palette-dullgold: #ab995e;
--wikt-palette-yellow: #4a3912;
--wikt-palette-dullyellow: #3a3520;
--wikt-palette-brightyellow: #5e5300;
--wikt-palette-palegreen: #112511;
--wikt-palette-palergreen: #051f0c;
--wikt-palette-mint: #194819;
--wikt-palette-green: #aad7aa;
--wikt-palette-forestgreen: #499149;
--wikt-palette-luminousgreen: #103310;
--wikt-palette-paleblue: #1c2229;
--wikt-palette-cyan: #0a3239;
--wikt-palette-palecyan: #171c21;
--wikt-palette-lightblue: #092f58;
--wikt-palette-dulllightblue: #305070;
--wikt-palette-blue: #aaaaff;
--wikt-palette-dullblue: #b8c0c5;
--wikt-palette-dullcyan: #27292d;
--wikt-palette-deepblue: #a5c3e1;
--wikt-palette-lavender: #1a1a2a;
--wikt-palette-lightyellow: #24241a;
--wikt-palette-red: #d73333;
--wikt-palette-darkred: #ffcccc;
--wikt-palette-deepred: #e36a79;
--wikt-palette-orange: #593e02;
--wikt-palette-lighterblue: #0e1e33;
--wikt-palette-dulllighterblue: #102437;
--wikt-palette-lightred: #421211;
--wikt-palette-indigo: #30306f;
--wikt-palette-lightindigo: #242448;
--wikt-palette-palerpink: #251111;
--wikt-palette-honey: #feefb3;
/* Autogenerated with [[Wiktionary:Palette/numbered]] */
--wikt-palette-red-0: #28070a;
--wikt-palette-red-1: #3e0c11;
--wikt-palette-red-2: #5a131a;
--wikt-palette-red-3: #731b24;
--wikt-palette-red-4: #89242d;
--wikt-palette-red-5: #a12d38;
--wikt-palette-red-6: #bf3b48;
--wikt-palette-red-7: #e95462;
--wikt-palette-red-8: #ee7984;
--wikt-palette-red-9: #f3a2aa;
--wikt-palette-scarlet-0: #250c05;
--wikt-palette-scarlet-1: #3a1409;
--wikt-palette-scarlet-2: #521d0e;
--wikt-palette-scarlet-3: #692814;
--wikt-palette-scarlet-4: #7f311a;
--wikt-palette-scarlet-5: #953c22;
--wikt-palette-scarlet-6: #b24b2c;
--wikt-palette-scarlet-7: #e25e37;
--wikt-palette-scarlet-8: #e88062;
--wikt-palette-scarlet-9: #efa791;
--wikt-palette-orange-0: #211101;
--wikt-palette-orange-1: #331b03;
--wikt-palette-orange-2: #462604;
--wikt-palette-orange-3: #5c3206;
--wikt-palette-orange-4: #703d08;
--wikt-palette-orange-5: #85490a;
--wikt-palette-orange-6: #a0580e;
--wikt-palette-orange-7: #cb7114;
--wikt-palette-orange-8: #de8931;
--wikt-palette-orange-9: #e8ac6e;
--wikt-palette-amber-0: #1c1401;
--wikt-palette-amber-1: #2c1f03;
--wikt-palette-amber-2: #3d2a04;
--wikt-palette-amber-3: #503806;
--wikt-palette-amber-4: #614508;
--wikt-palette-amber-5: #74520a;
--wikt-palette-amber-6: #8c640d;
--wikt-palette-amber-7: #b17f13;
--wikt-palette-amber-8: #cc9218;
--wikt-palette-amber-9: #ddb258;
--wikt-palette-yellow-0: #181504;
--wikt-palette-yellow-1: #262107;
--wikt-palette-yellow-2: #342e0a;
--wikt-palette-yellow-3: #443c0f;
--wikt-palette-yellow-4: #534a13;
--wikt-palette-yellow-5: #625819;
--wikt-palette-yellow-6: #776b21;
--wikt-palette-yellow-7: #97882f;
--wikt-palette-yellow-8: #ae9e3b;
--wikt-palette-yellow-9: #c8b95b;
--wikt-palette-lime-0: #131705;
--wikt-palette-lime-1: #1e2409;
--wikt-palette-lime-2: #28310d;
--wikt-palette-lime-3: #354112;
--wikt-palette-lime-4: #424f18;
--wikt-palette-lime-5: #4f5e1f;
--wikt-palette-lime-6: #607228;
--wikt-palette-lime-7: #7b903a;
--wikt-palette-lime-8: #8fa648;
--wikt-palette-lime-9: #aac25f;
--wikt-palette-green-0: #0c1805;
--wikt-palette-green-1: #122608;
--wikt-palette-green-2: #1a340c;
--wikt-palette-green-3: #234411;
--wikt-palette-green-4: #2c5317;
--wikt-palette-green-5: #35631d;
--wikt-palette-green-6: #427726;
--wikt-palette-green-7: #599737;
--wikt-palette-green-8: #6aae45;
--wikt-palette-green-9: #8dc86d;
--wikt-palette-teal-0: #04180f;
--wikt-palette-teal-1: #082618;
--wikt-palette-teal-2: #0c3422;
--wikt-palette-teal-3: #11452d;
--wikt-palette-teal-4: #165438;
--wikt-palette-teal-5: #1c6443;
--wikt-palette-teal-6: #267953;
--wikt-palette-teal-7: #36986b;
--wikt-palette-teal-8: #43af7e;
--wikt-palette-teal-9: #6fc9a0;
--wikt-palette-cyan-0: #04171b;
--wikt-palette-cyan-1: #07242a;
--wikt-palette-cyan-2: #0a3239;
--wikt-palette-cyan-3: #0f424b;
--wikt-palette-cyan-4: #14515c;
--wikt-palette-cyan-5: #1a606d;
--wikt-palette-cyan-6: #227583;
--wikt-palette-cyan-7: #3194a6;
--wikt-palette-cyan-8: #40aabd;
--wikt-palette-cyan-9: #78c4d1;
--wikt-palette-blue-0: #07132d;
--wikt-palette-blue-1: #0b1f45;
--wikt-palette-blue-2: #122b60;
--wikt-palette-blue-3: #19397b;
--wikt-palette-blue-4: #214692;
--wikt-palette-blue-5: #2954ab;
--wikt-palette-blue-6: #3667c9;
--wikt-palette-blue-7: #4e84f2;
--wikt-palette-blue-8: #709cf5;
--wikt-palette-blue-9: #99b8f8;
--wikt-palette-indigo-0: #100e36;
--wikt-palette-indigo-1: #1b1751;
--wikt-palette-indigo-2: #27226c;
--wikt-palette-indigo-3: #352f88;
--wikt-palette-indigo-4: #423b9e;
--wikt-palette-indigo-5: #5049b4;
--wikt-palette-indigo-6: #5f55e7;
--wikt-palette-indigo-7: #8179ec;
--wikt-palette-indigo-8: #9992f0;
--wikt-palette-indigo-9: #b6b1f4;
--wikt-palette-purple-0: #1a0934;
--wikt-palette-purple-1: #29104e;
--wikt-palette-purple-2: #3a196d;
--wikt-palette-purple-3: #4c238a;
--wikt-palette-purple-4: #5c2da3;
--wikt-palette-purple-5: #6e38bc;
--wikt-palette-purple-6: #8541e9;
--wikt-palette-purple-7: #a16cee;
--wikt-palette-purple-8: #b389f1;
--wikt-palette-purple-9: #c9abf5;
--wikt-palette-magenta-0: #20062d;
--wikt-palette-magenta-1: #310b46;
--wikt-palette-magenta-2: #471363;
--wikt-palette-magenta-3: #5c1b7f;
--wikt-palette-magenta-4: #6e2398;
--wikt-palette-magenta-5: #822cb1;
--wikt-palette-magenta-6: #9f30dc;
--wikt-palette-magenta-7: #b663e4;
--wikt-palette-magenta-8: #c581ea;
--wikt-palette-magenta-9: #d6a6f0;
--wikt-palette-rose-0: #240b1d;
--wikt-palette-rose-1: #37122d;
--wikt-palette-rose-2: #4b1c3f;
--wikt-palette-rose-3: #5f2751;
--wikt-palette-rose-4: #723362;
--wikt-palette-rose-5: #844072;
--wikt-palette-rose-6: #a7478e;
--wikt-palette-rose-7: #ba6fa6;
--wikt-palette-rose-8: #c78ab7;
--wikt-palette-rose-9: #d7abcc;
--wikt-palette-brown-0: #1d130b;
--wikt-palette-brown-1: #2d1e12;
--wikt-palette-brown-2: #3c2a1b;
--wikt-palette-brown-3: #4d3826;
--wikt-palette-brown-4: #5d4532;
--wikt-palette-brown-5: #6c533e;
--wikt-palette-brown-6: #876446;
--wikt-palette-brown-7: #9f826a;
--wikt-palette-brown-8: #b19986;
--wikt-palette-brown-9: #c6b5a7;
--wikt-palette-grey-red-0: #230f11;
--wikt-palette-grey-red-1: #34191b;
--wikt-palette-grey-red-2: #462327;
--wikt-palette-grey-red-3: #5a3034;
--wikt-palette-grey-red-4: #6b3c41;
--wikt-palette-grey-red-5: #7c4a4f;
--wikt-palette-grey-red-6: #925c61;
--wikt-palette-grey-red-7: #b1797e;
--wikt-palette-grey-red-8: #c78e94;
--wikt-palette-grey-red-9: #eba6ad;
--wikt-palette-grey-scarlet-0: #20110d;
--wikt-palette-grey-scarlet-1: #311c16;
--wikt-palette-grey-scarlet-2: #422720;
--wikt-palette-grey-scarlet-3: #54342b;
--wikt-palette-grey-scarlet-4: #654137;
--wikt-palette-grey-scarlet-5: #764e43;
--wikt-palette-grey-scarlet-6: #8b6154;
--wikt-palette-grey-scarlet-7: #aa7d70;
--wikt-palette-grey-scarlet-8: #c19386;
--wikt-palette-grey-scarlet-9: #e5ab9a;
--wikt-palette-grey-orange-0: #1c140a;
--wikt-palette-grey-orange-1: #2c1f11;
--wikt-palette-grey-orange-2: #3b2a19;
--wikt-palette-grey-orange-3: #4d3823;
--wikt-palette-grey-orange-4: #5d452c;
--wikt-palette-grey-orange-5: #6e5337;
--wikt-palette-grey-orange-6: #836547;
--wikt-palette-grey-orange-7: #a28260;
--wikt-palette-grey-orange-8: #b99774;
--wikt-palette-grey-orange-9: #ddb082;
--wikt-palette-grey-amber-0: #1a150a;
--wikt-palette-grey-amber-1: #282010;
--wikt-palette-grey-amber-2: #372c17;
--wikt-palette-grey-amber-3: #473b20;
--wikt-palette-grey-amber-4: #57482a;
--wikt-palette-grey-amber-5: #675634;
--wikt-palette-grey-amber-6: #7b6943;
--wikt-palette-grey-amber-7: #99855b;
--wikt-palette-grey-amber-8: #b09b70;
--wikt-palette-grey-amber-9: #d2b579;
--wikt-palette-grey-yellow-0: #17160a;
--wikt-palette-grey-yellow-1: #242211;
--wikt-palette-grey-yellow-2: #322e19;
--wikt-palette-grey-yellow-3: #413d22;
--wikt-palette-grey-yellow-4: #4f4a2c;
--wikt-palette-grey-yellow-5: #5e5837;
--wikt-palette-grey-yellow-6: #716b47;
--wikt-palette-grey-yellow-7: #8f8861;
--wikt-palette-grey-yellow-8: #a59e78;
--wikt-palette-grey-yellow-9: #c2b984;
--wikt-palette-grey-lime-0: #13160b;
--wikt-palette-grey-lime-1: #1f2312;
--wikt-palette-grey-lime-2: #2b3019;
--wikt-palette-grey-lime-3: #383f23;
--wikt-palette-grey-lime-4: #454d2d;
--wikt-palette-grey-lime-5: #535c39;
--wikt-palette-grey-lime-6: #666f49;
--wikt-palette-grey-lime-7: #838c65;
--wikt-palette-grey-lime-8: #98a27c;
--wikt-palette-grey-lime-9: #b1be8a;
--wikt-palette-grey-green-0: #0f170b;
--wikt-palette-grey-green-1: #182512;
--wikt-palette-grey-green-2: #22321a;
--wikt-palette-grey-green-3: #2e4124;
--wikt-palette-grey-green-4: #3a502e;
--wikt-palette-grey-green-5: #465f3a;
--wikt-palette-grey-green-6: #58724a;
--wikt-palette-grey-green-7: #748f66;
--wikt-palette-grey-green-8: #8ba57e;
--wikt-palette-grey-green-9: #a1c290;
--wikt-palette-grey-teal-0: #0b1812;
--wikt-palette-grey-teal-1: #12251d;
--wikt-palette-grey-teal-2: #1a3227;
--wikt-palette-grey-teal-3: #244235;
--wikt-palette-grey-teal-4: #2e5041;
--wikt-palette-grey-teal-5: #3a5f4e;
--wikt-palette-grey-teal-6: #4b7361;
--wikt-palette-grey-teal-7: #66907d;
--wikt-palette-grey-teal-8: #7ea694;
--wikt-palette-grey-teal-9: #91c3ac;
--wikt-palette-grey-cyan-0: #0b171a;
--wikt-palette-grey-cyan-1: #122428;
--wikt-palette-grey-cyan-2: #1a3136;
--wikt-palette-grey-cyan-3: #244145;
--wikt-palette-grey-cyan-4: #2f4f54;
--wikt-palette-grey-cyan-5: #3a5d64;
--wikt-palette-grey-cyan-6: #4b7178;
--wikt-palette-grey-cyan-7: #678e95;
--wikt-palette-grey-cyan-8: #7ea4ab;
--wikt-palette-grey-cyan-9: #94c0c8;
--wikt-palette-grey-blue-0: #0f1523;
--wikt-palette-grey-blue-1: #182134;
--wikt-palette-grey-blue-2: #212d46;
--wikt-palette-grey-blue-3: #2e3c5a;
--wikt-palette-grey-blue-4: #394a6a;
--wikt-palette-grey-blue-5: #47587c;
--wikt-palette-grey-blue-6: #586b92;
--wikt-palette-grey-blue-7: #7488b2;
--wikt-palette-grey-blue-8: #899ec8;
--wikt-palette-grey-blue-9: #9cb8f2;
--wikt-palette-grey-indigo-0: #141327;
--wikt-palette-grey-indigo-1: #201e3a;
--wikt-palette-grey-indigo-2: #2c2a4c;
--wikt-palette-grey-indigo-3: #3a385f;
--wikt-palette-grey-indigo-4: #484571;
--wikt-palette-grey-indigo-5: #565382;
--wikt-palette-grey-indigo-6: #696696;
--wikt-palette-grey-indigo-7: #8682b4;
--wikt-palette-grey-indigo-8: #9c98c8;
--wikt-palette-grey-indigo-9: #b6b2ec;
--wikt-palette-grey-purple-0: #1a1126;
--wikt-palette-grey-purple-1: #271b39;
--wikt-palette-grey-purple-2: #35264b;
--wikt-palette-grey-purple-3: #453460;
--wikt-palette-grey-purple-4: #544071;
--wikt-palette-grey-purple-5: #634e83;
--wikt-palette-grey-purple-6: #776098;
--wikt-palette-grey-purple-7: #957db7;
--wikt-palette-grey-purple-8: #aa93cc;
--wikt-palette-grey-purple-9: #c7acee;
--wikt-palette-grey-magenta-0: #1d0f24;
--wikt-palette-grey-magenta-1: #2c1937;
--wikt-palette-grey-magenta-2: #3c244a;
--wikt-palette-grey-magenta-3: #4d315d;
--wikt-palette-grey-magenta-4: #5d3d6e;
--wikt-palette-grey-magenta-5: #6c4b7f;
--wikt-palette-grey-magenta-6: #815d95;
--wikt-palette-grey-magenta-7: #9f7ab4;
--wikt-palette-grey-magenta-8: #be88dc;
--wikt-palette-grey-magenta-9: #d1aae6;
--wikt-palette-grey-rose-0: #1f101b;
--wikt-palette-grey-rose-1: #2f1a29;
--wikt-palette-grey-rose-2: #3e2538;
--wikt-palette-grey-rose-3: #503348;
--wikt-palette-grey-rose-4: #5f4057;
--wikt-palette-grey-rose-5: #6e4e66;
--wikt-palette-grey-rose-6: #816179;
--wikt-palette-grey-rose-7: #a9799c;
--wikt-palette-grey-rose-8: #b992ae;
--wikt-palette-grey-rose-9: #ccb0c5;
--wikt-palette-grey-brown-0: #1a140f;
--wikt-palette-grey-brown-1: #282019;
--wikt-palette-grey-brown-2: #362c23;
--wikt-palette-grey-brown-3: #463a31;
--wikt-palette-grey-brown-4: #53473d;
--wikt-palette-grey-brown-5: #62564c;
--wikt-palette-grey-brown-6: #746960;
--wikt-palette-grey-brown-7: #958578;
--wikt-palette-grey-brown-8: #a89b90;
--wikt-palette-grey-brown-9: #c0b7af;
--wikt-palette-grey-0: #151515;
--wikt-palette-grey-1: #212121;
--wikt-palette-grey-2: #2d2d2d;
--wikt-palette-grey-3: #3c3c3c;
--wikt-palette-grey-4: #494949;
--wikt-palette-grey-5: #585858;
--wikt-palette-grey-6: #6b6b6b;
--wikt-palette-grey-7: #878787;
--wikt-palette-grey-8: #9d9d9d;
--wikt-palette-grey-9: #b8b8b8;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os {
--wikt-palette-white: #101418;
--wikt-palette-black: #eaecf0;
--wikt-palette-beige: #2f1f10;
--wikt-palette-darkgrey: #b2bbc3;
--wikt-palette-grey: #4e4e4e;
--wikt-palette-lightgrey: #3d3d3d;
--wikt-palette-lightergrey: #242424;
--wikt-palette-cream: #2c2c21;
--wikt-palette-pink: #612042;
--wikt-palette-peach: #58230a;
--wikt-palette-gold: #c9b163;
--wikt-palette-dullgold: #ab995e;
--wikt-palette-yellow: #4a3912;
--wikt-palette-dullyellow: #3a3520;
--wikt-palette-brightyellow: #5e5300;
--wikt-palette-palegreen: #112511;
--wikt-palette-palergreen: #051f0c;
--wikt-palette-mint: #194819;
--wikt-palette-green: #aad7aa;
--wikt-palette-forestgreen: #499149;
--wikt-palette-luminousgreen: #103310;
--wikt-palette-paleblue: #1c2229;
--wikt-palette-cyan: #0a3239;
--wikt-palette-palecyan: #171c21;
--wikt-palette-lightblue: #092f58;
--wikt-palette-dulllightblue: #305070;
--wikt-palette-blue: #aaaaff;
--wikt-palette-dullblue: #b8c0c5;
--wikt-palette-dullcyan: #27292d;
--wikt-palette-deepblue: #a5c3e1;
--wikt-palette-lavender: #1a1a2a;
--wikt-palette-lightyellow: #24241a;
--wikt-palette-red: #d73333;
--wikt-palette-darkred: #ffcccc;
--wikt-palette-deepred: #e36a79;
--wikt-palette-orange: #593e02;
--wikt-palette-lighterblue: #0e1e33;
--wikt-palette-dulllighterblue: #102437;
--wikt-palette-lightred: #421211;
--wikt-palette-indigo: #30306f;
--wikt-palette-lightindigo: #242448;
--wikt-palette-palerpink: #251111;
--wikt-palette-honey: #feefb3;
/* Autogenerated with [[Wiktionary:Palette/numbered]] */
--wikt-palette-red-0: #28070a;
--wikt-palette-red-1: #3e0c11;
--wikt-palette-red-2: #5a131a;
--wikt-palette-red-3: #731b24;
--wikt-palette-red-4: #89242d;
--wikt-palette-red-5: #a12d38;
--wikt-palette-red-6: #bf3b48;
--wikt-palette-red-7: #e95462;
--wikt-palette-red-8: #ee7984;
--wikt-palette-red-9: #f3a2aa;
--wikt-palette-scarlet-0: #250c05;
--wikt-palette-scarlet-1: #3a1409;
--wikt-palette-scarlet-2: #521d0e;
--wikt-palette-scarlet-3: #692814;
--wikt-palette-scarlet-4: #7f311a;
--wikt-palette-scarlet-5: #953c22;
--wikt-palette-scarlet-6: #b24b2c;
--wikt-palette-scarlet-7: #e25e37;
--wikt-palette-scarlet-8: #e88062;
--wikt-palette-scarlet-9: #efa791;
--wikt-palette-orange-0: #211101;
--wikt-palette-orange-1: #331b03;
--wikt-palette-orange-2: #462604;
--wikt-palette-orange-3: #5c3206;
--wikt-palette-orange-4: #703d08;
--wikt-palette-orange-5: #85490a;
--wikt-palette-orange-6: #a0580e;
--wikt-palette-orange-7: #cb7114;
--wikt-palette-orange-8: #de8931;
--wikt-palette-orange-9: #e8ac6e;
--wikt-palette-amber-0: #1c1401;
--wikt-palette-amber-1: #2c1f03;
--wikt-palette-amber-2: #3d2a04;
--wikt-palette-amber-3: #503806;
--wikt-palette-amber-4: #614508;
--wikt-palette-amber-5: #74520a;
--wikt-palette-amber-6: #8c640d;
--wikt-palette-amber-7: #b17f13;
--wikt-palette-amber-8: #cc9218;
--wikt-palette-amber-9: #ddb258;
--wikt-palette-yellow-0: #181504;
--wikt-palette-yellow-1: #262107;
--wikt-palette-yellow-2: #342e0a;
--wikt-palette-yellow-3: #443c0f;
--wikt-palette-yellow-4: #534a13;
--wikt-palette-yellow-5: #625819;
--wikt-palette-yellow-6: #776b21;
--wikt-palette-yellow-7: #97882f;
--wikt-palette-yellow-8: #ae9e3b;
--wikt-palette-yellow-9: #c8b95b;
--wikt-palette-lime-0: #131705;
--wikt-palette-lime-1: #1e2409;
--wikt-palette-lime-2: #28310d;
--wikt-palette-lime-3: #354112;
--wikt-palette-lime-4: #424f18;
--wikt-palette-lime-5: #4f5e1f;
--wikt-palette-lime-6: #607228;
--wikt-palette-lime-7: #7b903a;
--wikt-palette-lime-8: #8fa648;
--wikt-palette-lime-9: #aac25f;
--wikt-palette-green-0: #0c1805;
--wikt-palette-green-1: #122608;
--wikt-palette-green-2: #1a340c;
--wikt-palette-green-3: #234411;
--wikt-palette-green-4: #2c5317;
--wikt-palette-green-5: #35631d;
--wikt-palette-green-6: #427726;
--wikt-palette-green-7: #599737;
--wikt-palette-green-8: #6aae45;
--wikt-palette-green-9: #8dc86d;
--wikt-palette-teal-0: #04180f;
--wikt-palette-teal-1: #082618;
--wikt-palette-teal-2: #0c3422;
--wikt-palette-teal-3: #11452d;
--wikt-palette-teal-4: #165438;
--wikt-palette-teal-5: #1c6443;
--wikt-palette-teal-6: #267953;
--wikt-palette-teal-7: #36986b;
--wikt-palette-teal-8: #43af7e;
--wikt-palette-teal-9: #6fc9a0;
--wikt-palette-cyan-0: #04171b;
--wikt-palette-cyan-1: #07242a;
--wikt-palette-cyan-2: #0a3239;
--wikt-palette-cyan-3: #0f424b;
--wikt-palette-cyan-4: #14515c;
--wikt-palette-cyan-5: #1a606d;
--wikt-palette-cyan-6: #227583;
--wikt-palette-cyan-7: #3194a6;
--wikt-palette-cyan-8: #40aabd;
--wikt-palette-cyan-9: #78c4d1;
--wikt-palette-blue-0: #07132d;
--wikt-palette-blue-1: #0b1f45;
--wikt-palette-blue-2: #122b60;
--wikt-palette-blue-3: #19397b;
--wikt-palette-blue-4: #214692;
--wikt-palette-blue-5: #2954ab;
--wikt-palette-blue-6: #3667c9;
--wikt-palette-blue-7: #4e84f2;
--wikt-palette-blue-8: #709cf5;
--wikt-palette-blue-9: #99b8f8;
--wikt-palette-indigo-0: #100e36;
--wikt-palette-indigo-1: #1b1751;
--wikt-palette-indigo-2: #27226c;
--wikt-palette-indigo-3: #352f88;
--wikt-palette-indigo-4: #423b9e;
--wikt-palette-indigo-5: #5049b4;
--wikt-palette-indigo-6: #5f55e7;
--wikt-palette-indigo-7: #8179ec;
--wikt-palette-indigo-8: #9992f0;
--wikt-palette-indigo-9: #b6b1f4;
--wikt-palette-purple-0: #1a0934;
--wikt-palette-purple-1: #29104e;
--wikt-palette-purple-2: #3a196d;
--wikt-palette-purple-3: #4c238a;
--wikt-palette-purple-4: #5c2da3;
--wikt-palette-purple-5: #6e38bc;
--wikt-palette-purple-6: #8541e9;
--wikt-palette-purple-7: #a16cee;
--wikt-palette-purple-8: #b389f1;
--wikt-palette-purple-9: #c9abf5;
--wikt-palette-magenta-0: #20062d;
--wikt-palette-magenta-1: #310b46;
--wikt-palette-magenta-2: #471363;
--wikt-palette-magenta-3: #5c1b7f;
--wikt-palette-magenta-4: #6e2398;
--wikt-palette-magenta-5: #822cb1;
--wikt-palette-magenta-6: #9f30dc;
--wikt-palette-magenta-7: #b663e4;
--wikt-palette-magenta-8: #c581ea;
--wikt-palette-magenta-9: #d6a6f0;
--wikt-palette-rose-0: #240b1d;
--wikt-palette-rose-1: #37122d;
--wikt-palette-rose-2: #4b1c3f;
--wikt-palette-rose-3: #5f2751;
--wikt-palette-rose-4: #723362;
--wikt-palette-rose-5: #844072;
--wikt-palette-rose-6: #a7478e;
--wikt-palette-rose-7: #ba6fa6;
--wikt-palette-rose-8: #c78ab7;
--wikt-palette-rose-9: #d7abcc;
--wikt-palette-brown-0: #1d130b;
--wikt-palette-brown-1: #2d1e12;
--wikt-palette-brown-2: #3c2a1b;
--wikt-palette-brown-3: #4d3826;
--wikt-palette-brown-4: #5d4532;
--wikt-palette-brown-5: #6c533e;
--wikt-palette-brown-6: #876446;
--wikt-palette-brown-7: #9f826a;
--wikt-palette-brown-8: #b19986;
--wikt-palette-brown-9: #c6b5a7;
--wikt-palette-grey-red-0: #230f11;
--wikt-palette-grey-red-1: #34191b;
--wikt-palette-grey-red-2: #462327;
--wikt-palette-grey-red-3: #5a3034;
--wikt-palette-grey-red-4: #6b3c41;
--wikt-palette-grey-red-5: #7c4a4f;
--wikt-palette-grey-red-6: #925c61;
--wikt-palette-grey-red-7: #b1797e;
--wikt-palette-grey-red-8: #c78e94;
--wikt-palette-grey-red-9: #eba6ad;
--wikt-palette-grey-scarlet-0: #20110d;
--wikt-palette-grey-scarlet-1: #311c16;
--wikt-palette-grey-scarlet-2: #422720;
--wikt-palette-grey-scarlet-3: #54342b;
--wikt-palette-grey-scarlet-4: #654137;
--wikt-palette-grey-scarlet-5: #764e43;
--wikt-palette-grey-scarlet-6: #8b6154;
--wikt-palette-grey-scarlet-7: #aa7d70;
--wikt-palette-grey-scarlet-8: #c19386;
--wikt-palette-grey-scarlet-9: #e5ab9a;
--wikt-palette-grey-orange-0: #1c140a;
--wikt-palette-grey-orange-1: #2c1f11;
--wikt-palette-grey-orange-2: #3b2a19;
--wikt-palette-grey-orange-3: #4d3823;
--wikt-palette-grey-orange-4: #5d452c;
--wikt-palette-grey-orange-5: #6e5337;
--wikt-palette-grey-orange-6: #836547;
--wikt-palette-grey-orange-7: #a28260;
--wikt-palette-grey-orange-8: #b99774;
--wikt-palette-grey-orange-9: #ddb082;
--wikt-palette-grey-amber-0: #1a150a;
--wikt-palette-grey-amber-1: #282010;
--wikt-palette-grey-amber-2: #372c17;
--wikt-palette-grey-amber-3: #473b20;
--wikt-palette-grey-amber-4: #57482a;
--wikt-palette-grey-amber-5: #675634;
--wikt-palette-grey-amber-6: #7b6943;
--wikt-palette-grey-amber-7: #99855b;
--wikt-palette-grey-amber-8: #b09b70;
--wikt-palette-grey-amber-9: #d2b579;
--wikt-palette-grey-yellow-0: #17160a;
--wikt-palette-grey-yellow-1: #242211;
--wikt-palette-grey-yellow-2: #322e19;
--wikt-palette-grey-yellow-3: #413d22;
--wikt-palette-grey-yellow-4: #4f4a2c;
--wikt-palette-grey-yellow-5: #5e5837;
--wikt-palette-grey-yellow-6: #716b47;
--wikt-palette-grey-yellow-7: #8f8861;
--wikt-palette-grey-yellow-8: #a59e78;
--wikt-palette-grey-yellow-9: #c2b984;
--wikt-palette-grey-lime-0: #13160b;
--wikt-palette-grey-lime-1: #1f2312;
--wikt-palette-grey-lime-2: #2b3019;
--wikt-palette-grey-lime-3: #383f23;
--wikt-palette-grey-lime-4: #454d2d;
--wikt-palette-grey-lime-5: #535c39;
--wikt-palette-grey-lime-6: #666f49;
--wikt-palette-grey-lime-7: #838c65;
--wikt-palette-grey-lime-8: #98a27c;
--wikt-palette-grey-lime-9: #b1be8a;
--wikt-palette-grey-green-0: #0f170b;
--wikt-palette-grey-green-1: #182512;
--wikt-palette-grey-green-2: #22321a;
--wikt-palette-grey-green-3: #2e4124;
--wikt-palette-grey-green-4: #3a502e;
--wikt-palette-grey-green-5: #465f3a;
--wikt-palette-grey-green-6: #58724a;
--wikt-palette-grey-green-7: #748f66;
--wikt-palette-grey-green-8: #8ba57e;
--wikt-palette-grey-green-9: #a1c290;
--wikt-palette-grey-teal-0: #0b1812;
--wikt-palette-grey-teal-1: #12251d;
--wikt-palette-grey-teal-2: #1a3227;
--wikt-palette-grey-teal-3: #244235;
--wikt-palette-grey-teal-4: #2e5041;
--wikt-palette-grey-teal-5: #3a5f4e;
--wikt-palette-grey-teal-6: #4b7361;
--wikt-palette-grey-teal-7: #66907d;
--wikt-palette-grey-teal-8: #7ea694;
--wikt-palette-grey-teal-9: #91c3ac;
--wikt-palette-grey-cyan-0: #0b171a;
--wikt-palette-grey-cyan-1: #122428;
--wikt-palette-grey-cyan-2: #1a3136;
--wikt-palette-grey-cyan-3: #244145;
--wikt-palette-grey-cyan-4: #2f4f54;
--wikt-palette-grey-cyan-5: #3a5d64;
--wikt-palette-grey-cyan-6: #4b7178;
--wikt-palette-grey-cyan-7: #678e95;
--wikt-palette-grey-cyan-8: #7ea4ab;
--wikt-palette-grey-cyan-9: #94c0c8;
--wikt-palette-grey-blue-0: #0f1523;
--wikt-palette-grey-blue-1: #182134;
--wikt-palette-grey-blue-2: #212d46;
--wikt-palette-grey-blue-3: #2e3c5a;
--wikt-palette-grey-blue-4: #394a6a;
--wikt-palette-grey-blue-5: #47587c;
--wikt-palette-grey-blue-6: #586b92;
--wikt-palette-grey-blue-7: #7488b2;
--wikt-palette-grey-blue-8: #899ec8;
--wikt-palette-grey-blue-9: #9cb8f2;
--wikt-palette-grey-indigo-0: #141327;
--wikt-palette-grey-indigo-1: #201e3a;
--wikt-palette-grey-indigo-2: #2c2a4c;
--wikt-palette-grey-indigo-3: #3a385f;
--wikt-palette-grey-indigo-4: #484571;
--wikt-palette-grey-indigo-5: #565382;
--wikt-palette-grey-indigo-6: #696696;
--wikt-palette-grey-indigo-7: #8682b4;
--wikt-palette-grey-indigo-8: #9c98c8;
--wikt-palette-grey-indigo-9: #b6b2ec;
--wikt-palette-grey-purple-0: #1a1126;
--wikt-palette-grey-purple-1: #271b39;
--wikt-palette-grey-purple-2: #35264b;
--wikt-palette-grey-purple-3: #453460;
--wikt-palette-grey-purple-4: #544071;
--wikt-palette-grey-purple-5: #634e83;
--wikt-palette-grey-purple-6: #776098;
--wikt-palette-grey-purple-7: #957db7;
--wikt-palette-grey-purple-8: #aa93cc;
--wikt-palette-grey-purple-9: #c7acee;
--wikt-palette-grey-magenta-0: #1d0f24;
--wikt-palette-grey-magenta-1: #2c1937;
--wikt-palette-grey-magenta-2: #3c244a;
--wikt-palette-grey-magenta-3: #4d315d;
--wikt-palette-grey-magenta-4: #5d3d6e;
--wikt-palette-grey-magenta-5: #6c4b7f;
--wikt-palette-grey-magenta-6: #815d95;
--wikt-palette-grey-magenta-7: #9f7ab4;
--wikt-palette-grey-magenta-8: #be88dc;
--wikt-palette-grey-magenta-9: #d1aae6;
--wikt-palette-grey-rose-0: #1f101b;
--wikt-palette-grey-rose-1: #2f1a29;
--wikt-palette-grey-rose-2: #3e2538;
--wikt-palette-grey-rose-3: #503348;
--wikt-palette-grey-rose-4: #5f4057;
--wikt-palette-grey-rose-5: #6e4e66;
--wikt-palette-grey-rose-6: #816179;
--wikt-palette-grey-rose-7: #a9799c;
--wikt-palette-grey-rose-8: #b992ae;
--wikt-palette-grey-rose-9: #ccb0c5;
--wikt-palette-grey-brown-0: #1a140f;
--wikt-palette-grey-brown-1: #282019;
--wikt-palette-grey-brown-2: #362c23;
--wikt-palette-grey-brown-3: #463a31;
--wikt-palette-grey-brown-4: #53473d;
--wikt-palette-grey-brown-5: #62564c;
--wikt-palette-grey-brown-6: #746960;
--wikt-palette-grey-brown-7: #958578;
--wikt-palette-grey-brown-8: #a89b90;
--wikt-palette-grey-brown-9: #c0b7af;
--wikt-palette-grey-0: #151515;
--wikt-palette-grey-1: #212121;
--wikt-palette-grey-2: #2d2d2d;
--wikt-palette-grey-3: #3c3c3c;
--wikt-palette-grey-4: #494949;
--wikt-palette-grey-5: #585858;
--wikt-palette-grey-6: #6b6b6b;
--wikt-palette-grey-7: #878787;
--wikt-palette-grey-8: #9d9d9d;
--wikt-palette-grey-9: #b8b8b8;
}
}
/* Shorthand available with data-wikt-palette-p and scoping */
[data-wikt-palette-p="red"] {
--wikt-p-0: var(--wikt-palette-red-0);
--wikt-p-1: var(--wikt-palette-red-1);
--wikt-p-2: var(--wikt-palette-red-2);
--wikt-p-3: var(--wikt-palette-red-3);
--wikt-p-4: var(--wikt-palette-red-4);
--wikt-p-5: var(--wikt-palette-red-5);
--wikt-p-6: var(--wikt-palette-red-6);
--wikt-p-7: var(--wikt-palette-red-7);
--wikt-p-8: var(--wikt-palette-red-8);
--wikt-p-9: var(--wikt-palette-red-9);
--wikt-p-g-0: var(--wikt-palette-grey-red-0);
--wikt-p-g-1: var(--wikt-palette-grey-red-1);
--wikt-p-g-2: var(--wikt-palette-grey-red-2);
--wikt-p-g-3: var(--wikt-palette-grey-red-3);
--wikt-p-g-4: var(--wikt-palette-grey-red-4);
--wikt-p-g-5: var(--wikt-palette-grey-red-5);
--wikt-p-g-6: var(--wikt-palette-grey-red-6);
--wikt-p-g-7: var(--wikt-palette-grey-red-7);
--wikt-p-g-8: var(--wikt-palette-grey-red-8);
--wikt-p-g-9: var(--wikt-palette-grey-red-9);
}
[data-wikt-palette-p="scarlet"] {
--wikt-p-0: var(--wikt-palette-scarlet-0);
--wikt-p-1: var(--wikt-palette-scarlet-1);
--wikt-p-2: var(--wikt-palette-scarlet-2);
--wikt-p-3: var(--wikt-palette-scarlet-3);
--wikt-p-4: var(--wikt-palette-scarlet-4);
--wikt-p-5: var(--wikt-palette-scarlet-5);
--wikt-p-6: var(--wikt-palette-scarlet-6);
--wikt-p-7: var(--wikt-palette-scarlet-7);
--wikt-p-8: var(--wikt-palette-scarlet-8);
--wikt-p-9: var(--wikt-palette-scarlet-9);
--wikt-p-g-0: var(--wikt-palette-grey-scarlet-0);
--wikt-p-g-1: var(--wikt-palette-grey-scarlet-1);
--wikt-p-g-2: var(--wikt-palette-grey-scarlet-2);
--wikt-p-g-3: var(--wikt-palette-grey-scarlet-3);
--wikt-p-g-4: var(--wikt-palette-grey-scarlet-4);
--wikt-p-g-5: var(--wikt-palette-grey-scarlet-5);
--wikt-p-g-6: var(--wikt-palette-grey-scarlet-6);
--wikt-p-g-7: var(--wikt-palette-grey-scarlet-7);
--wikt-p-g-8: var(--wikt-palette-grey-scarlet-8);
--wikt-p-g-9: var(--wikt-palette-grey-scarlet-9);
}
[data-wikt-palette-p="orange"] {
--wikt-p-0: var(--wikt-palette-orange-0);
--wikt-p-1: var(--wikt-palette-orange-1);
--wikt-p-2: var(--wikt-palette-orange-2);
--wikt-p-3: var(--wikt-palette-orange-3);
--wikt-p-4: var(--wikt-palette-orange-4);
--wikt-p-5: var(--wikt-palette-orange-5);
--wikt-p-6: var(--wikt-palette-orange-6);
--wikt-p-7: var(--wikt-palette-orange-7);
--wikt-p-8: var(--wikt-palette-orange-8);
--wikt-p-9: var(--wikt-palette-orange-9);
--wikt-p-g-0: var(--wikt-palette-grey-orange-0);
--wikt-p-g-1: var(--wikt-palette-grey-orange-1);
--wikt-p-g-2: var(--wikt-palette-grey-orange-2);
--wikt-p-g-3: var(--wikt-palette-grey-orange-3);
--wikt-p-g-4: var(--wikt-palette-grey-orange-4);
--wikt-p-g-5: var(--wikt-palette-grey-orange-5);
--wikt-p-g-6: var(--wikt-palette-grey-orange-6);
--wikt-p-g-7: var(--wikt-palette-grey-orange-7);
--wikt-p-g-8: var(--wikt-palette-grey-orange-8);
--wikt-p-g-9: var(--wikt-palette-grey-orange-9);
}
[data-wikt-palette-p="amber"] {
--wikt-p-0: var(--wikt-palette-amber-0);
--wikt-p-1: var(--wikt-palette-amber-1);
--wikt-p-2: var(--wikt-palette-amber-2);
--wikt-p-3: var(--wikt-palette-amber-3);
--wikt-p-4: var(--wikt-palette-amber-4);
--wikt-p-5: var(--wikt-palette-amber-5);
--wikt-p-6: var(--wikt-palette-amber-6);
--wikt-p-7: var(--wikt-palette-amber-7);
--wikt-p-8: var(--wikt-palette-amber-8);
--wikt-p-9: var(--wikt-palette-amber-9);
--wikt-p-g-0: var(--wikt-palette-grey-amber-0);
--wikt-p-g-1: var(--wikt-palette-grey-amber-1);
--wikt-p-g-2: var(--wikt-palette-grey-amber-2);
--wikt-p-g-3: var(--wikt-palette-grey-amber-3);
--wikt-p-g-4: var(--wikt-palette-grey-amber-4);
--wikt-p-g-5: var(--wikt-palette-grey-amber-5);
--wikt-p-g-6: var(--wikt-palette-grey-amber-6);
--wikt-p-g-7: var(--wikt-palette-grey-amber-7);
--wikt-p-g-8: var(--wikt-palette-grey-amber-8);
--wikt-p-g-9: var(--wikt-palette-grey-amber-9);
}
[data-wikt-palette-p="yellow"] {
--wikt-p-0: var(--wikt-palette-yellow-0);
--wikt-p-1: var(--wikt-palette-yellow-1);
--wikt-p-2: var(--wikt-palette-yellow-2);
--wikt-p-3: var(--wikt-palette-yellow-3);
--wikt-p-4: var(--wikt-palette-yellow-4);
--wikt-p-5: var(--wikt-palette-yellow-5);
--wikt-p-6: var(--wikt-palette-yellow-6);
--wikt-p-7: var(--wikt-palette-yellow-7);
--wikt-p-8: var(--wikt-palette-yellow-8);
--wikt-p-9: var(--wikt-palette-yellow-9);
--wikt-p-g-0: var(--wikt-palette-grey-yellow-0);
--wikt-p-g-1: var(--wikt-palette-grey-yellow-1);
--wikt-p-g-2: var(--wikt-palette-grey-yellow-2);
--wikt-p-g-3: var(--wikt-palette-grey-yellow-3);
--wikt-p-g-4: var(--wikt-palette-grey-yellow-4);
--wikt-p-g-5: var(--wikt-palette-grey-yellow-5);
--wikt-p-g-6: var(--wikt-palette-grey-yellow-6);
--wikt-p-g-7: var(--wikt-palette-grey-yellow-7);
--wikt-p-g-8: var(--wikt-palette-grey-yellow-8);
--wikt-p-g-9: var(--wikt-palette-grey-yellow-9);
}
[data-wikt-palette-p="lime"] {
--wikt-p-0: var(--wikt-palette-lime-0);
--wikt-p-1: var(--wikt-palette-lime-1);
--wikt-p-2: var(--wikt-palette-lime-2);
--wikt-p-3: var(--wikt-palette-lime-3);
--wikt-p-4: var(--wikt-palette-lime-4);
--wikt-p-5: var(--wikt-palette-lime-5);
--wikt-p-6: var(--wikt-palette-lime-6);
--wikt-p-7: var(--wikt-palette-lime-7);
--wikt-p-8: var(--wikt-palette-lime-8);
--wikt-p-9: var(--wikt-palette-lime-9);
--wikt-p-g-0: var(--wikt-palette-grey-lime-0);
--wikt-p-g-1: var(--wikt-palette-grey-lime-1);
--wikt-p-g-2: var(--wikt-palette-grey-lime-2);
--wikt-p-g-3: var(--wikt-palette-grey-lime-3);
--wikt-p-g-4: var(--wikt-palette-grey-lime-4);
--wikt-p-g-5: var(--wikt-palette-grey-lime-5);
--wikt-p-g-6: var(--wikt-palette-grey-lime-6);
--wikt-p-g-7: var(--wikt-palette-grey-lime-7);
--wikt-p-g-8: var(--wikt-palette-grey-lime-8);
--wikt-p-g-9: var(--wikt-palette-grey-lime-9);
}
[data-wikt-palette-p="green"] {
--wikt-p-0: var(--wikt-palette-green-0);
--wikt-p-1: var(--wikt-palette-green-1);
--wikt-p-2: var(--wikt-palette-green-2);
--wikt-p-3: var(--wikt-palette-green-3);
--wikt-p-4: var(--wikt-palette-green-4);
--wikt-p-5: var(--wikt-palette-green-5);
--wikt-p-6: var(--wikt-palette-green-6);
--wikt-p-7: var(--wikt-palette-green-7);
--wikt-p-8: var(--wikt-palette-green-8);
--wikt-p-9: var(--wikt-palette-green-9);
--wikt-p-g-0: var(--wikt-palette-grey-green-0);
--wikt-p-g-1: var(--wikt-palette-grey-green-1);
--wikt-p-g-2: var(--wikt-palette-grey-green-2);
--wikt-p-g-3: var(--wikt-palette-grey-green-3);
--wikt-p-g-4: var(--wikt-palette-grey-green-4);
--wikt-p-g-5: var(--wikt-palette-grey-green-5);
--wikt-p-g-6: var(--wikt-palette-grey-green-6);
--wikt-p-g-7: var(--wikt-palette-grey-green-7);
--wikt-p-g-8: var(--wikt-palette-grey-green-8);
--wikt-p-g-9: var(--wikt-palette-grey-green-9);
}
[data-wikt-palette-p="teal"] {
--wikt-p-0: var(--wikt-palette-teal-0);
--wikt-p-1: var(--wikt-palette-teal-1);
--wikt-p-2: var(--wikt-palette-teal-2);
--wikt-p-3: var(--wikt-palette-teal-3);
--wikt-p-4: var(--wikt-palette-teal-4);
--wikt-p-5: var(--wikt-palette-teal-5);
--wikt-p-6: var(--wikt-palette-teal-6);
--wikt-p-7: var(--wikt-palette-teal-7);
--wikt-p-8: var(--wikt-palette-teal-8);
--wikt-p-9: var(--wikt-palette-teal-9);
--wikt-p-g-0: var(--wikt-palette-grey-teal-0);
--wikt-p-g-1: var(--wikt-palette-grey-teal-1);
--wikt-p-g-2: var(--wikt-palette-grey-teal-2);
--wikt-p-g-3: var(--wikt-palette-grey-teal-3);
--wikt-p-g-4: var(--wikt-palette-grey-teal-4);
--wikt-p-g-5: var(--wikt-palette-grey-teal-5);
--wikt-p-g-6: var(--wikt-palette-grey-teal-6);
--wikt-p-g-7: var(--wikt-palette-grey-teal-7);
--wikt-p-g-8: var(--wikt-palette-grey-teal-8);
--wikt-p-g-9: var(--wikt-palette-grey-teal-9);
}
[data-wikt-palette-p="cyan"] {
--wikt-p-0: var(--wikt-palette-cyan-0);
--wikt-p-1: var(--wikt-palette-cyan-1);
--wikt-p-2: var(--wikt-palette-cyan-2);
--wikt-p-3: var(--wikt-palette-cyan-3);
--wikt-p-4: var(--wikt-palette-cyan-4);
--wikt-p-5: var(--wikt-palette-cyan-5);
--wikt-p-6: var(--wikt-palette-cyan-6);
--wikt-p-7: var(--wikt-palette-cyan-7);
--wikt-p-8: var(--wikt-palette-cyan-8);
--wikt-p-9: var(--wikt-palette-cyan-9);
--wikt-p-g-0: var(--wikt-palette-grey-cyan-0);
--wikt-p-g-1: var(--wikt-palette-grey-cyan-1);
--wikt-p-g-2: var(--wikt-palette-grey-cyan-2);
--wikt-p-g-3: var(--wikt-palette-grey-cyan-3);
--wikt-p-g-4: var(--wikt-palette-grey-cyan-4);
--wikt-p-g-5: var(--wikt-palette-grey-cyan-5);
--wikt-p-g-6: var(--wikt-palette-grey-cyan-6);
--wikt-p-g-7: var(--wikt-palette-grey-cyan-7);
--wikt-p-g-8: var(--wikt-palette-grey-cyan-8);
--wikt-p-g-9: var(--wikt-palette-grey-cyan-9);
}
[data-wikt-palette-p="blue"] {
--wikt-p-0: var(--wikt-palette-blue-0);
--wikt-p-1: var(--wikt-palette-blue-1);
--wikt-p-2: var(--wikt-palette-blue-2);
--wikt-p-3: var(--wikt-palette-blue-3);
--wikt-p-4: var(--wikt-palette-blue-4);
--wikt-p-5: var(--wikt-palette-blue-5);
--wikt-p-6: var(--wikt-palette-blue-6);
--wikt-p-7: var(--wikt-palette-blue-7);
--wikt-p-8: var(--wikt-palette-blue-8);
--wikt-p-9: var(--wikt-palette-blue-9);
--wikt-p-g-0: var(--wikt-palette-grey-blue-0);
--wikt-p-g-1: var(--wikt-palette-grey-blue-1);
--wikt-p-g-2: var(--wikt-palette-grey-blue-2);
--wikt-p-g-3: var(--wikt-palette-grey-blue-3);
--wikt-p-g-4: var(--wikt-palette-grey-blue-4);
--wikt-p-g-5: var(--wikt-palette-grey-blue-5);
--wikt-p-g-6: var(--wikt-palette-grey-blue-6);
--wikt-p-g-7: var(--wikt-palette-grey-blue-7);
--wikt-p-g-8: var(--wikt-palette-grey-blue-8);
--wikt-p-g-9: var(--wikt-palette-grey-blue-9);
}
[data-wikt-palette-p="indigo"] {
--wikt-p-0: var(--wikt-palette-indigo-0);
--wikt-p-1: var(--wikt-palette-indigo-1);
--wikt-p-2: var(--wikt-palette-indigo-2);
--wikt-p-3: var(--wikt-palette-indigo-3);
--wikt-p-4: var(--wikt-palette-indigo-4);
--wikt-p-5: var(--wikt-palette-indigo-5);
--wikt-p-6: var(--wikt-palette-indigo-6);
--wikt-p-7: var(--wikt-palette-indigo-7);
--wikt-p-8: var(--wikt-palette-indigo-8);
--wikt-p-9: var(--wikt-palette-indigo-9);
--wikt-p-g-0: var(--wikt-palette-grey-indigo-0);
--wikt-p-g-1: var(--wikt-palette-grey-indigo-1);
--wikt-p-g-2: var(--wikt-palette-grey-indigo-2);
--wikt-p-g-3: var(--wikt-palette-grey-indigo-3);
--wikt-p-g-4: var(--wikt-palette-grey-indigo-4);
--wikt-p-g-5: var(--wikt-palette-grey-indigo-5);
--wikt-p-g-6: var(--wikt-palette-grey-indigo-6);
--wikt-p-g-7: var(--wikt-palette-grey-indigo-7);
--wikt-p-g-8: var(--wikt-palette-grey-indigo-8);
--wikt-p-g-9: var(--wikt-palette-grey-indigo-9);
}
[data-wikt-palette-p="purple"] {
--wikt-p-0: var(--wikt-palette-purple-0);
--wikt-p-1: var(--wikt-palette-purple-1);
--wikt-p-2: var(--wikt-palette-purple-2);
--wikt-p-3: var(--wikt-palette-purple-3);
--wikt-p-4: var(--wikt-palette-purple-4);
--wikt-p-5: var(--wikt-palette-purple-5);
--wikt-p-6: var(--wikt-palette-purple-6);
--wikt-p-7: var(--wikt-palette-purple-7);
--wikt-p-8: var(--wikt-palette-purple-8);
--wikt-p-9: var(--wikt-palette-purple-9);
--wikt-p-g-0: var(--wikt-palette-grey-purple-0);
--wikt-p-g-1: var(--wikt-palette-grey-purple-1);
--wikt-p-g-2: var(--wikt-palette-grey-purple-2);
--wikt-p-g-3: var(--wikt-palette-grey-purple-3);
--wikt-p-g-4: var(--wikt-palette-grey-purple-4);
--wikt-p-g-5: var(--wikt-palette-grey-purple-5);
--wikt-p-g-6: var(--wikt-palette-grey-purple-6);
--wikt-p-g-7: var(--wikt-palette-grey-purple-7);
--wikt-p-g-8: var(--wikt-palette-grey-purple-8);
--wikt-p-g-9: var(--wikt-palette-grey-purple-9);
}
[data-wikt-palette-p="magenta"] {
--wikt-p-0: var(--wikt-palette-magenta-0);
--wikt-p-1: var(--wikt-palette-magenta-1);
--wikt-p-2: var(--wikt-palette-magenta-2);
--wikt-p-3: var(--wikt-palette-magenta-3);
--wikt-p-4: var(--wikt-palette-magenta-4);
--wikt-p-5: var(--wikt-palette-magenta-5);
--wikt-p-6: var(--wikt-palette-magenta-6);
--wikt-p-7: var(--wikt-palette-magenta-7);
--wikt-p-8: var(--wikt-palette-magenta-8);
--wikt-p-9: var(--wikt-palette-magenta-9);
--wikt-p-g-0: var(--wikt-palette-grey-magenta-0);
--wikt-p-g-1: var(--wikt-palette-grey-magenta-1);
--wikt-p-g-2: var(--wikt-palette-grey-magenta-2);
--wikt-p-g-3: var(--wikt-palette-grey-magenta-3);
--wikt-p-g-4: var(--wikt-palette-grey-magenta-4);
--wikt-p-g-5: var(--wikt-palette-grey-magenta-5);
--wikt-p-g-6: var(--wikt-palette-grey-magenta-6);
--wikt-p-g-7: var(--wikt-palette-grey-magenta-7);
--wikt-p-g-8: var(--wikt-palette-grey-magenta-8);
--wikt-p-g-9: var(--wikt-palette-grey-magenta-9);
}
[data-wikt-palette-p="rose"] {
--wikt-p-0: var(--wikt-palette-rose-0);
--wikt-p-1: var(--wikt-palette-rose-1);
--wikt-p-2: var(--wikt-palette-rose-2);
--wikt-p-3: var(--wikt-palette-rose-3);
--wikt-p-4: var(--wikt-palette-rose-4);
--wikt-p-5: var(--wikt-palette-rose-5);
--wikt-p-6: var(--wikt-palette-rose-6);
--wikt-p-7: var(--wikt-palette-rose-7);
--wikt-p-8: var(--wikt-palette-rose-8);
--wikt-p-9: var(--wikt-palette-rose-9);
--wikt-p-g-0: var(--wikt-palette-grey-rose-0);
--wikt-p-g-1: var(--wikt-palette-grey-rose-1);
--wikt-p-g-2: var(--wikt-palette-grey-rose-2);
--wikt-p-g-3: var(--wikt-palette-grey-rose-3);
--wikt-p-g-4: var(--wikt-palette-grey-rose-4);
--wikt-p-g-5: var(--wikt-palette-grey-rose-5);
--wikt-p-g-6: var(--wikt-palette-grey-rose-6);
--wikt-p-g-7: var(--wikt-palette-grey-rose-7);
--wikt-p-g-8: var(--wikt-palette-grey-rose-8);
--wikt-p-g-9: var(--wikt-palette-grey-rose-9);
}
[data-wikt-palette-p="brown"] {
--wikt-p-0: var(--wikt-palette-brown-0);
--wikt-p-1: var(--wikt-palette-brown-1);
--wikt-p-2: var(--wikt-palette-brown-2);
--wikt-p-3: var(--wikt-palette-brown-3);
--wikt-p-4: var(--wikt-palette-brown-4);
--wikt-p-5: var(--wikt-palette-brown-5);
--wikt-p-6: var(--wikt-palette-brown-6);
--wikt-p-7: var(--wikt-palette-brown-7);
--wikt-p-8: var(--wikt-palette-brown-8);
--wikt-p-9: var(--wikt-palette-brown-9);
--wikt-p-g-0: var(--wikt-palette-grey-brown-0);
--wikt-p-g-1: var(--wikt-palette-grey-brown-1);
--wikt-p-g-2: var(--wikt-palette-grey-brown-2);
--wikt-p-g-3: var(--wikt-palette-grey-brown-3);
--wikt-p-g-4: var(--wikt-palette-grey-brown-4);
--wikt-p-g-5: var(--wikt-palette-grey-brown-5);
--wikt-p-g-6: var(--wikt-palette-grey-brown-6);
--wikt-p-g-7: var(--wikt-palette-grey-brown-7);
--wikt-p-g-8: var(--wikt-palette-grey-brown-8);
--wikt-p-g-9: var(--wikt-palette-grey-brown-9);
}
[data-wikt-palette-p="grey"] {
--wikt-p-0: var(--wikt-palette-grey-0);
--wikt-p-1: var(--wikt-palette-grey-1);
--wikt-p-2: var(--wikt-palette-grey-2);
--wikt-p-3: var(--wikt-palette-grey-3);
--wikt-p-4: var(--wikt-palette-grey-4);
--wikt-p-5: var(--wikt-palette-grey-5);
--wikt-p-6: var(--wikt-palette-grey-6);
--wikt-p-7: var(--wikt-palette-grey-7);
--wikt-p-8: var(--wikt-palette-grey-8);
--wikt-p-9: var(--wikt-palette-grey-9);
--wikt-p-g-0: var(--wikt-palette-grey-0);
--wikt-p-g-1: var(--wikt-palette-grey-1);
--wikt-p-g-2: var(--wikt-palette-grey-2);
--wikt-p-g-3: var(--wikt-palette-grey-3);
--wikt-p-g-4: var(--wikt-palette-grey-4);
--wikt-p-g-5: var(--wikt-palette-grey-5);
--wikt-p-g-6: var(--wikt-palette-grey-6);
--wikt-p-g-7: var(--wikt-palette-grey-7);
--wikt-p-g-8: var(--wikt-palette-grey-8);
--wikt-p-g-9: var(--wikt-palette-grey-9);
}
t46x6el1qmhny2nrq55ifhccynp1qdz
MediaWiki:Gadget-Edittools.js
8
17953
88908
2024-01-30T19:15:04Z
en>Erutuon
0
incorrect change as these elements are created in the loop
88908
javascript
text/javascript
// <nowiki>
// implicit dependencies: mediawiki.cookie, jquery.textselection
(function charInsertIIFE () {
"use strict";
function CharInsertCookieLegacy(key) {
this.key = key;
}
CharInsertCookieLegacy.prototype = {
get: function () {
return parseInt(mw.cookie.get(this.key), 10) || 0;
},
set: function (value) {
if (typeof value === 'number')
value = value.toString(10);
if (!(typeof value === 'string' && !isNaN(parseInt(value, 10))))
throw new TypeError("Expected string or number");
return mw.cookie.set(this.key, value);
},
};
function CharInsertCookie(key) {
this.key = key;
}
CharInsertCookie.prototype = {
get: function () {
return mw.cookie.get(this.key);
},
set: function (value) {
if (typeof value !== 'string')
throw new TypeError("Expected string");
return mw.cookie.set(this.key, value);
},
};
var charInsertCookieLegacy = new CharInsertCookieLegacy('edittoolscharsubset');
var charInsertCookie = new CharInsertCookie('edittoolscharsubsetname');
/* ===applyCharinserts=== */
/* handle <span class="charinsert"> like <charinsert> */
function applyCharinserts() {
var textbox = $('#wpTextbox1');
textbox.encapsulate = function (left, right) {
return this.textSelection(
'encapsulateSelection', {
pre: left.replace(/^ */, '')
.replace(/\u0640(.)/g, '$1'), // remove ARABIC TATWEEL before Arabic diacritic
peri: '',
post: right
}
);
};
// Must set insertArgs in the element for which this is an event handler.
function clickFunction () {
textbox.encapsulate.apply(textbox, this.insertArgs);
return false;
}
function getInsertArg(string, start, end) {
string = string.substring(start, end);
return string !== ''
? string.replace(/\x22/g, '"')
: string;
}
function makeInsertArgs(string) {
var index = string.indexOf('+');
if (index === -1)
index = string.length;
var left = getInsertArg(string, 0, index);
var right = getInsertArg(string, index + 1);
return [ left, right ];
}
function makeCharInserter(string) {
var charInserter = document.createElement('a');
charInserter.onclick = clickFunction;
charInserter.href = '#';
charInserter.classList.add("charinserter");
var insertArgs = makeInsertArgs(string);
charInserter.insertArgs = insertArgs.map(function(arg) {
return arg
// No-break space ( ) must become an ASCII space in the inserted
// text, but it must be displayed as a no-break space so that combining
// characters are clickable.
.replace(/\xA0/g, ' ')
// Dotted circle can be used as a seat for diacritics
// but shouldn't be inserted into the text box.
.replace(/^\u25CC(.)/, '$1');
});
var visibleText = insertArgs.join('');
charInserter.appendChild(document.createTextNode(visibleText));
return charInserter;
}
function charInsertify(parent) {
if (parent.charInsertified)
return;
parent.charInsertified = true;
// Go through all child nodes of parent.
for (var childNode = parent.firstChild;
childNode !== null;
childNode = childNode.nextSibling) {
if (childNode.nodeType === 1) { // Element node
charInsertify(childNode);
} else if (childNode.nodeType === 3) { // Text node
// There is a newline when the wikitext has a line break
// followed by a character entity reference such as  .
// Remove the newline because we don't want to see multiple spaces.
childNode.nodeValue = childNode.nodeValue
.replace(/^\n+/, '');
var strings = childNode.nodeValue
// Split text content on ASCII whitespace characters
// and en space (U+2002) character references.
.split(/[ \f\n\r\t\v\u2002]+/g)
.filter(function(string) { return string !== ''; });
var addedNew = false;
for (var i = 0; i < strings.length; ++i) {
if (i > 0)
parent.insertBefore(document.createTextNode(' '), childNode);
parent.insertBefore(makeCharInserter(strings[i]), childNode);
addedNew = true;
}
if (addedNew)
parent.removeChild(childNode);
}
}
}
var charInsertSpans = document.querySelectorAll('#editpage-specialchars .charinsert');
Array.prototype.forEach.call(charInsertSpans, charInsertify);
}
function makeCharSubsetMenuText(slug) {
return decodeURIComponent(slug
.replace(/\.([0-9A-F][0-9A-F])/g, '%$1')
.replace(/_/g, '%20'));
}
/* ===addCharSubsetMenu=== */
/* add menu for selecting subsets of secial characters */
function addCharSubsetMenu() {
var edittools = $('#editpage-specialchars');
if (edittools.length === 0) return;
var menu = $('<select>')
.attr("id", 'charSubsetControl').css("display", "inline")
.on("change", function() {
chooseCharSubset($(this).val());
});
var sections = edittools.find('p');
if (sections.length === 0) return;
sections.each(function() {
var slug = ($(this).attr("id") || '').replace(/^edittools-/, '');
$('<option>').text(makeCharSubsetMenuText(slug)).val(slug).appendTo(menu);
});
/* default subset from cookie */
var sectionIndex = charInsertCookieLegacy.get();
var sectionName = charInsertCookie.get();
if (!sectionName) {
sectionName = menu.children()[sectionIndex].value;
}
/* update dropdown control to value of cookie */
menu.val(sectionName);
/* display the subset indicated by the cookie */
chooseCharSubset(sectionName);
edittools.prepend(menu);
}
/* ===chooseCharSubsetMenu=== */
/* select subsection of special characters */
function chooseCharSubset(sectionSlug) {
var sections = $('#editpage-specialchars').find('p');
var idToShow = 'edittools-' + sectionSlug;
for (var i = 0; i < sections.length; i++) {
var style = sections[i].style;
style.display = sections[i].id == idToShow ? 'inline' : 'none';
}
charInsertCookie.set(sectionSlug);
}
$(function() {
var action = mw.config.get('wgAction');
if (window.testNewEditJs || !(action === 'edit' || action === 'submit'
|| $('#editpage-specialchars').length > 0))
return;
if (!window.doNotUseDefaultEditTools) // [[User:Conrad.Irwin/edittools.js]]
addCharSubsetMenu();
applyCharinserts();
// Provide three arguments to add one category;
// provide an array with [{name: categoryName, insertBefore: categoryInsertBefore, html: categoryHtml}]
// to add more than one.
mw.hook('enwiktionary.edittools.addCategory').add(function (categoryName, categoryInsertBefore, categoryHtml) {
var categories = (categoryInsertBefore !== undefined)
? [{name: categoryName, insertBefore: categoryInsertBefore, html: categoryHtml}]
: categoryName;
var edittools = $('#editpage-specialchars');
if (edittools.length === 0) return;
var menu = edittools.find('#charSubsetControl');
if (menu.length === 0) return;
for (var i = 0; i < categories.length; i++) {
var category = categories[i];
var name = category.name,
insertBefore = category.insertBefore,
html = category.html;
name = name.replace(/\s/g, '_');
var selector = insertBefore ? '#charSubsetControl option[value="' + insertBefore.replace('"', '\\"') + '"]' : null;
var option = $('<option>').text(makeCharSubsetMenuText(name)).val(name);
$('<p>').attr('id', 'edittools-' + name).addClass('speciallang').css('display', 'none').html(html).appendTo(edittools);
if (selector && menu.find(selector)) {
option.insertBefore(selector);
} else {
option.appendTo(menu);
}
}
applyCharinserts();
var cookieVal = charInsertCookie.get();
menu.val(cookieVal);
chooseCharSubset(cookieVal);
});
});
})();
// </nowiki>
av4h0w783rxittyviie2wjvdsukc936
88909
88908
2026-08-06T23:11:42Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-Edittools.js]]
88908
javascript
text/javascript
// <nowiki>
// implicit dependencies: mediawiki.cookie, jquery.textselection
(function charInsertIIFE () {
"use strict";
function CharInsertCookieLegacy(key) {
this.key = key;
}
CharInsertCookieLegacy.prototype = {
get: function () {
return parseInt(mw.cookie.get(this.key), 10) || 0;
},
set: function (value) {
if (typeof value === 'number')
value = value.toString(10);
if (!(typeof value === 'string' && !isNaN(parseInt(value, 10))))
throw new TypeError("Expected string or number");
return mw.cookie.set(this.key, value);
},
};
function CharInsertCookie(key) {
this.key = key;
}
CharInsertCookie.prototype = {
get: function () {
return mw.cookie.get(this.key);
},
set: function (value) {
if (typeof value !== 'string')
throw new TypeError("Expected string");
return mw.cookie.set(this.key, value);
},
};
var charInsertCookieLegacy = new CharInsertCookieLegacy('edittoolscharsubset');
var charInsertCookie = new CharInsertCookie('edittoolscharsubsetname');
/* ===applyCharinserts=== */
/* handle <span class="charinsert"> like <charinsert> */
function applyCharinserts() {
var textbox = $('#wpTextbox1');
textbox.encapsulate = function (left, right) {
return this.textSelection(
'encapsulateSelection', {
pre: left.replace(/^ */, '')
.replace(/\u0640(.)/g, '$1'), // remove ARABIC TATWEEL before Arabic diacritic
peri: '',
post: right
}
);
};
// Must set insertArgs in the element for which this is an event handler.
function clickFunction () {
textbox.encapsulate.apply(textbox, this.insertArgs);
return false;
}
function getInsertArg(string, start, end) {
string = string.substring(start, end);
return string !== ''
? string.replace(/\x22/g, '"')
: string;
}
function makeInsertArgs(string) {
var index = string.indexOf('+');
if (index === -1)
index = string.length;
var left = getInsertArg(string, 0, index);
var right = getInsertArg(string, index + 1);
return [ left, right ];
}
function makeCharInserter(string) {
var charInserter = document.createElement('a');
charInserter.onclick = clickFunction;
charInserter.href = '#';
charInserter.classList.add("charinserter");
var insertArgs = makeInsertArgs(string);
charInserter.insertArgs = insertArgs.map(function(arg) {
return arg
// No-break space ( ) must become an ASCII space in the inserted
// text, but it must be displayed as a no-break space so that combining
// characters are clickable.
.replace(/\xA0/g, ' ')
// Dotted circle can be used as a seat for diacritics
// but shouldn't be inserted into the text box.
.replace(/^\u25CC(.)/, '$1');
});
var visibleText = insertArgs.join('');
charInserter.appendChild(document.createTextNode(visibleText));
return charInserter;
}
function charInsertify(parent) {
if (parent.charInsertified)
return;
parent.charInsertified = true;
// Go through all child nodes of parent.
for (var childNode = parent.firstChild;
childNode !== null;
childNode = childNode.nextSibling) {
if (childNode.nodeType === 1) { // Element node
charInsertify(childNode);
} else if (childNode.nodeType === 3) { // Text node
// There is a newline when the wikitext has a line break
// followed by a character entity reference such as  .
// Remove the newline because we don't want to see multiple spaces.
childNode.nodeValue = childNode.nodeValue
.replace(/^\n+/, '');
var strings = childNode.nodeValue
// Split text content on ASCII whitespace characters
// and en space (U+2002) character references.
.split(/[ \f\n\r\t\v\u2002]+/g)
.filter(function(string) { return string !== ''; });
var addedNew = false;
for (var i = 0; i < strings.length; ++i) {
if (i > 0)
parent.insertBefore(document.createTextNode(' '), childNode);
parent.insertBefore(makeCharInserter(strings[i]), childNode);
addedNew = true;
}
if (addedNew)
parent.removeChild(childNode);
}
}
}
var charInsertSpans = document.querySelectorAll('#editpage-specialchars .charinsert');
Array.prototype.forEach.call(charInsertSpans, charInsertify);
}
function makeCharSubsetMenuText(slug) {
return decodeURIComponent(slug
.replace(/\.([0-9A-F][0-9A-F])/g, '%$1')
.replace(/_/g, '%20'));
}
/* ===addCharSubsetMenu=== */
/* add menu for selecting subsets of secial characters */
function addCharSubsetMenu() {
var edittools = $('#editpage-specialchars');
if (edittools.length === 0) return;
var menu = $('<select>')
.attr("id", 'charSubsetControl').css("display", "inline")
.on("change", function() {
chooseCharSubset($(this).val());
});
var sections = edittools.find('p');
if (sections.length === 0) return;
sections.each(function() {
var slug = ($(this).attr("id") || '').replace(/^edittools-/, '');
$('<option>').text(makeCharSubsetMenuText(slug)).val(slug).appendTo(menu);
});
/* default subset from cookie */
var sectionIndex = charInsertCookieLegacy.get();
var sectionName = charInsertCookie.get();
if (!sectionName) {
sectionName = menu.children()[sectionIndex].value;
}
/* update dropdown control to value of cookie */
menu.val(sectionName);
/* display the subset indicated by the cookie */
chooseCharSubset(sectionName);
edittools.prepend(menu);
}
/* ===chooseCharSubsetMenu=== */
/* select subsection of special characters */
function chooseCharSubset(sectionSlug) {
var sections = $('#editpage-specialchars').find('p');
var idToShow = 'edittools-' + sectionSlug;
for (var i = 0; i < sections.length; i++) {
var style = sections[i].style;
style.display = sections[i].id == idToShow ? 'inline' : 'none';
}
charInsertCookie.set(sectionSlug);
}
$(function() {
var action = mw.config.get('wgAction');
if (window.testNewEditJs || !(action === 'edit' || action === 'submit'
|| $('#editpage-specialchars').length > 0))
return;
if (!window.doNotUseDefaultEditTools) // [[User:Conrad.Irwin/edittools.js]]
addCharSubsetMenu();
applyCharinserts();
// Provide three arguments to add one category;
// provide an array with [{name: categoryName, insertBefore: categoryInsertBefore, html: categoryHtml}]
// to add more than one.
mw.hook('enwiktionary.edittools.addCategory').add(function (categoryName, categoryInsertBefore, categoryHtml) {
var categories = (categoryInsertBefore !== undefined)
? [{name: categoryName, insertBefore: categoryInsertBefore, html: categoryHtml}]
: categoryName;
var edittools = $('#editpage-specialchars');
if (edittools.length === 0) return;
var menu = edittools.find('#charSubsetControl');
if (menu.length === 0) return;
for (var i = 0; i < categories.length; i++) {
var category = categories[i];
var name = category.name,
insertBefore = category.insertBefore,
html = category.html;
name = name.replace(/\s/g, '_');
var selector = insertBefore ? '#charSubsetControl option[value="' + insertBefore.replace('"', '\\"') + '"]' : null;
var option = $('<option>').text(makeCharSubsetMenuText(name)).val(name);
$('<p>').attr('id', 'edittools-' + name).addClass('speciallang').css('display', 'none').html(html).appendTo(edittools);
if (selector && menu.find(selector)) {
option.insertBefore(selector);
} else {
option.appendTo(menu);
}
}
applyCharinserts();
var cookieVal = charInsertCookie.get();
menu.val(cookieVal);
chooseCharSubset(cookieVal);
});
});
})();
// </nowiki>
av4h0w783rxittyviie2wjvdsukc936
MediaWiki:Gadget-Edittools.css
8
17954
88910
2022-08-18T20:23:30Z
en>Erutuon
0
Created page with "/* CSS borrowed from Wikipedia's [[w:MediaWiki:Gadget-charinsert-styles.css]]. */ .charinserter { /*background-color: #f9f9f9;*/ /*border: 1px solid #ddd;*/ padding: 1px 4px; }"
88910
css
text/css
/* CSS borrowed from Wikipedia's [[w:MediaWiki:Gadget-charinsert-styles.css]]. */
.charinserter {
/*background-color: #f9f9f9;*/
/*border: 1px solid #ddd;*/
padding: 1px 4px;
}
bksa5383zgxhkcuw7l53pphz0jvw3mm
88911
88910
2026-08-06T23:11:51Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-Edittools.css]]
88910
css
text/css
/* CSS borrowed from Wikipedia's [[w:MediaWiki:Gadget-charinsert-styles.css]]. */
.charinserter {
/*background-color: #f9f9f9;*/
/*border: 1px solid #ddd;*/
padding: 1px 4px;
}
bksa5383zgxhkcuw7l53pphz0jvw3mm
MediaWiki:Gadget-NoAutoCaseRedirect.js
8
17955
88912
2024-01-17T04:27:30Z
en>This, that and the other
0
Created page with "window.disableAutoRedirect = true;"
88912
javascript
text/javascript
window.disableAutoRedirect = true;
24pdvveeqbc3n84c6t533ssk2ibayrv
88913
88912
2026-08-06T23:12:14Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-NoAutoCaseRedirect.js]]
88912
javascript
text/javascript
window.disableAutoRedirect = true;
24pdvveeqbc3n84c6t533ssk2ibayrv
MediaWiki:Gadget-DotsSyntaxHighlighter.js
8
17956
88914
2017-05-13T17:20:26Z
en>-sche
0
88914
javascript
text/javascript
// {{documentation}}
mw.loader.load('//www.mediawiki.org/w/index.php?title=MediaWiki:Gadget-DotsSyntaxHighlighter.js&action=raw&ctype=text/javascript&smaxage=21600&maxage=86400');
erifp41x2kc6dejtdqketyrzpihcwtm
88915
88914
2026-08-06T23:12:31Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-DotsSyntaxHighlighter.js]]
88914
javascript
text/javascript
// {{documentation}}
mw.loader.load('//www.mediawiki.org/w/index.php?title=MediaWiki:Gadget-DotsSyntaxHighlighter.js&action=raw&ctype=text/javascript&smaxage=21600&maxage=86400');
erifp41x2kc6dejtdqketyrzpihcwtm
MediaWiki:Gadget-TranslationAdder.js
8
17957
88916
2026-02-08T07:09:50Z
en>This, that and the other
0
remove special case for nds ([[Wiktionary:Language treatment requests#Merge Dutch Low Saxon (nds-nl) and German Low German (nds-de) into Low German (nds)]])
88916
javascript
text/javascript
// <syntaxhighlight lang="javascript">
// <nowiki>
// implicit dependencies : ext.gadget.Editor,ext.gadget.LegacyScriptsNewNode,mediawiki.cookie,ext.gadget.LanguageUtils,mediawiki.util
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, sub:true */
/* global mw, importScript, importScriptURI, $, AdderWrapper, LangMetadata */
// ******** imported from [[MediaWiki:Gadget-LegacyScriptsNewNode.js]] ********
var newNode = window.newNode = function newNode(tagname) {
var node = document.createElement(tagname);
for (var i = 1; i < arguments.length; i++) {
var argument = arguments[i];
if (typeof argument == 'string') { //Text
node.appendChild(document.createTextNode(argument));
} else if (typeof argument == 'object') {
if (argument instanceof Node) { // If it is a DOM Node
node.appendChild(argument);
} else { // Attributes (hopefully)
for (var j in argument) {
if (j === 'class') { // Classname different because...
node.className = argument[j];
} else if (j == 'style') { // Style is special
node.style.cssText = argument[j];
} else if (typeof argument[j] == 'function') { // Basic event handlers
node.addEventListener(j, argument[j], false);
} else {
node.setAttribute(j, argument[j]); //Normal attributes
}
}
}
}
}
return node;
};
// ****************************************************************************
/**
* Storage of "string" preferences.
*/
function CookiePreferences(context) {
//Repeated calls with the same context should get the same preferences object.
if (arguments.callee[context])
return arguments.callee[context];
else
arguments.callee[context] = this;
var storage = {};
var defaults = {};
/**
* Change the value of a preference and store into a cookie.
*/
this.set = function (name, value) {
if (value === null || storage[name] === value)
return;
storage[name] = value;
updateCookie();
};
/**
* Get the value of a preference from the cookie or default
*
* If the preference isn't set, return the second argument or undefined.
*/
this.get = function (name, def) {
return storage[name] || defaults[name] || def;
};
/**
* let the default for get(name) be value for this session
*/
this.setDefault = function (name, value) {
defaults[name] = value;
};
// Save storage into the cookie, and if available, the local storage.
function updateCookie() {
if ('localStorage' in window && window.localStorage) {
window.localStorage.setItem('TranslationAdderSettings', JSON.stringify(storage));
}
var value = "";
for (var name in storage) {
value += '&' + encodeURIComponent(name) + "=" + encodeURIComponent(storage[name]);
}
mw.cookie.set('preferences' + context, value, {
expires: 30
});
}
// Load storage from the cookie.
// NOTE: If you wish to update the cookie format, both loading and storing
// must continue to work for 30 days.
function updateStorage() {
var value;
if ('localStorage' in window && window.localStorage) {
value = window.localStorage.getItem('TranslationAdderSettings');
if (value) {
try {
var inLocal = JSON.parse(value);
for (var key in inLocal) {
if (inLocal.hasOwnProperty(key)) {
storage[key] = inLocal[key];
}
}
return;
} catch (e) { }
}
}
value = mw.cookie.set('preferences' + context, value, {
expires: 30
}) || '';
var pairs = value.split('&');
for (var i = 1; i < pairs.length; i++) {
var val = pairs[i].split('=');
if (storage[val[0]] === val[1])
continue;
storage[val[0]] = val[1];
}
}
//__init__
updateStorage();
updateCookie(); // update this too
}
var util = {
getVanillaIndexOf: function (str, text, pos) {
function getIndex(string, regex, pos) {
var newPos = string.substring(pos).search(regex);
return newPos === -1 ? -1 : newPos + pos;
}
if (!pos)
pos = 0;
var cpos = 0,
mpos = 0;
tpos = 0,
wpos = 0,
spos = 0;
do {
cpos = text.indexOf('<!--', pos);
mpos = getIndex(text, /\{\{\s*multitrans\s*\|/, pos);
tpos = text.indexOf('{{', pos);
wpos = text.indexOf('<nowiki>', pos);
spos = text.indexOf(str, pos);
pos = Math.min(
cpos == -1 ? Infinity : cpos,
tpos == -1 ? Infinity : tpos,
wpos == -1 ? Infinity : wpos,
spos == -1 ? Infinity : spos
);
if (pos == spos)
return pos == Infinity ? -1 : pos;
else if (pos == cpos)
pos = text.indexOf('-->', pos) + 3;
else if (pos == wpos)
pos = text.indexOf('<\/nowiki>', pos) + 9;
else if (pos == mpos)
pos = getIndex(text, /\|\s*data\s*=/, pos);
else if (pos == tpos)
pos = text.indexOf('}}', pos) + 2;
} while (pos < Infinity);
return -1;
},
validateNoWikisyntax: function (field, nonempty) {
return function (txt, error) {
if (/[\{\}#]/.test(txt))
return error("Please don't use wiki markup ({}#) in the " + field + ".");
if (nonempty && !txt)
return error("Please specify a " + field + ".");
return txt;
};
},
// pos is a position in the line containing the gloss
getWikitextGloss: function (txt, pos) {
var start = txt.lastIndexOf('\n{{trans-top', pos) + 1;
var end = txt.indexOf('\n', pos);
var line = txt.substring(start, end);
var parametersMatch = /\{\{trans-top[^\|\}]*(\|[^\{\}]+)/.exec(line);
var gloss = "";
if (parametersMatch) {
var parameterText = parametersMatch[1];
var parameterRegex = /\|(?:([^\|=]*)=([^\|]+)|([^\|]*))/g;
var parameterMatch;
var index = 0;
while ((parameterMatch = parameterRegex.exec(parameterText))) {
var key = parameterMatch[1],
value = parameterMatch[2],
unnamedValue = parameterMatch[3];
if (key) {
if (key === "1") {
gloss = value;
}
} else {
index++;
if (index === 1) {
gloss = unnamedValue;
}
}
}
}
return gloss;
},
// get [start_pos, end_pos] of position of wikitext for trans_table containing node in text
getTransTable: function (text, node, recursive) {
var gloss = util.getTransGloss(node);
var pos = 0;
var transect = [];
while (pos > -1) {
pos = util.getVanillaIndexOf('{{trans-top', text, pos + 1);
if (pos > -1 && util.matchGloss(util.getWikitextGloss(text, pos), gloss)) {
transect.push(pos);
}
}
if (transect.length > 1) {
var poss = transect;
transect = [];
for (var i = 0; i < poss.length; i++) {
pos = poss[i];
if (util.matchGloss(gloss, util.getWikitextGloss(text, pos))) {
transect.push(pos);
}
}
if (transect.length > 1 && !recursive)
transect = util.tieBreakTransTable(text, transect, node);
}
if (transect.length == 1) {
pos = transect[0];
pos = util.getVanillaIndexOf("\n", text, pos) + 1;
var endpos = text.indexOf('{{trans-bottom}}', pos);
if (endpos > -1 && pos > 0)
return [pos, endpos];
}
console.trace("getTransTable failure; text = %o, node = %o, recursive = %o, gloss = %o", text, node, recursive, gloss);
return false;
},
// try to narrow down the correct poss if multiple matching trans tables
tieBreakTransTable: function (text, poss, node) {
if (node.nodeName == 'DIV') {
while (node && !(node.classList && node.classList.contains('NavFrame')))
node = node.parentNode;
var nodes = node.getElementsByTagName('table');
if (!nodes.length)
return poss;
node = nodes[0];
} else {
while (node && node.nodeName != 'TABLE')
node = node.parentNode;
}
var before_count = 0;
var after_count = 0;
var is_found = false;
$("table.translations").each(function () {
var gloss = util.getTransGloss(this);
if (gloss == "Translations to be checked")
return;
if (this == node) {
is_found = true;
return;
}
var pos = util.getTransTable(text, this, true);
if (pos) {
for (var j = 0; j < poss.length; j++) {
if (poss[j] == pos)
return util.tieBreakTransTable(poss.splice(j, 1), node);
}
} else {
var matched = 0;
for (var j = 0; j < poss.length; j++) {
if (util.matchGloss(util.getWikitextGloss(text, poss[j]), gloss) &&
util.matchGloss(gloss, util.getWikitextGloss(text, poss[j]))) {
matched++;
}
}
if (matched == poss.length) {
if (is_found)
after_count++;
else
before_count++;
}
}
});
if (before_count + 1 + after_count == poss.length)
return [poss[before_count]];
else
return poss;
},
matchGloss: function (line, gloss) {
if (gloss.match(/^ *$/))
return !!(line.match(/\{\{trans-top\|? *\}\}/) || line.match(/^ *$/));
var words = gloss.split(/\W+/);
var pos = 0;
for (var i = 0; i < words.length; i++) {
pos = line.indexOf(words[i], pos);
if (pos == -1)
return false;
}
return pos > -1;
},
// If the translationNode is a NavHead, this goes up to the NavFrame and then
// down to the translation table. If the translationNode is the translation
// table or is inside the translation table, it goes up till it finds the
// translation table. Then it retrieves the data-gloss="..." attribute
// that is provided by {{trans-top}} and the other translation header
// templates.
// This function returns a gloss after it has been transformed by the parser
// into HTML. So the gloss will not match the gloss in the wikitext if the
// gloss in the wikitext contains templates, for instance.
getTransGloss: function(translationNode) {
var node = translationNode;
if (translationNode.nodeType === Node.ELEMENT_NODE
&& translationNode.nodeName === 'DIV'
&& translationNode.classList.contains('NavHead')) {
var navFrame = translationNode.parentNode;
if (!navFrame.classList.contains('NavFrame')) {
console.trace('No NavFrame parent of %o', translationNode);
return '';
}
node = navFrame.getElementsByClassName('translations')[0];
if (!node) {
console.trace('No child of parent of %o with "translations" class', translationNode);
return '';
}
}
while (true) {
if (node.nodeType === Node.ELEMENT_NODE
&& node.nodeName === 'TABLE'
&& node.classList.contains('translations')) {
return node.dataset.gloss;
}
node = node.parentNode;
if (!node || node.id === 'mw-parser-output') break;
}
console.trace('Found no gloss in <table class="translations" data-gloss="..."> at or above %o', translationNode);
return '';
},
isTrreq: function (li) {
var span = li.getElementsByTagName('span')[0];
return (span && span.classList.contains("trreq"));
},
genderList: {
"s": "singular", "d": "dual", "p": "plural",
"m": "masc.", "m-d": "masc. dual", "m-p": "masc. pl.",
"f": "fem.", "f-d": "fem. dual", "f-p": "fem. pl.",
"c": "common", "c-d": "common dual", "c-p": "common pl.",
"n": "neuter", "n-d": "neuter dual", "n-p": "neuter pl.",
"impf": "imperfective", "pf": "perfective"
}
};
// An adder for translations on en.wikt
function TranslationAdders(editor) {
function TranslationLabeller(insertDiv) {
var original_span;
var adder_form;
var initial_value;
var edit_button;
var editing = false;
var adderInterface = {
'fields': {
'gloss': function (txt, error) {
return util.validateNoWikisyntax('gloss', true)(txt, error);
}
},
'createForm': function () {
var thisId = "a" + String(Math.random()).replace(".", "");
adder_form = newNode('form', {
style: 'display: inline',
width: 'auto',
click: kill_event
},
newNode('label', {
'for': thisId
}, "Gloss: "),
newNode('input', {
type: 'text',
name: 'gloss',
value: initial_value,
style: 'width: 50%',
title: 'Insert a summary of the relevant definition',
id: thisId
}),
newNode('input', {
type: 'submit',
name: 'preview',
value: 'Preview'
}),
newNode('a', {
href: '/wiki/Help:Glosses'
}, 'Help?!')
);
return adder_form;
},
'onsubmit': function (values, render) {
render(values.gloss, function (new_html) {
if (editing)
toggle_editing(false);
var old_html = original_span.innerHTML;
editor.addEdit({
'undo': function () {
original_span.innerHTML = old_html;
if (!editing) toggle_editing();
},
'redo': function () {
original_span.innerHTML = new_html;
if (editing) toggle_editing();
},
'edit': function (text) {
return perform_edit(text, values.gloss);
},
'summary': 'tgloss:"' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '...' : values.gloss + '"')
}, original_span);
});
}
};
// The actual modification to the wikitext
function perform_edit(wikitext, gloss) {
var pos = util.getTransTable(wikitext, insertDiv)[0] - 4;
var g_start = wikitext.lastIndexOf('\n{{trans-top', pos) + 1;
var g_end = wikitext.indexOf('}}\n', pos) + 2;
if (g_start === 0 || wikitext.substr(g_start, g_end - g_start).indexOf("\n") > -1) {
editor.error("Could not find translation table.");
return wikitext;
} else {
return wikitext.substr(0, g_start) + '{{trans-top|' + gloss + '}}' + wikitext.substr(g_end);
}
}
// Don't open and close box when interacting with form.
function kill_event(e) {
e.stopPropagation();
}
// What to do when the +/- button is clicked.
function toggle_editing() {
if (editing) {
adder_form.style.display = "none";
original_span.style.display = "inline";
editing = false;
return;
}
editing = true;
edit_button.text("Loading...");
editor.withCurrentText(function (currentText) {
var pos = util.getTransTable(currentText, insertDiv);
edit_button.text('±');
if (!pos)
return editor.error("Could not find translation table");
var gloss_line = currentText.substr(currentText.lastIndexOf('\n', pos[0] - 2) + 1);
gloss_line = gloss_line.substr(0, gloss_line.indexOf('\n'));
initial_value = gloss_line.replace(/^\{\{trans-top(\|(.*)|)\}\}\s*$/, "$2");
if (initial_value.indexOf("\n") > 0)
return editor.error("Internal error: guess spanning multiple lines");
if (!original_span) {
original_span = newNode('span', {
'class': 'wt-edit-recurse'
});
for (var i = 0; i < insertDiv.childNodes.length; i++) {
var child = insertDiv.childNodes[i];
if (child != edit_button[0] && child.className !== 'NavToggle') {
original_span.appendChild(insertDiv.removeChild(child));
i--;
}
}
insertDiv.appendChild(original_span);
new AdderWrapper(editor, adderInterface, insertDiv, original_span);
}
original_span.style.display = "none";
adder_form.style.display = "inline";
adder_form.getElementsByTagName('input')[0].focus();
});
}
edit_button = $('<a href="#">±</a>')
//.addClass("translation-gloss-edit-button")
.attr("title", "Edit table heading").css("padding", "2px").css("margin-left", "-5px")
.on("click", function (e) {
if (e && e.preventDefault)
e.preventDefault();
kill_event(e);
toggle_editing();
return false;
});
$(insertDiv).prepend(edit_button); //XXX: this places anchor before NavToggle which may not be a good idea
}
function TranslationAdder(insertUl) {
// Hippietrail
var langmetadata = new LangMetadata();
this.fields = {
lang: function (txt, error) {
if (txt == 'en')
return error("Please choose a foreign language. (fr, es, aaa)");
if (!/^[a-z]{2,3}(?:-[a-z]{2,3}){0,2}$/.test(txt))
return error("Please use a language code. (fr, es, aaa)");
return txt;
},
word: function (txt, error) {
if (txt == '{{trreq}}')
return '{{t-needed}}';
if (txt == '{{t-needed}}')
return txt;
if (txt.indexOf(',') == -1 || forceComma) {
forceComma = false;
if (langmetadata.expectedCase(thiz.elements.lang.value, mw.config.get('wgTitle'), txt) || forceCase) {
forceCase = false;
return util.validateNoWikisyntax('translation', true)(txt, error);
}
if (prefs.get('case-knowledge', 'none') == 'none') {
return error(newNode('span',
"Translations normally don't have capital letters. If you're certain it does, you can ",
newNode('span', {
style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;",
click: function () {
forceCase = true;
inputForm.onsubmit();
prefs.set('case-knowledge', 'guru');
}
}, "continue by clicking here.")));
} else {
var msg = newNode('span',
newNode('span', {
style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;",
click: function () {
prefs.set('case-knowledge', 'none');
try {
msg.parentNode.removeChild(msg);
} catch (e) {
}
editor.undo();
}
}, "Please click undo"), " unless you are certain that this translation has a capital letter.");
error(msg);
return txt;
}
}
if (prefs.get('comma-knowledge', 'none') == 'none') {
return error(newNode('span',
"You can only add one translation at a time. If this is one translation that contains a comma, you can ",
newNode('span', {
style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;",
click: function () {
forceComma = true;
inputForm.onsubmit();
prefs.set('comma-knowledge', 'guru');
}
}, "add it by clicking here.")));
} else {
var msg = newNode('span',
newNode('span', {
style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;",
click: function () {
prefs.set('comma-knowledge', 'none');
try {
msg.parentNode.removeChild(msg);
} catch (e) {
}
editor.undo();
}
}, "Please click undo"), " if you were trying to create a list of translations in one go, this is currently not supported.");
error(msg);
return txt;
}
},
qual: util.validateNoWikisyntax('qualifier'),
tr: util.validateNoWikisyntax('transcription'),
lit: util.validateNoWikisyntax('literal'),
rawPageName: util.validateNoWikisyntax('raw page name'),
nested: util.validateNoWikisyntax('nested'),
"s": 'checkbox',
"d": 'checkbox',
"p": 'checkbox',
"m": 'checkbox',
"m-d": 'checkbox',
"m-p": 'checkbox',
"f": 'checkbox',
"f-d": 'checkbox',
"f-p": 'checkbox',
"c": 'checkbox',
"c-d": 'checkbox',
"c-p": 'checkbox',
"n": 'checkbox',
"n-d": 'checkbox',
"n-p": 'checkbox',
"impf": 'checkbox',
"pf": 'checkbox',
nclass1: util.validateNoWikisyntax('noun class'),
nclass2: util.validateNoWikisyntax('plural class')
};
this.createForm = function () {
function createGenderNode(name, text) {
var inp = document.createElement("input");
inp.type = "checkbox";
inp.name = name;
var lbl = document.createElement("label");
lbl.appendChild(inp);
lbl.appendChild(document.createTextNode(text));
return lbl;
}
var genderControls = {};
for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]);
var controls = {
lang: newNode('input', {
size: 4,
type: 'text',
name: 'lang',
value: prefs.get('curlang', ''),
title: 'The two or three letter ISO 639 language code'
}),
transliteration: newNode('p', newNode('a', {
href: '/wiki/Wiktionary:Transliteration'
}, "Transliteration"), ": ",
newNode('input', {
name: "tr",
title: "The word transliterated into the Latin alphabet."
}), " (e.g. zìmǔ for 字母)"),
literal: newNode('p', "Literal translation: ",
newNode('input', {
name: "lit",
title: "The literal translation of the term."
})),
qualifier: newNode('p', "Qualifier: ", newNode('input', {
name: 'qual',
title: "A qualifier for the word"
}), " (e.g. literally, formally, slang)"),
display: newNode('p', "Raw page name: ", newNode('input', {
name: 'rawPageName',
title: "A link to lemma, if a translation is not a lemma."
}), " (e.g. 疲れる for 疲れた)"),
nested: newNode('p', "Nesting: ", newNode('input', {
name: 'nested',
title: "The nesting of this language"
}), " (e.g. Serbo-Croatian/Cyrillic)"),
// Genders
genders: genderControls,
// Noun class
nclass: newNode('p',
"Noun class: ", newNode('input', {
type: 'text',
size: 4,
name: 'nclass1',
title: "The noun class of the word in the singular or citation form."
}),
" Plural class: ", newNode('input', {
type: 'text',
size: 4,
name: 'nclass2',
title: "The noun class of the word in the plural form, if any."
}))
};
// suppress unused script field
function createGenderDiv(genderz) {
var $div = $("<div>");
for (var i = 1; i < arguments.length; i++) $div.append(genderz[arguments[i]]);
return $div;
}
var $mdiv = createGenderDiv(controls.genders, "m", "m-d", "m-p");
var $fdiv = createGenderDiv(controls.genders, "f", "f-d", "f-p");
var $cdiv = createGenderDiv(controls.genders, "c", "c-d", "c-p");
var $ndiv = createGenderDiv(controls.genders, "n", "n-d", "n-p");
var $sdpdiv = createGenderDiv(controls.genders, "s", "d", "p");
var $pfdiv = createGenderDiv(controls.genders, "impf", "pf");
controls.gender = $("<p>").append($mdiv).append($fdiv).append($cdiv).append($ndiv).append($sdpdiv).append($pfdiv)[0];
langInput = controls.lang;
var showButton = newNode('span', {
'click': function () {
if (!advancedMode) {
advancedMode = true;
showButton.innerHTML = " Less";
} else {
advancedMode = false;
showButton.innerHTML = " More";
}
updateFieldVisibility.call(langInput, true);
},
'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;"
}, advancedMode ? " Less" : " More");
function autoTransliterate() {
var rawPageName = langmetadata.computeRawPageName(thiz.elements.lang.value, thiz.elements.word.value);
if (rawPageName && rawPageName !== thiz.elements.word.value)
thiz.elements.rawPageName.value = rawPageName;
}
function updateFieldVisibility(preserve) {
preserve = (preserve === true);
//show all arguments
function show() {
for (var i = 0; i < arguments.length; i++) {
if (arguments[i].nodeName == 'P')
arguments[i].style.display = "block";
else
arguments[i].style.display = "inline";
}
}
//hide all arguments
function hide() {
for (var i = 0; i < arguments.length; i++)
arguments[i].style.display = "none";
}
//if the first argument is false hide the remaining arguments, otherwise show them.
function toggle(condition) {
if (condition) //eww...
show.apply(this, [].splice.call(arguments, 1, arguments.length - 1));
else
hide.apply(this, [].splice.call(arguments, 1, arguments.length - 1));
}
if (!preserve)
langInput.value = langmetadata.cleanLangCode(langInput.value);
var scriptGuess = langmetadata.guessScript(langInput.value || '');
if (!preserve) {
thiz.elements.nested.value = langmetadata.getNested(langInput.value || '');
thiz.elements.nested.dataset.initialValue = thiz.elements.nested.value;
autoTransliterate();
}
var lang = langInput.value;
if (!advancedMode) {
var g = langmetadata.getGenders(lang);
if (!lang) {
hide(controls.gender);
}
else if (g === undefined) {
hide(controls.gender);
}
else {
for (var genderName in util.genderList) {
toggle(g.indexOf(genderName) != -1, controls.genders[genderName]);
}
}
toggle(g, controls.gender);
toggle(langmetadata.hasNounClasses(lang), controls.nclass);
var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang);
toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration);
toggle(langmetadata.needsRawPageName(lang), controls.display);
hide(controls.literal, controls.qualifier, controls.nested); //only in more
} else {
show(
controls.gender,
controls.genders['s'], controls.genders['d'], controls.genders['p'],
controls.genders['m'], controls.genders['m-d'], controls.genders['m-p'],
controls.genders['f'], controls.genders['f-d'], controls.genders['f-p'],
controls.genders['c'], controls.genders['c-d'], controls.genders['c-p'],
controls.genders['n'], controls.genders['n-d'], controls.genders['n-p'],
controls.genders['impf'], controls.genders['pf'],
controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display,
controls.nested);
}
}
//autocomplete language names
function langAutoFill(e) {
e = (e || event).keyCode;
if ((e >= 33 && e <= 40) || e == 8 || e == 46 || e == 27 || e == 16) {
return;
}
var t = this,
langPrefix = t.value;
if (langPrefix.substr(0, 1) != langPrefix.substr(0, 1).toUpperCase()) {
return;
}
new mw.Api().get({
"action": "expandtemplates",
"format": "json",
"text": "{{#invoke:languages/javascript-interface|GetSingleLanguageByLanguagePrefix|" + langPrefix + "}}",
"prop": "wikitext"
}).done(function (r) {
if (r.expandtemplates && r.expandtemplates.wikitext !== "") {
var langcode = r.expandtemplates.wikitext.split(":")[0];
var langname = r.expandtemplates.wikitext.split(":")[1];
var oldLength = t.value.length;
t.value = langname;
$(t).data("langcode", langcode);
if (t.setSelectionRange) {
t.setSelectionRange(oldLength, langname.length);
} else if (t.createTextRange) {
var z = t.createTextRange();
z.moveEnd('character', 0 - z.move('character', [t.value.length, t.value = langname][0]) + t.value.length);
z.select();
}
} else {
$(t).removeData("langcode");
}
});
}
langInput.onkeyup = langAutoFill;
langInput.onblur = function () {
if ($(this).data("langcode"))
$(this).val($(this).data("langcode"));
updateFieldVisibility.call(langInput);
};
window.setTimeout(function () {
updateFieldVisibility.call(langInput);
}, 0);
inputForm = newNode('form',
newNode('p', newNode('a', {
href: "/wiki/MediaWiki:Gadget-TranslationAdder.js/help#Usage"
}, "Add translation"), ' ',
langInput, newNode('b', ': '), newNode('input', {
'name': 'word',
size: 20,
keyup: autoTransliterate,
change: autoTransliterate
}),
newNode('input', {
'type': 'submit',
'value': 'Preview translation'
}), showButton
),
controls.gender,
controls.nclass,
controls.transliteration,
controls.literal,
controls.display,
controls.qualifier,
controls.nested
);
return inputForm;
};
this.onsubmit = function (values, render) {
var wikitext;
function callRender() {
render(wikitext, function (html) {
registerEdits(values, wikitext, html);
});
}
// Keep track of whether the "nested" field has changed
// so that we can tag the edit
values.nestedChanged = inputForm.nested.dataset.initialValue !== values.nested;
var languagePrefix = '{{subst:#invoke:languages/templates|getByCodeAllowEtym|' + values.lang + '|getCanonicalName}}: ';
if (values.word.indexOf('{{t-needed') === 0) {
wikitext = languagePrefix + ' {{t-needed|' + values.lang + '}}';
callRender();
} else {
langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) {
if (needsRawPageName) {
values.rawPageName = rawPageName;
values.wordMinus = '';
} else {
values.rawPageName = '';
values.wordMinus = rawPageName;
}
langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) {
var templateBody = '|' + values.lang + '|' +
langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) +
(values.nclass1 ? '|c' + values.nclass1 : '') +
(values.nclass2 ? '|c' + values.nclass2 : '');
// Add gender codes
templateBody += [
's', 'd', 'p', 'm', 'm-d', 'm-p', 'f', 'f-d', 'f-p',
'c', 'c-d', 'c-p', 'n', 'n-d', 'n-p', 'impf', 'pf'
].map(function(k) {
return values[k] ? '|' + k : '';
}).join("");
templateBody +=
(values.tr ? '|tr=' + values.tr : '') +
(values.lit ? '|lit=' + values.lit : '') +
(values.rawPageName ? '|alt=' + values.word : '');
var qualifier = values.qual ? ' {{q|' + values.qual + '}}' : '';
wikitext = languagePrefix + "{{" + (hasInterwiki ? "t+" : "t") + templateBody + "}}" + qualifier;
callRender();
});
});
}
};
var thiz = this;
var prefs = new CookiePreferences('EditorJs');
var langInput;
var inputForm;
var advancedMode = prefs.get('more-display', 'none') != 'none';
var forceComma = false;
var forceCase = false;
//Reset elements to default values.
function resetElements() {
if (prefs.get('more-display', 'none') != advancedMode ? 'block' : 'none')
prefs.set('more-display', advancedMode ? 'block' : 'none'); //named for compatibility
thiz.elements.word.value = thiz.elements.nclass1.value = thiz.elements.nclass2.value = thiz.elements.lit.value = thiz.elements.tr.value = thiz.elements.rawPageName.value = thiz.elements.qual.value = '';
thiz.elements['s'].checked = false;
thiz.elements['d'].checked = false;
thiz.elements['p'].checked = false;
thiz.elements['m'].checked = false;
thiz.elements['m-d'].checked = false;
thiz.elements['m-p'].checked = false;
thiz.elements['f'].checked = false;
thiz.elements['f-d'].checked = false;
thiz.elements['f-p'].checked = false;
thiz.elements['c'].checked = false;
thiz.elements['c-d'].checked = false;
thiz.elements['c-p'].checked = false;
thiz.elements['n'].checked = false;
thiz.elements['n-d'].checked = false;
thiz.elements['n-p'].checked = false;
thiz.elements['impf'].checked = false;
thiz.elements['pf'].checked = false;
prefs.set('curlang', thiz.elements.lang.value);
}
// This is onsubmit after the wikitext has been rendered to give content
function registerEdits(values, wikitext, content) {
var li = newNode('li', {
'class': 'trans-' + values.lang
});
li.innerHTML = content;
var lang = getLangName(li);
var x = langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word);
var resp = x;
var langCodeBad = false;
if (x.indexOf("{{") < 0) {
new mw.Api().get({
"action": "expandtemplates",
"format": "json",
"text": "{{ll|" + values.lang + "|" + x + "}}",
"prop": "wikitext"
}, {"async": false}).done(function (r) {
if (r.expandtemplates && r.expandtemplates.wikitext) {
// [[page#Language|display]]
// resp = r.expandtemplates.wikitext;
// [[page|display]]
// resp = r.expandtemplates.wikitext.replace(/#[^|]*/, "");
// [[page]]
resp = r.expandtemplates.wikitext.replace(/#[^\]]*/, "");
try {
if (DOMParser && new DOMParser().parseFromString(r.expandtemplates.wikitext, "text/html").querySelector(".scribunto-error"))
langCodeBad = true;
} catch (e) { }
}
}).fail(function(r) {
console.error(r);
resp = resp.includes("[[") ? resp : "[[" + resp + "]]";
});
}
if (langCodeBad)
return editor.error("Unrecognized language code '" + values.lang + "'");
var summary = 't+' + values.lang + ':' + resp;
var insertBefore = null;
var nextLanguage = null;
var nestedHeading, nestedLang;
var nested = values.nested.split('/');
if (nested.length > 1) {
nestedHeading = nested[0];
nestedLang = (nested[1] === 'Roman' ? 'Latin' : nested[1]); // [[Wiktionary:Beer parlour/2026/January#sorting and reformating translation tables]]
if (nestedHeading === '')
nestedHeading = lang;
content = content.replace(/.*: /, nestedLang + ": ");
wikitext = wikitext.replace("subst:", "").replace(/.*: /, nestedLang + ": ");
} else {
nestedHeading = values.nested;
nestedLang = lang;
}
var nestedWikitext = "\n* " + nestedHeading + ":\n*: " + wikitext;
function addEdit(edit, span) {
var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"];
editor.addEdit({
'undo': function () {
edit.undo();
if (thiz.elements.word.value === "" &&
thiz.elements.nclass1.value === "" &&
thiz.elements.nclass2.value === "" &&
thiz.elements.tr.value === "" &&
thiz.elements.lit.value === "" &&
thiz.elements.rawPageName.value === "" &&
thiz.elements.qual.value === "") {
for (var i = 0; i < fields.length; i++) {
thiz.elements[fields[i]].value = values[fields[i]];
}
for (var genderName in util.genderList) {
thiz.elements[genderName].checked = values[genderName];
}
}
},
'redo': function () {
edit.redo();
for (var i = 0; i < fields.length; i++) {
if (thiz.elements[fields[i]].value != values[fields[i]])
return;
}
resetElements();
},
'edit': edit.edit,
'tags': [values.nestedChanged ? 'translation-adder-custom-nesting' : null],
'summary': summary
}, span);
}
// Compare language names without accents and punctuation marks
var normalizeAndStrip = s => s.normalize("NFD").replace(/[\u0300-\u036f'ʼ", -]/g, '').toUpperCase();
var compare = function(a, b) {
if (normalizeAndStrip(a) < normalizeAndStrip(b)) {
return -1;
}
if (normalizeAndStrip(a) > normalizeAndStrip(b)) {
return 1;
}
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
}
if (lang) {
//Get all li's in this table row.
var lss = $(insertUl).parent().parent().find('li').get();
var dds = $(insertUl).parent().parent().find('dd').get();
var lis = lss.concat(dds);
for (var j = 0; j < lis.length; j++) {
if (lis[j].getElementsByTagName('form').length > 0)
continue;
var ln = getLangName(lis[j]);
if (ln == lang) {
if (values.word == '{{t-needed}}') {
addEdit({
'redo': function () {
},
'undo': function () {
},
'edit': function () {
return editor.error("Can not add a translation request for a language with translations");
}
});
return resetElements();
}
var span = newNode('span');
var parent = lis[j];
if (util.isTrreq(parent)) {
span.innerHTML = content.substr(content.indexOf(':') + 1);
var trspan = parent.getElementsByTagName('span')[0];
addEdit({
'redo': function () {
parent.removeChild(trspan);
parent.appendChild(span);
},
'undo': function () {
parent.removeChild(span);
parent.appendChild(trspan);
},
'edit': getEditFunction(values, wikitext, ln, values.lang, true, function (text, ipos) {
//Converting a Translation request into a translation
var lineend = text.indexOf('\n', ipos);
return text.substr(0, ipos) + wikitext + text.substr(lineend);
})
}, span);
return resetElements();
} else if (parent.tagName !== 'DD' || lang !== 'Latin') {
if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) {
span.innerHTML = ", " + content.substr(content.indexOf(':') + 1);
addEdit({
'redo': function () {
parent.appendChild(span)
},
'undo': function () {
parent.removeChild(span)
},
'edit': getEditFunction(values, wikitext, ln, values.lang, false, function (text, ipos) {
//We are adding the wikitext to a list of translations that already exists.
var lineend = text.indexOf('\n', ipos);
var wt = wikitext.replace('subst:#invoke:', '');
wt = wt.substr(wt.indexOf(':') + 1);
return text.substr(0, lineend) + "," + wt + text.substr(lineend);
})
}, span);
return resetElements();
} else {
var node = parent.firstChild;
var hastrans = false;
while (node) {
if (node.nodeType == 1) {
var nn = node.nodeName;
if (nn == 'UL' || nn == 'DL') {
// If we want to use the dialectical nesting for orthographical nesting
// then we need to skip this (otherwise perfect) match.
if (!hastrans && nestedHeading == ln) {
node = node.nextSibling;
continue;
}
span.innerHTML = (hastrans ? ", " : " ") + content.substr(content.indexOf(':') + 1);
addEdit({
'redo': function () {
parent.insertBefore(span, node)
},
'undo': function () {
parent.removeChild(span)
},
'edit': getEditFunction(values, wikitext, ln, values.lang, false, function (text, ipos) {
//Adding the translation to a language that has nested translations under it
var lineend = text.indexOf('\n', ipos);
var wt = wikitext.replace('subst:#invoke:', '');
wt = wt.substr(wt.indexOf(':') + 1);
return text.substr(0, lineend) + (hastrans ? "," : "") + wt + text.substr(lineend);
})
}, span);
return resetElements();
} else {
hastrans = true;
}
}
node = node.nextSibling;
}
}
}
} else if (ln && compare(ln, lang) > 0 && (!nextLanguage || compare(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') {
nextLanguage = ln;
var parent = lis[j];
insertBefore = [{
'redo': function () {
parent.parentNode.insertBefore(li, parent);
},
'undo': function () {
parent.parentNode.removeChild(li)
},
'edit': getEditFunction(values, wikitext, ln, getLangCode(parent), util.isTrreq(parent), function (text, ipos) {
//Adding a new language's translation before another language's translation
var lineend = text.lastIndexOf('\n', ipos);
return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend);
})
}, li];
}
}
}
if (values.nested) {
nextLanguage = null;
insertBefore = null;
li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>";
var lis = insertUl.parentNode.parentNode.getElementsByTagName('li');
for (var j = 0; j < lis.length; j++) {
//Ignore the editor form
if (lis[j].getElementsByTagName('form').length > 0)
continue;
//Don't look at nested translations
if (lis[j].parentNode.parentNode.nodeName != 'TD')
continue;
var ln = getLangName(lis[j]);
if (ln == nestedHeading) {
var sublis = lis[j].getElementsByTagName('li');
if (!sublis.length)
sublis = lis[j].getElementsByTagName('dd');
if (sublis.length == 0) {
var parent = lis[j];
var dd = newNode('dd', {
'class': 'trans-' + values.lang
});
var dl = newNode('dl', dd);
dd.innerHTML = content;
addEdit({
'redo': function () {
parent.appendChild(dl);
},
'undo': function () {
parent.removeChild(dl);
},
'edit': getEditFunction(values, wikitext, nestedHeading, getLangCode(parent), util.isTrreq(parent), function (text, ipos) {
//Adding a new dl to an existing translation line
var lineend = text.indexOf('\n', ipos);
return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend);
})
}, dd);
return resetElements();
} else {
var dd = newNode(sublis[0].nodeName, {
'class': 'trans-' + values.lang
});
var linestart = dd.nodeName == 'DD' ? '\n*: ' : '\n** ';
dd.innerHTML = content;
for (var k = 0; k < sublis.length; k++) {
var subln = getLangName(sublis[k]);
var parent = sublis[k];
if (subln == nestedLang) {
var span = newNode('span');
span.innerHTML = ", " + content.substr(content.indexOf(':') + 1);
addEdit({
'redo': function () {
parent.appendChild(span)
},
'undo': function () {
parent.removeChild(span)
},
'edit': getEditFunction(values, wikitext, nestedLang, values.lang, false, function (text, ipos) {
// Adding the wikitext to a list of translations that already exists.
var lineend = text.indexOf('\n', ipos);
var wt = wikitext.replace('subst:#invoke:', '');
wt = wt.substr(wt.indexOf(':') + 1);
return text.substr(0, lineend) + "," + wt + text.substr(lineend);
})
}, span);
return resetElements();
} else if (langmetadata.nestsBefore(nestedLang, subln)) {
addEdit({
'redo': function () {
parent.parentNode.insertBefore(dd, parent);
},
'undo': function () {
parent.parentNode.removeChild(dd);
},
'edit': getEditFunction(values, wikitext, subln, getLangCode(parent), util.isTrreq(parent),
function (text, ipos) {
// Adding a nested translation in-order
var lineend = text.lastIndexOf('\n', ipos);
return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend);
})
}, dd);
return resetElements();
}
}
addEdit({
'redo': function () {
parent.parentNode.appendChild(dd);
},
'undo': function () {
parent.parentNode.removeChild(dd);
},
'edit': getEditFunction(values, wikitext, subln, getLangCode(parent), util.isTrreq(parent),
function (text, ipos) {
// Adding a nested translation at the end of its group
var lineend = text.indexOf('\n', ipos);
return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend);
})
}, dd);
return resetElements();
}
} else if (ln && compare(ln, nestedHeading) > 0 && (!nextLanguage || compare(ln, nextLanguage) < 0)) {
nextLanguage = ln;
var parent = lis[j];
insertBefore = [{
'redo': function () {
parent.parentNode.insertBefore(li, parent);
},
'undo': function () {
parent.parentNode.removeChild(li)
},
'edit': getEditFunction(values, wikitext, ln, getLangCode(parent), util.isTrreq(parent), function (text, ipos) {
//Adding a new nested translation section.
var lineend = text.lastIndexOf('\n', ipos);
return text.substr(0, lineend) + nestedWikitext + text.substr(lineend);
})
}, li];
}
}
wikitext = nestedHeading + ":\n*: " + wikitext;
}
li.className = "trans-" + values.lang;
if (insertBefore) {
addEdit(insertBefore[0], insertBefore[1]);
} else {
//Append the translations to the end (no better way found)
addEdit({
'redo': function () {
insertUl.appendChild(li);
},
'undo': function () {
insertUl.removeChild(li)
},
'edit': getEditFunction(values, wikitext)
}, li);
}
return resetElements();
}
//Get the wikitext modification for the current form submission.
function getEditFunction(values, wikitext, findLanguage, findLangCode, trreq, callback) {
return function (text) {
var p = util.getTransTable(text, insertUl);
if (!p)
return editor.error("Could not find translation table for '"
+ values.lang + ":" + values.word
+ "'. Glosses should be unique");
var stapos = p[0];
var endpos = p[1];
if (findLanguage) {
var ipos = 0;
if (trreq) {
ipos = text.substr(stapos).search(new RegExp(mw.util.escapeRegExp(findLanguage) + ":\\s*\\{\\{t-needed(?:\\|" + findLangCode + "}})?")) + stapos;
if (ipos < stapos || ipos > endpos)
ipos = text.indexOf('{{trreq|' + findLanguage + '}}', stapos);
if (ipos < stapos || ipos > endpos)
ipos = text.indexOf('{{trreq|' + findLangCode + '}}', stapos);
}
// If we have a nested trreq, then we still need to look for the non-trreq form of the heading language
if (!trreq || ipos < stapos || ipos > endpos) {
var escapedLang = mw.util.escapeRegExp(findLanguage);
//if the translation should not be nested it should not go before any nested language
var regexByShouldFindSubLanguage = values.nested != "" ? "[:*]?" : "";
var possibleRegexes = [
RegExp("\\*" + regexByShouldFindSubLanguage + " ?\\[\\[" + escapedLang + "\\]\\]:"),
RegExp("\\*" + regexByShouldFindSubLanguage + " ?" + escapedLang + ":"),
RegExp("\\*" + regexByShouldFindSubLanguage + " ?\\{\\{ttbc\\|" + escapedLang + "}}:")
];
ipos = text.substr(stapos).search(possibleRegexes[0]) + stapos;
if (ipos < stapos || ipos > endpos)
ipos = text.substr(stapos).search(possibleRegexes[1]) + stapos;
if (ipos < stapos || ipos > endpos)
ipos = text.substr(stapos).search(possibleRegexes[2]) + stapos;
if (ipos < stapos || ipos > endpos)
ipos = text.indexOf('{{subst:#invoke:languages/templates|getByCodeAllowEtym|' + findLangCode + '|getCanonicalName}}:', stapos);
}
if (ipos >= stapos && ipos < endpos) {
return callback(text, ipos, trreq);
} else {
return editor.error("Could not find translation entry for '" + values.lang + ":" + values.word + "'. Please reformat");
}
}
return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos);
};
}
// For an <li> in well-formed translation sections, return the language name.
function getLangName(li) {
var guess = li.textContent || li.innerText;
if (guess)
guess = guess.substr(0, guess.indexOf(':'));
if (guess == 'Template') {
return false;
}
return guess.replace(/^[\s\n]*/, '');
}
// Try to get the language code from an <li> containing {{t}}, {{t+}} or {{t-}}
function getLangCode(li) {
if (li.className.indexOf('trans-') === 0)
return li.className.substr(6);
var spans = li.getElementsByTagName('span');
for (var i = 0; i < spans.length; i++) {
if (spans[i].lang) {
return spans[i].lang;
}
if (spans[i].dataset && spans[i].dataset.lang) {
return spans[i].dataset.lang;
}
}
return false;
}
}
function createTranslationAdderRow(li) {
var $ul = $("<ul>").append(li);
return $("<tr>")
.append($("<td>"))
.append($("<td>"))
.append($("<td>").css("text-align", "left").append($ul));
}
$('table.translations').each(function () {
if (util.getTransGloss(this) != 'Translations to be checked') {
var tdSelector = "td:not(.mw-hiero-table td):not(.targeted-translations-select)";
var uls = $(this).find(tdSelector + " > ul").get();
if (uls.length == 0) {
$(this).find(tdSelector)[0].appendChild(newNode('ul'));
uls = this.getElementsByTagName('ul');
}
if (uls.length == 1) {
var td = $(this).find(tdSelector)[2];
if (td) {
td.appendChild(newNode('ul'));
uls = this.getElementsByTagName('ul');
}
}
if (uls) {
var li = document.createElement('li');
var ul = uls[uls.length - 1];
$(this).append(createTranslationAdderRow(li));
new AdderWrapper(editor, new TranslationAdder(ul), li);
if (true) {// XXX: why not? (new CookiePreferences('EditorJs')).get('labeller') == 'true') {
var div = $(this).parent().parent().find('div.NavHead')[0];
if (div) {
new TranslationLabeller(div)
}
}
}
}
});
}
$(function () {
// Check if we are on a sensible page
var isOnPrintable = mw.util.getParamValue("printable") !== null;
var isDiff = mw.util.getParamValue("diff") !== null || mw.util.getParamValue("oldid") !== null;
if (mw.config.get('wgAction') != 'view' || isOnPrintable || isDiff)
return;
var editor = new Editor();
TranslationAdders(editor);
/*
// Check that we have not been disabled
var prefs = new CookiePreferences('EditorJs');
if (prefs.get('enabled', 'true') == 'true') {
if (!window.loadedEditor) {
prefs.setDefault('labeller', mw.config.get('wgUserName') ? 'true' : 'false');
window.loadedEditor = true;
}
}
// The enable-disable button on WT:EDIT
var node = document.getElementById('editor-js-disable-button');
if (node) {
node.innerHTML = "";
var toggle = newNode('span', {
click: function() {
if (prefs.get('enabled', 'true') == 'true') {
toggle.innerHTML = "Enable";
prefs.set('enabled', 'false');
} else {
toggle.innerHTML = "Disable";
prefs.set('enabled', 'true');
}
}
}, (prefs.get('enabled', 'true') == 'true' ? 'Disable' : 'Enable'))
node.appendChild(toggle);
}
var node = document.getElementById("editor-js-labeller-button");
if (node) {
node.innerHTML = "";
var toggle2 = newNode('span', {
click: function() {
if (prefs.get('labeller') == 'true') {
toggle2.innerHTML = "Enable";
prefs.set('labeller', 'false');
} else {
toggle2.innerHTML = "Disable";
prefs.set('labeller', 'true');
}
}
}, (prefs.get('labeller') == 'true' ? 'Disable' : 'Enable'))
node.appendChild(toggle2);
}*/
});
//</nowiki>
// </syntaxhighlight>
fenbzaedj1d7qphc980f0ohcrd0z8qg
88917
88916
2026-08-06T23:12:43Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-TranslationAdder.js]]
88916
javascript
text/javascript
// <syntaxhighlight lang="javascript">
// <nowiki>
// implicit dependencies : ext.gadget.Editor,ext.gadget.LegacyScriptsNewNode,mediawiki.cookie,ext.gadget.LanguageUtils,mediawiki.util
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, sub:true */
/* global mw, importScript, importScriptURI, $, AdderWrapper, LangMetadata */
// ******** imported from [[MediaWiki:Gadget-LegacyScriptsNewNode.js]] ********
var newNode = window.newNode = function newNode(tagname) {
var node = document.createElement(tagname);
for (var i = 1; i < arguments.length; i++) {
var argument = arguments[i];
if (typeof argument == 'string') { //Text
node.appendChild(document.createTextNode(argument));
} else if (typeof argument == 'object') {
if (argument instanceof Node) { // If it is a DOM Node
node.appendChild(argument);
} else { // Attributes (hopefully)
for (var j in argument) {
if (j === 'class') { // Classname different because...
node.className = argument[j];
} else if (j == 'style') { // Style is special
node.style.cssText = argument[j];
} else if (typeof argument[j] == 'function') { // Basic event handlers
node.addEventListener(j, argument[j], false);
} else {
node.setAttribute(j, argument[j]); //Normal attributes
}
}
}
}
}
return node;
};
// ****************************************************************************
/**
* Storage of "string" preferences.
*/
function CookiePreferences(context) {
//Repeated calls with the same context should get the same preferences object.
if (arguments.callee[context])
return arguments.callee[context];
else
arguments.callee[context] = this;
var storage = {};
var defaults = {};
/**
* Change the value of a preference and store into a cookie.
*/
this.set = function (name, value) {
if (value === null || storage[name] === value)
return;
storage[name] = value;
updateCookie();
};
/**
* Get the value of a preference from the cookie or default
*
* If the preference isn't set, return the second argument or undefined.
*/
this.get = function (name, def) {
return storage[name] || defaults[name] || def;
};
/**
* let the default for get(name) be value for this session
*/
this.setDefault = function (name, value) {
defaults[name] = value;
};
// Save storage into the cookie, and if available, the local storage.
function updateCookie() {
if ('localStorage' in window && window.localStorage) {
window.localStorage.setItem('TranslationAdderSettings', JSON.stringify(storage));
}
var value = "";
for (var name in storage) {
value += '&' + encodeURIComponent(name) + "=" + encodeURIComponent(storage[name]);
}
mw.cookie.set('preferences' + context, value, {
expires: 30
});
}
// Load storage from the cookie.
// NOTE: If you wish to update the cookie format, both loading and storing
// must continue to work for 30 days.
function updateStorage() {
var value;
if ('localStorage' in window && window.localStorage) {
value = window.localStorage.getItem('TranslationAdderSettings');
if (value) {
try {
var inLocal = JSON.parse(value);
for (var key in inLocal) {
if (inLocal.hasOwnProperty(key)) {
storage[key] = inLocal[key];
}
}
return;
} catch (e) { }
}
}
value = mw.cookie.set('preferences' + context, value, {
expires: 30
}) || '';
var pairs = value.split('&');
for (var i = 1; i < pairs.length; i++) {
var val = pairs[i].split('=');
if (storage[val[0]] === val[1])
continue;
storage[val[0]] = val[1];
}
}
//__init__
updateStorage();
updateCookie(); // update this too
}
var util = {
getVanillaIndexOf: function (str, text, pos) {
function getIndex(string, regex, pos) {
var newPos = string.substring(pos).search(regex);
return newPos === -1 ? -1 : newPos + pos;
}
if (!pos)
pos = 0;
var cpos = 0,
mpos = 0;
tpos = 0,
wpos = 0,
spos = 0;
do {
cpos = text.indexOf('<!--', pos);
mpos = getIndex(text, /\{\{\s*multitrans\s*\|/, pos);
tpos = text.indexOf('{{', pos);
wpos = text.indexOf('<nowiki>', pos);
spos = text.indexOf(str, pos);
pos = Math.min(
cpos == -1 ? Infinity : cpos,
tpos == -1 ? Infinity : tpos,
wpos == -1 ? Infinity : wpos,
spos == -1 ? Infinity : spos
);
if (pos == spos)
return pos == Infinity ? -1 : pos;
else if (pos == cpos)
pos = text.indexOf('-->', pos) + 3;
else if (pos == wpos)
pos = text.indexOf('<\/nowiki>', pos) + 9;
else if (pos == mpos)
pos = getIndex(text, /\|\s*data\s*=/, pos);
else if (pos == tpos)
pos = text.indexOf('}}', pos) + 2;
} while (pos < Infinity);
return -1;
},
validateNoWikisyntax: function (field, nonempty) {
return function (txt, error) {
if (/[\{\}#]/.test(txt))
return error("Please don't use wiki markup ({}#) in the " + field + ".");
if (nonempty && !txt)
return error("Please specify a " + field + ".");
return txt;
};
},
// pos is a position in the line containing the gloss
getWikitextGloss: function (txt, pos) {
var start = txt.lastIndexOf('\n{{trans-top', pos) + 1;
var end = txt.indexOf('\n', pos);
var line = txt.substring(start, end);
var parametersMatch = /\{\{trans-top[^\|\}]*(\|[^\{\}]+)/.exec(line);
var gloss = "";
if (parametersMatch) {
var parameterText = parametersMatch[1];
var parameterRegex = /\|(?:([^\|=]*)=([^\|]+)|([^\|]*))/g;
var parameterMatch;
var index = 0;
while ((parameterMatch = parameterRegex.exec(parameterText))) {
var key = parameterMatch[1],
value = parameterMatch[2],
unnamedValue = parameterMatch[3];
if (key) {
if (key === "1") {
gloss = value;
}
} else {
index++;
if (index === 1) {
gloss = unnamedValue;
}
}
}
}
return gloss;
},
// get [start_pos, end_pos] of position of wikitext for trans_table containing node in text
getTransTable: function (text, node, recursive) {
var gloss = util.getTransGloss(node);
var pos = 0;
var transect = [];
while (pos > -1) {
pos = util.getVanillaIndexOf('{{trans-top', text, pos + 1);
if (pos > -1 && util.matchGloss(util.getWikitextGloss(text, pos), gloss)) {
transect.push(pos);
}
}
if (transect.length > 1) {
var poss = transect;
transect = [];
for (var i = 0; i < poss.length; i++) {
pos = poss[i];
if (util.matchGloss(gloss, util.getWikitextGloss(text, pos))) {
transect.push(pos);
}
}
if (transect.length > 1 && !recursive)
transect = util.tieBreakTransTable(text, transect, node);
}
if (transect.length == 1) {
pos = transect[0];
pos = util.getVanillaIndexOf("\n", text, pos) + 1;
var endpos = text.indexOf('{{trans-bottom}}', pos);
if (endpos > -1 && pos > 0)
return [pos, endpos];
}
console.trace("getTransTable failure; text = %o, node = %o, recursive = %o, gloss = %o", text, node, recursive, gloss);
return false;
},
// try to narrow down the correct poss if multiple matching trans tables
tieBreakTransTable: function (text, poss, node) {
if (node.nodeName == 'DIV') {
while (node && !(node.classList && node.classList.contains('NavFrame')))
node = node.parentNode;
var nodes = node.getElementsByTagName('table');
if (!nodes.length)
return poss;
node = nodes[0];
} else {
while (node && node.nodeName != 'TABLE')
node = node.parentNode;
}
var before_count = 0;
var after_count = 0;
var is_found = false;
$("table.translations").each(function () {
var gloss = util.getTransGloss(this);
if (gloss == "Translations to be checked")
return;
if (this == node) {
is_found = true;
return;
}
var pos = util.getTransTable(text, this, true);
if (pos) {
for (var j = 0; j < poss.length; j++) {
if (poss[j] == pos)
return util.tieBreakTransTable(poss.splice(j, 1), node);
}
} else {
var matched = 0;
for (var j = 0; j < poss.length; j++) {
if (util.matchGloss(util.getWikitextGloss(text, poss[j]), gloss) &&
util.matchGloss(gloss, util.getWikitextGloss(text, poss[j]))) {
matched++;
}
}
if (matched == poss.length) {
if (is_found)
after_count++;
else
before_count++;
}
}
});
if (before_count + 1 + after_count == poss.length)
return [poss[before_count]];
else
return poss;
},
matchGloss: function (line, gloss) {
if (gloss.match(/^ *$/))
return !!(line.match(/\{\{trans-top\|? *\}\}/) || line.match(/^ *$/));
var words = gloss.split(/\W+/);
var pos = 0;
for (var i = 0; i < words.length; i++) {
pos = line.indexOf(words[i], pos);
if (pos == -1)
return false;
}
return pos > -1;
},
// If the translationNode is a NavHead, this goes up to the NavFrame and then
// down to the translation table. If the translationNode is the translation
// table or is inside the translation table, it goes up till it finds the
// translation table. Then it retrieves the data-gloss="..." attribute
// that is provided by {{trans-top}} and the other translation header
// templates.
// This function returns a gloss after it has been transformed by the parser
// into HTML. So the gloss will not match the gloss in the wikitext if the
// gloss in the wikitext contains templates, for instance.
getTransGloss: function(translationNode) {
var node = translationNode;
if (translationNode.nodeType === Node.ELEMENT_NODE
&& translationNode.nodeName === 'DIV'
&& translationNode.classList.contains('NavHead')) {
var navFrame = translationNode.parentNode;
if (!navFrame.classList.contains('NavFrame')) {
console.trace('No NavFrame parent of %o', translationNode);
return '';
}
node = navFrame.getElementsByClassName('translations')[0];
if (!node) {
console.trace('No child of parent of %o with "translations" class', translationNode);
return '';
}
}
while (true) {
if (node.nodeType === Node.ELEMENT_NODE
&& node.nodeName === 'TABLE'
&& node.classList.contains('translations')) {
return node.dataset.gloss;
}
node = node.parentNode;
if (!node || node.id === 'mw-parser-output') break;
}
console.trace('Found no gloss in <table class="translations" data-gloss="..."> at or above %o', translationNode);
return '';
},
isTrreq: function (li) {
var span = li.getElementsByTagName('span')[0];
return (span && span.classList.contains("trreq"));
},
genderList: {
"s": "singular", "d": "dual", "p": "plural",
"m": "masc.", "m-d": "masc. dual", "m-p": "masc. pl.",
"f": "fem.", "f-d": "fem. dual", "f-p": "fem. pl.",
"c": "common", "c-d": "common dual", "c-p": "common pl.",
"n": "neuter", "n-d": "neuter dual", "n-p": "neuter pl.",
"impf": "imperfective", "pf": "perfective"
}
};
// An adder for translations on en.wikt
function TranslationAdders(editor) {
function TranslationLabeller(insertDiv) {
var original_span;
var adder_form;
var initial_value;
var edit_button;
var editing = false;
var adderInterface = {
'fields': {
'gloss': function (txt, error) {
return util.validateNoWikisyntax('gloss', true)(txt, error);
}
},
'createForm': function () {
var thisId = "a" + String(Math.random()).replace(".", "");
adder_form = newNode('form', {
style: 'display: inline',
width: 'auto',
click: kill_event
},
newNode('label', {
'for': thisId
}, "Gloss: "),
newNode('input', {
type: 'text',
name: 'gloss',
value: initial_value,
style: 'width: 50%',
title: 'Insert a summary of the relevant definition',
id: thisId
}),
newNode('input', {
type: 'submit',
name: 'preview',
value: 'Preview'
}),
newNode('a', {
href: '/wiki/Help:Glosses'
}, 'Help?!')
);
return adder_form;
},
'onsubmit': function (values, render) {
render(values.gloss, function (new_html) {
if (editing)
toggle_editing(false);
var old_html = original_span.innerHTML;
editor.addEdit({
'undo': function () {
original_span.innerHTML = old_html;
if (!editing) toggle_editing();
},
'redo': function () {
original_span.innerHTML = new_html;
if (editing) toggle_editing();
},
'edit': function (text) {
return perform_edit(text, values.gloss);
},
'summary': 'tgloss:"' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '...' : values.gloss + '"')
}, original_span);
});
}
};
// The actual modification to the wikitext
function perform_edit(wikitext, gloss) {
var pos = util.getTransTable(wikitext, insertDiv)[0] - 4;
var g_start = wikitext.lastIndexOf('\n{{trans-top', pos) + 1;
var g_end = wikitext.indexOf('}}\n', pos) + 2;
if (g_start === 0 || wikitext.substr(g_start, g_end - g_start).indexOf("\n") > -1) {
editor.error("Could not find translation table.");
return wikitext;
} else {
return wikitext.substr(0, g_start) + '{{trans-top|' + gloss + '}}' + wikitext.substr(g_end);
}
}
// Don't open and close box when interacting with form.
function kill_event(e) {
e.stopPropagation();
}
// What to do when the +/- button is clicked.
function toggle_editing() {
if (editing) {
adder_form.style.display = "none";
original_span.style.display = "inline";
editing = false;
return;
}
editing = true;
edit_button.text("Loading...");
editor.withCurrentText(function (currentText) {
var pos = util.getTransTable(currentText, insertDiv);
edit_button.text('±');
if (!pos)
return editor.error("Could not find translation table");
var gloss_line = currentText.substr(currentText.lastIndexOf('\n', pos[0] - 2) + 1);
gloss_line = gloss_line.substr(0, gloss_line.indexOf('\n'));
initial_value = gloss_line.replace(/^\{\{trans-top(\|(.*)|)\}\}\s*$/, "$2");
if (initial_value.indexOf("\n") > 0)
return editor.error("Internal error: guess spanning multiple lines");
if (!original_span) {
original_span = newNode('span', {
'class': 'wt-edit-recurse'
});
for (var i = 0; i < insertDiv.childNodes.length; i++) {
var child = insertDiv.childNodes[i];
if (child != edit_button[0] && child.className !== 'NavToggle') {
original_span.appendChild(insertDiv.removeChild(child));
i--;
}
}
insertDiv.appendChild(original_span);
new AdderWrapper(editor, adderInterface, insertDiv, original_span);
}
original_span.style.display = "none";
adder_form.style.display = "inline";
adder_form.getElementsByTagName('input')[0].focus();
});
}
edit_button = $('<a href="#">±</a>')
//.addClass("translation-gloss-edit-button")
.attr("title", "Edit table heading").css("padding", "2px").css("margin-left", "-5px")
.on("click", function (e) {
if (e && e.preventDefault)
e.preventDefault();
kill_event(e);
toggle_editing();
return false;
});
$(insertDiv).prepend(edit_button); //XXX: this places anchor before NavToggle which may not be a good idea
}
function TranslationAdder(insertUl) {
// Hippietrail
var langmetadata = new LangMetadata();
this.fields = {
lang: function (txt, error) {
if (txt == 'en')
return error("Please choose a foreign language. (fr, es, aaa)");
if (!/^[a-z]{2,3}(?:-[a-z]{2,3}){0,2}$/.test(txt))
return error("Please use a language code. (fr, es, aaa)");
return txt;
},
word: function (txt, error) {
if (txt == '{{trreq}}')
return '{{t-needed}}';
if (txt == '{{t-needed}}')
return txt;
if (txt.indexOf(',') == -1 || forceComma) {
forceComma = false;
if (langmetadata.expectedCase(thiz.elements.lang.value, mw.config.get('wgTitle'), txt) || forceCase) {
forceCase = false;
return util.validateNoWikisyntax('translation', true)(txt, error);
}
if (prefs.get('case-knowledge', 'none') == 'none') {
return error(newNode('span',
"Translations normally don't have capital letters. If you're certain it does, you can ",
newNode('span', {
style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;",
click: function () {
forceCase = true;
inputForm.onsubmit();
prefs.set('case-knowledge', 'guru');
}
}, "continue by clicking here.")));
} else {
var msg = newNode('span',
newNode('span', {
style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;",
click: function () {
prefs.set('case-knowledge', 'none');
try {
msg.parentNode.removeChild(msg);
} catch (e) {
}
editor.undo();
}
}, "Please click undo"), " unless you are certain that this translation has a capital letter.");
error(msg);
return txt;
}
}
if (prefs.get('comma-knowledge', 'none') == 'none') {
return error(newNode('span',
"You can only add one translation at a time. If this is one translation that contains a comma, you can ",
newNode('span', {
style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;",
click: function () {
forceComma = true;
inputForm.onsubmit();
prefs.set('comma-knowledge', 'guru');
}
}, "add it by clicking here.")));
} else {
var msg = newNode('span',
newNode('span', {
style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;",
click: function () {
prefs.set('comma-knowledge', 'none');
try {
msg.parentNode.removeChild(msg);
} catch (e) {
}
editor.undo();
}
}, "Please click undo"), " if you were trying to create a list of translations in one go, this is currently not supported.");
error(msg);
return txt;
}
},
qual: util.validateNoWikisyntax('qualifier'),
tr: util.validateNoWikisyntax('transcription'),
lit: util.validateNoWikisyntax('literal'),
rawPageName: util.validateNoWikisyntax('raw page name'),
nested: util.validateNoWikisyntax('nested'),
"s": 'checkbox',
"d": 'checkbox',
"p": 'checkbox',
"m": 'checkbox',
"m-d": 'checkbox',
"m-p": 'checkbox',
"f": 'checkbox',
"f-d": 'checkbox',
"f-p": 'checkbox',
"c": 'checkbox',
"c-d": 'checkbox',
"c-p": 'checkbox',
"n": 'checkbox',
"n-d": 'checkbox',
"n-p": 'checkbox',
"impf": 'checkbox',
"pf": 'checkbox',
nclass1: util.validateNoWikisyntax('noun class'),
nclass2: util.validateNoWikisyntax('plural class')
};
this.createForm = function () {
function createGenderNode(name, text) {
var inp = document.createElement("input");
inp.type = "checkbox";
inp.name = name;
var lbl = document.createElement("label");
lbl.appendChild(inp);
lbl.appendChild(document.createTextNode(text));
return lbl;
}
var genderControls = {};
for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]);
var controls = {
lang: newNode('input', {
size: 4,
type: 'text',
name: 'lang',
value: prefs.get('curlang', ''),
title: 'The two or three letter ISO 639 language code'
}),
transliteration: newNode('p', newNode('a', {
href: '/wiki/Wiktionary:Transliteration'
}, "Transliteration"), ": ",
newNode('input', {
name: "tr",
title: "The word transliterated into the Latin alphabet."
}), " (e.g. zìmǔ for 字母)"),
literal: newNode('p', "Literal translation: ",
newNode('input', {
name: "lit",
title: "The literal translation of the term."
})),
qualifier: newNode('p', "Qualifier: ", newNode('input', {
name: 'qual',
title: "A qualifier for the word"
}), " (e.g. literally, formally, slang)"),
display: newNode('p', "Raw page name: ", newNode('input', {
name: 'rawPageName',
title: "A link to lemma, if a translation is not a lemma."
}), " (e.g. 疲れる for 疲れた)"),
nested: newNode('p', "Nesting: ", newNode('input', {
name: 'nested',
title: "The nesting of this language"
}), " (e.g. Serbo-Croatian/Cyrillic)"),
// Genders
genders: genderControls,
// Noun class
nclass: newNode('p',
"Noun class: ", newNode('input', {
type: 'text',
size: 4,
name: 'nclass1',
title: "The noun class of the word in the singular or citation form."
}),
" Plural class: ", newNode('input', {
type: 'text',
size: 4,
name: 'nclass2',
title: "The noun class of the word in the plural form, if any."
}))
};
// suppress unused script field
function createGenderDiv(genderz) {
var $div = $("<div>");
for (var i = 1; i < arguments.length; i++) $div.append(genderz[arguments[i]]);
return $div;
}
var $mdiv = createGenderDiv(controls.genders, "m", "m-d", "m-p");
var $fdiv = createGenderDiv(controls.genders, "f", "f-d", "f-p");
var $cdiv = createGenderDiv(controls.genders, "c", "c-d", "c-p");
var $ndiv = createGenderDiv(controls.genders, "n", "n-d", "n-p");
var $sdpdiv = createGenderDiv(controls.genders, "s", "d", "p");
var $pfdiv = createGenderDiv(controls.genders, "impf", "pf");
controls.gender = $("<p>").append($mdiv).append($fdiv).append($cdiv).append($ndiv).append($sdpdiv).append($pfdiv)[0];
langInput = controls.lang;
var showButton = newNode('span', {
'click': function () {
if (!advancedMode) {
advancedMode = true;
showButton.innerHTML = " Less";
} else {
advancedMode = false;
showButton.innerHTML = " More";
}
updateFieldVisibility.call(langInput, true);
},
'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;"
}, advancedMode ? " Less" : " More");
function autoTransliterate() {
var rawPageName = langmetadata.computeRawPageName(thiz.elements.lang.value, thiz.elements.word.value);
if (rawPageName && rawPageName !== thiz.elements.word.value)
thiz.elements.rawPageName.value = rawPageName;
}
function updateFieldVisibility(preserve) {
preserve = (preserve === true);
//show all arguments
function show() {
for (var i = 0; i < arguments.length; i++) {
if (arguments[i].nodeName == 'P')
arguments[i].style.display = "block";
else
arguments[i].style.display = "inline";
}
}
//hide all arguments
function hide() {
for (var i = 0; i < arguments.length; i++)
arguments[i].style.display = "none";
}
//if the first argument is false hide the remaining arguments, otherwise show them.
function toggle(condition) {
if (condition) //eww...
show.apply(this, [].splice.call(arguments, 1, arguments.length - 1));
else
hide.apply(this, [].splice.call(arguments, 1, arguments.length - 1));
}
if (!preserve)
langInput.value = langmetadata.cleanLangCode(langInput.value);
var scriptGuess = langmetadata.guessScript(langInput.value || '');
if (!preserve) {
thiz.elements.nested.value = langmetadata.getNested(langInput.value || '');
thiz.elements.nested.dataset.initialValue = thiz.elements.nested.value;
autoTransliterate();
}
var lang = langInput.value;
if (!advancedMode) {
var g = langmetadata.getGenders(lang);
if (!lang) {
hide(controls.gender);
}
else if (g === undefined) {
hide(controls.gender);
}
else {
for (var genderName in util.genderList) {
toggle(g.indexOf(genderName) != -1, controls.genders[genderName]);
}
}
toggle(g, controls.gender);
toggle(langmetadata.hasNounClasses(lang), controls.nclass);
var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang);
toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration);
toggle(langmetadata.needsRawPageName(lang), controls.display);
hide(controls.literal, controls.qualifier, controls.nested); //only in more
} else {
show(
controls.gender,
controls.genders['s'], controls.genders['d'], controls.genders['p'],
controls.genders['m'], controls.genders['m-d'], controls.genders['m-p'],
controls.genders['f'], controls.genders['f-d'], controls.genders['f-p'],
controls.genders['c'], controls.genders['c-d'], controls.genders['c-p'],
controls.genders['n'], controls.genders['n-d'], controls.genders['n-p'],
controls.genders['impf'], controls.genders['pf'],
controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display,
controls.nested);
}
}
//autocomplete language names
function langAutoFill(e) {
e = (e || event).keyCode;
if ((e >= 33 && e <= 40) || e == 8 || e == 46 || e == 27 || e == 16) {
return;
}
var t = this,
langPrefix = t.value;
if (langPrefix.substr(0, 1) != langPrefix.substr(0, 1).toUpperCase()) {
return;
}
new mw.Api().get({
"action": "expandtemplates",
"format": "json",
"text": "{{#invoke:languages/javascript-interface|GetSingleLanguageByLanguagePrefix|" + langPrefix + "}}",
"prop": "wikitext"
}).done(function (r) {
if (r.expandtemplates && r.expandtemplates.wikitext !== "") {
var langcode = r.expandtemplates.wikitext.split(":")[0];
var langname = r.expandtemplates.wikitext.split(":")[1];
var oldLength = t.value.length;
t.value = langname;
$(t).data("langcode", langcode);
if (t.setSelectionRange) {
t.setSelectionRange(oldLength, langname.length);
} else if (t.createTextRange) {
var z = t.createTextRange();
z.moveEnd('character', 0 - z.move('character', [t.value.length, t.value = langname][0]) + t.value.length);
z.select();
}
} else {
$(t).removeData("langcode");
}
});
}
langInput.onkeyup = langAutoFill;
langInput.onblur = function () {
if ($(this).data("langcode"))
$(this).val($(this).data("langcode"));
updateFieldVisibility.call(langInput);
};
window.setTimeout(function () {
updateFieldVisibility.call(langInput);
}, 0);
inputForm = newNode('form',
newNode('p', newNode('a', {
href: "/wiki/MediaWiki:Gadget-TranslationAdder.js/help#Usage"
}, "Add translation"), ' ',
langInput, newNode('b', ': '), newNode('input', {
'name': 'word',
size: 20,
keyup: autoTransliterate,
change: autoTransliterate
}),
newNode('input', {
'type': 'submit',
'value': 'Preview translation'
}), showButton
),
controls.gender,
controls.nclass,
controls.transliteration,
controls.literal,
controls.display,
controls.qualifier,
controls.nested
);
return inputForm;
};
this.onsubmit = function (values, render) {
var wikitext;
function callRender() {
render(wikitext, function (html) {
registerEdits(values, wikitext, html);
});
}
// Keep track of whether the "nested" field has changed
// so that we can tag the edit
values.nestedChanged = inputForm.nested.dataset.initialValue !== values.nested;
var languagePrefix = '{{subst:#invoke:languages/templates|getByCodeAllowEtym|' + values.lang + '|getCanonicalName}}: ';
if (values.word.indexOf('{{t-needed') === 0) {
wikitext = languagePrefix + ' {{t-needed|' + values.lang + '}}';
callRender();
} else {
langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) {
if (needsRawPageName) {
values.rawPageName = rawPageName;
values.wordMinus = '';
} else {
values.rawPageName = '';
values.wordMinus = rawPageName;
}
langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) {
var templateBody = '|' + values.lang + '|' +
langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) +
(values.nclass1 ? '|c' + values.nclass1 : '') +
(values.nclass2 ? '|c' + values.nclass2 : '');
// Add gender codes
templateBody += [
's', 'd', 'p', 'm', 'm-d', 'm-p', 'f', 'f-d', 'f-p',
'c', 'c-d', 'c-p', 'n', 'n-d', 'n-p', 'impf', 'pf'
].map(function(k) {
return values[k] ? '|' + k : '';
}).join("");
templateBody +=
(values.tr ? '|tr=' + values.tr : '') +
(values.lit ? '|lit=' + values.lit : '') +
(values.rawPageName ? '|alt=' + values.word : '');
var qualifier = values.qual ? ' {{q|' + values.qual + '}}' : '';
wikitext = languagePrefix + "{{" + (hasInterwiki ? "t+" : "t") + templateBody + "}}" + qualifier;
callRender();
});
});
}
};
var thiz = this;
var prefs = new CookiePreferences('EditorJs');
var langInput;
var inputForm;
var advancedMode = prefs.get('more-display', 'none') != 'none';
var forceComma = false;
var forceCase = false;
//Reset elements to default values.
function resetElements() {
if (prefs.get('more-display', 'none') != advancedMode ? 'block' : 'none')
prefs.set('more-display', advancedMode ? 'block' : 'none'); //named for compatibility
thiz.elements.word.value = thiz.elements.nclass1.value = thiz.elements.nclass2.value = thiz.elements.lit.value = thiz.elements.tr.value = thiz.elements.rawPageName.value = thiz.elements.qual.value = '';
thiz.elements['s'].checked = false;
thiz.elements['d'].checked = false;
thiz.elements['p'].checked = false;
thiz.elements['m'].checked = false;
thiz.elements['m-d'].checked = false;
thiz.elements['m-p'].checked = false;
thiz.elements['f'].checked = false;
thiz.elements['f-d'].checked = false;
thiz.elements['f-p'].checked = false;
thiz.elements['c'].checked = false;
thiz.elements['c-d'].checked = false;
thiz.elements['c-p'].checked = false;
thiz.elements['n'].checked = false;
thiz.elements['n-d'].checked = false;
thiz.elements['n-p'].checked = false;
thiz.elements['impf'].checked = false;
thiz.elements['pf'].checked = false;
prefs.set('curlang', thiz.elements.lang.value);
}
// This is onsubmit after the wikitext has been rendered to give content
function registerEdits(values, wikitext, content) {
var li = newNode('li', {
'class': 'trans-' + values.lang
});
li.innerHTML = content;
var lang = getLangName(li);
var x = langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word);
var resp = x;
var langCodeBad = false;
if (x.indexOf("{{") < 0) {
new mw.Api().get({
"action": "expandtemplates",
"format": "json",
"text": "{{ll|" + values.lang + "|" + x + "}}",
"prop": "wikitext"
}, {"async": false}).done(function (r) {
if (r.expandtemplates && r.expandtemplates.wikitext) {
// [[page#Language|display]]
// resp = r.expandtemplates.wikitext;
// [[page|display]]
// resp = r.expandtemplates.wikitext.replace(/#[^|]*/, "");
// [[page]]
resp = r.expandtemplates.wikitext.replace(/#[^\]]*/, "");
try {
if (DOMParser && new DOMParser().parseFromString(r.expandtemplates.wikitext, "text/html").querySelector(".scribunto-error"))
langCodeBad = true;
} catch (e) { }
}
}).fail(function(r) {
console.error(r);
resp = resp.includes("[[") ? resp : "[[" + resp + "]]";
});
}
if (langCodeBad)
return editor.error("Unrecognized language code '" + values.lang + "'");
var summary = 't+' + values.lang + ':' + resp;
var insertBefore = null;
var nextLanguage = null;
var nestedHeading, nestedLang;
var nested = values.nested.split('/');
if (nested.length > 1) {
nestedHeading = nested[0];
nestedLang = (nested[1] === 'Roman' ? 'Latin' : nested[1]); // [[Wiktionary:Beer parlour/2026/January#sorting and reformating translation tables]]
if (nestedHeading === '')
nestedHeading = lang;
content = content.replace(/.*: /, nestedLang + ": ");
wikitext = wikitext.replace("subst:", "").replace(/.*: /, nestedLang + ": ");
} else {
nestedHeading = values.nested;
nestedLang = lang;
}
var nestedWikitext = "\n* " + nestedHeading + ":\n*: " + wikitext;
function addEdit(edit, span) {
var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"];
editor.addEdit({
'undo': function () {
edit.undo();
if (thiz.elements.word.value === "" &&
thiz.elements.nclass1.value === "" &&
thiz.elements.nclass2.value === "" &&
thiz.elements.tr.value === "" &&
thiz.elements.lit.value === "" &&
thiz.elements.rawPageName.value === "" &&
thiz.elements.qual.value === "") {
for (var i = 0; i < fields.length; i++) {
thiz.elements[fields[i]].value = values[fields[i]];
}
for (var genderName in util.genderList) {
thiz.elements[genderName].checked = values[genderName];
}
}
},
'redo': function () {
edit.redo();
for (var i = 0; i < fields.length; i++) {
if (thiz.elements[fields[i]].value != values[fields[i]])
return;
}
resetElements();
},
'edit': edit.edit,
'tags': [values.nestedChanged ? 'translation-adder-custom-nesting' : null],
'summary': summary
}, span);
}
// Compare language names without accents and punctuation marks
var normalizeAndStrip = s => s.normalize("NFD").replace(/[\u0300-\u036f'ʼ", -]/g, '').toUpperCase();
var compare = function(a, b) {
if (normalizeAndStrip(a) < normalizeAndStrip(b)) {
return -1;
}
if (normalizeAndStrip(a) > normalizeAndStrip(b)) {
return 1;
}
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
}
if (lang) {
//Get all li's in this table row.
var lss = $(insertUl).parent().parent().find('li').get();
var dds = $(insertUl).parent().parent().find('dd').get();
var lis = lss.concat(dds);
for (var j = 0; j < lis.length; j++) {
if (lis[j].getElementsByTagName('form').length > 0)
continue;
var ln = getLangName(lis[j]);
if (ln == lang) {
if (values.word == '{{t-needed}}') {
addEdit({
'redo': function () {
},
'undo': function () {
},
'edit': function () {
return editor.error("Can not add a translation request for a language with translations");
}
});
return resetElements();
}
var span = newNode('span');
var parent = lis[j];
if (util.isTrreq(parent)) {
span.innerHTML = content.substr(content.indexOf(':') + 1);
var trspan = parent.getElementsByTagName('span')[0];
addEdit({
'redo': function () {
parent.removeChild(trspan);
parent.appendChild(span);
},
'undo': function () {
parent.removeChild(span);
parent.appendChild(trspan);
},
'edit': getEditFunction(values, wikitext, ln, values.lang, true, function (text, ipos) {
//Converting a Translation request into a translation
var lineend = text.indexOf('\n', ipos);
return text.substr(0, ipos) + wikitext + text.substr(lineend);
})
}, span);
return resetElements();
} else if (parent.tagName !== 'DD' || lang !== 'Latin') {
if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) {
span.innerHTML = ", " + content.substr(content.indexOf(':') + 1);
addEdit({
'redo': function () {
parent.appendChild(span)
},
'undo': function () {
parent.removeChild(span)
},
'edit': getEditFunction(values, wikitext, ln, values.lang, false, function (text, ipos) {
//We are adding the wikitext to a list of translations that already exists.
var lineend = text.indexOf('\n', ipos);
var wt = wikitext.replace('subst:#invoke:', '');
wt = wt.substr(wt.indexOf(':') + 1);
return text.substr(0, lineend) + "," + wt + text.substr(lineend);
})
}, span);
return resetElements();
} else {
var node = parent.firstChild;
var hastrans = false;
while (node) {
if (node.nodeType == 1) {
var nn = node.nodeName;
if (nn == 'UL' || nn == 'DL') {
// If we want to use the dialectical nesting for orthographical nesting
// then we need to skip this (otherwise perfect) match.
if (!hastrans && nestedHeading == ln) {
node = node.nextSibling;
continue;
}
span.innerHTML = (hastrans ? ", " : " ") + content.substr(content.indexOf(':') + 1);
addEdit({
'redo': function () {
parent.insertBefore(span, node)
},
'undo': function () {
parent.removeChild(span)
},
'edit': getEditFunction(values, wikitext, ln, values.lang, false, function (text, ipos) {
//Adding the translation to a language that has nested translations under it
var lineend = text.indexOf('\n', ipos);
var wt = wikitext.replace('subst:#invoke:', '');
wt = wt.substr(wt.indexOf(':') + 1);
return text.substr(0, lineend) + (hastrans ? "," : "") + wt + text.substr(lineend);
})
}, span);
return resetElements();
} else {
hastrans = true;
}
}
node = node.nextSibling;
}
}
}
} else if (ln && compare(ln, lang) > 0 && (!nextLanguage || compare(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') {
nextLanguage = ln;
var parent = lis[j];
insertBefore = [{
'redo': function () {
parent.parentNode.insertBefore(li, parent);
},
'undo': function () {
parent.parentNode.removeChild(li)
},
'edit': getEditFunction(values, wikitext, ln, getLangCode(parent), util.isTrreq(parent), function (text, ipos) {
//Adding a new language's translation before another language's translation
var lineend = text.lastIndexOf('\n', ipos);
return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend);
})
}, li];
}
}
}
if (values.nested) {
nextLanguage = null;
insertBefore = null;
li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>";
var lis = insertUl.parentNode.parentNode.getElementsByTagName('li');
for (var j = 0; j < lis.length; j++) {
//Ignore the editor form
if (lis[j].getElementsByTagName('form').length > 0)
continue;
//Don't look at nested translations
if (lis[j].parentNode.parentNode.nodeName != 'TD')
continue;
var ln = getLangName(lis[j]);
if (ln == nestedHeading) {
var sublis = lis[j].getElementsByTagName('li');
if (!sublis.length)
sublis = lis[j].getElementsByTagName('dd');
if (sublis.length == 0) {
var parent = lis[j];
var dd = newNode('dd', {
'class': 'trans-' + values.lang
});
var dl = newNode('dl', dd);
dd.innerHTML = content;
addEdit({
'redo': function () {
parent.appendChild(dl);
},
'undo': function () {
parent.removeChild(dl);
},
'edit': getEditFunction(values, wikitext, nestedHeading, getLangCode(parent), util.isTrreq(parent), function (text, ipos) {
//Adding a new dl to an existing translation line
var lineend = text.indexOf('\n', ipos);
return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend);
})
}, dd);
return resetElements();
} else {
var dd = newNode(sublis[0].nodeName, {
'class': 'trans-' + values.lang
});
var linestart = dd.nodeName == 'DD' ? '\n*: ' : '\n** ';
dd.innerHTML = content;
for (var k = 0; k < sublis.length; k++) {
var subln = getLangName(sublis[k]);
var parent = sublis[k];
if (subln == nestedLang) {
var span = newNode('span');
span.innerHTML = ", " + content.substr(content.indexOf(':') + 1);
addEdit({
'redo': function () {
parent.appendChild(span)
},
'undo': function () {
parent.removeChild(span)
},
'edit': getEditFunction(values, wikitext, nestedLang, values.lang, false, function (text, ipos) {
// Adding the wikitext to a list of translations that already exists.
var lineend = text.indexOf('\n', ipos);
var wt = wikitext.replace('subst:#invoke:', '');
wt = wt.substr(wt.indexOf(':') + 1);
return text.substr(0, lineend) + "," + wt + text.substr(lineend);
})
}, span);
return resetElements();
} else if (langmetadata.nestsBefore(nestedLang, subln)) {
addEdit({
'redo': function () {
parent.parentNode.insertBefore(dd, parent);
},
'undo': function () {
parent.parentNode.removeChild(dd);
},
'edit': getEditFunction(values, wikitext, subln, getLangCode(parent), util.isTrreq(parent),
function (text, ipos) {
// Adding a nested translation in-order
var lineend = text.lastIndexOf('\n', ipos);
return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend);
})
}, dd);
return resetElements();
}
}
addEdit({
'redo': function () {
parent.parentNode.appendChild(dd);
},
'undo': function () {
parent.parentNode.removeChild(dd);
},
'edit': getEditFunction(values, wikitext, subln, getLangCode(parent), util.isTrreq(parent),
function (text, ipos) {
// Adding a nested translation at the end of its group
var lineend = text.indexOf('\n', ipos);
return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend);
})
}, dd);
return resetElements();
}
} else if (ln && compare(ln, nestedHeading) > 0 && (!nextLanguage || compare(ln, nextLanguage) < 0)) {
nextLanguage = ln;
var parent = lis[j];
insertBefore = [{
'redo': function () {
parent.parentNode.insertBefore(li, parent);
},
'undo': function () {
parent.parentNode.removeChild(li)
},
'edit': getEditFunction(values, wikitext, ln, getLangCode(parent), util.isTrreq(parent), function (text, ipos) {
//Adding a new nested translation section.
var lineend = text.lastIndexOf('\n', ipos);
return text.substr(0, lineend) + nestedWikitext + text.substr(lineend);
})
}, li];
}
}
wikitext = nestedHeading + ":\n*: " + wikitext;
}
li.className = "trans-" + values.lang;
if (insertBefore) {
addEdit(insertBefore[0], insertBefore[1]);
} else {
//Append the translations to the end (no better way found)
addEdit({
'redo': function () {
insertUl.appendChild(li);
},
'undo': function () {
insertUl.removeChild(li)
},
'edit': getEditFunction(values, wikitext)
}, li);
}
return resetElements();
}
//Get the wikitext modification for the current form submission.
function getEditFunction(values, wikitext, findLanguage, findLangCode, trreq, callback) {
return function (text) {
var p = util.getTransTable(text, insertUl);
if (!p)
return editor.error("Could not find translation table for '"
+ values.lang + ":" + values.word
+ "'. Glosses should be unique");
var stapos = p[0];
var endpos = p[1];
if (findLanguage) {
var ipos = 0;
if (trreq) {
ipos = text.substr(stapos).search(new RegExp(mw.util.escapeRegExp(findLanguage) + ":\\s*\\{\\{t-needed(?:\\|" + findLangCode + "}})?")) + stapos;
if (ipos < stapos || ipos > endpos)
ipos = text.indexOf('{{trreq|' + findLanguage + '}}', stapos);
if (ipos < stapos || ipos > endpos)
ipos = text.indexOf('{{trreq|' + findLangCode + '}}', stapos);
}
// If we have a nested trreq, then we still need to look for the non-trreq form of the heading language
if (!trreq || ipos < stapos || ipos > endpos) {
var escapedLang = mw.util.escapeRegExp(findLanguage);
//if the translation should not be nested it should not go before any nested language
var regexByShouldFindSubLanguage = values.nested != "" ? "[:*]?" : "";
var possibleRegexes = [
RegExp("\\*" + regexByShouldFindSubLanguage + " ?\\[\\[" + escapedLang + "\\]\\]:"),
RegExp("\\*" + regexByShouldFindSubLanguage + " ?" + escapedLang + ":"),
RegExp("\\*" + regexByShouldFindSubLanguage + " ?\\{\\{ttbc\\|" + escapedLang + "}}:")
];
ipos = text.substr(stapos).search(possibleRegexes[0]) + stapos;
if (ipos < stapos || ipos > endpos)
ipos = text.substr(stapos).search(possibleRegexes[1]) + stapos;
if (ipos < stapos || ipos > endpos)
ipos = text.substr(stapos).search(possibleRegexes[2]) + stapos;
if (ipos < stapos || ipos > endpos)
ipos = text.indexOf('{{subst:#invoke:languages/templates|getByCodeAllowEtym|' + findLangCode + '|getCanonicalName}}:', stapos);
}
if (ipos >= stapos && ipos < endpos) {
return callback(text, ipos, trreq);
} else {
return editor.error("Could not find translation entry for '" + values.lang + ":" + values.word + "'. Please reformat");
}
}
return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos);
};
}
// For an <li> in well-formed translation sections, return the language name.
function getLangName(li) {
var guess = li.textContent || li.innerText;
if (guess)
guess = guess.substr(0, guess.indexOf(':'));
if (guess == 'Template') {
return false;
}
return guess.replace(/^[\s\n]*/, '');
}
// Try to get the language code from an <li> containing {{t}}, {{t+}} or {{t-}}
function getLangCode(li) {
if (li.className.indexOf('trans-') === 0)
return li.className.substr(6);
var spans = li.getElementsByTagName('span');
for (var i = 0; i < spans.length; i++) {
if (spans[i].lang) {
return spans[i].lang;
}
if (spans[i].dataset && spans[i].dataset.lang) {
return spans[i].dataset.lang;
}
}
return false;
}
}
function createTranslationAdderRow(li) {
var $ul = $("<ul>").append(li);
return $("<tr>")
.append($("<td>"))
.append($("<td>"))
.append($("<td>").css("text-align", "left").append($ul));
}
$('table.translations').each(function () {
if (util.getTransGloss(this) != 'Translations to be checked') {
var tdSelector = "td:not(.mw-hiero-table td):not(.targeted-translations-select)";
var uls = $(this).find(tdSelector + " > ul").get();
if (uls.length == 0) {
$(this).find(tdSelector)[0].appendChild(newNode('ul'));
uls = this.getElementsByTagName('ul');
}
if (uls.length == 1) {
var td = $(this).find(tdSelector)[2];
if (td) {
td.appendChild(newNode('ul'));
uls = this.getElementsByTagName('ul');
}
}
if (uls) {
var li = document.createElement('li');
var ul = uls[uls.length - 1];
$(this).append(createTranslationAdderRow(li));
new AdderWrapper(editor, new TranslationAdder(ul), li);
if (true) {// XXX: why not? (new CookiePreferences('EditorJs')).get('labeller') == 'true') {
var div = $(this).parent().parent().find('div.NavHead')[0];
if (div) {
new TranslationLabeller(div)
}
}
}
}
});
}
$(function () {
// Check if we are on a sensible page
var isOnPrintable = mw.util.getParamValue("printable") !== null;
var isDiff = mw.util.getParamValue("diff") !== null || mw.util.getParamValue("oldid") !== null;
if (mw.config.get('wgAction') != 'view' || isOnPrintable || isDiff)
return;
var editor = new Editor();
TranslationAdders(editor);
/*
// Check that we have not been disabled
var prefs = new CookiePreferences('EditorJs');
if (prefs.get('enabled', 'true') == 'true') {
if (!window.loadedEditor) {
prefs.setDefault('labeller', mw.config.get('wgUserName') ? 'true' : 'false');
window.loadedEditor = true;
}
}
// The enable-disable button on WT:EDIT
var node = document.getElementById('editor-js-disable-button');
if (node) {
node.innerHTML = "";
var toggle = newNode('span', {
click: function() {
if (prefs.get('enabled', 'true') == 'true') {
toggle.innerHTML = "Enable";
prefs.set('enabled', 'false');
} else {
toggle.innerHTML = "Disable";
prefs.set('enabled', 'true');
}
}
}, (prefs.get('enabled', 'true') == 'true' ? 'Disable' : 'Enable'))
node.appendChild(toggle);
}
var node = document.getElementById("editor-js-labeller-button");
if (node) {
node.innerHTML = "";
var toggle2 = newNode('span', {
click: function() {
if (prefs.get('labeller') == 'true') {
toggle2.innerHTML = "Enable";
prefs.set('labeller', 'false');
} else {
toggle2.innerHTML = "Disable";
prefs.set('labeller', 'true');
}
}
}, (prefs.get('labeller') == 'true' ? 'Disable' : 'Enable'))
node.appendChild(toggle2);
}*/
});
//</nowiki>
// </syntaxhighlight>
fenbzaedj1d7qphc980f0ohcrd0z8qg
MediaWiki:Gadget-BlockInfo.js
8
17958
88918
2017-05-13T16:14:01Z
en>-sche
0
88918
javascript
text/javascript
// {{documentation}}
/*jshint undef:true */
/*global jQuery, mw */
(function(){
'use strict';
var api = new mw.Api();
function el(tag, child, attr, events) {
var key, node = document.createElement(tag);
if (child) {
if (typeof child !== 'object')
child = [child];
for (var i = 0; i < child.length; ++i) {
var ch = child[i];
if ((ch === void(null)) || (ch === null))
continue;
else if (typeof ch !== 'object')
ch = document.createTextNode(String(ch));
node.appendChild(ch);
}
}
if (attr) for (key in attr) {
node.setAttribute(key, String(attr[key]));
}
if (events) for (key in events) {
node.addEventListener(key, events[key], false);
}
return node;
}
function fmtDate(date) {
// XXX: uses browser's time zone instead of preferences
date = new Date(date);
switch (mw.user.options.get('date')) {
case 'dmy' : return date.toLocaleTimeString() + ', ' + date.getDate() + ' ' + mw.language.months.genitive[date.getMonth()] + ' ' + date.getFullYear();
case 'mdy' : return date.toLocaleTimeString() + ', ' + mw.language.months.genitive[date.getMonth()] + ' ' + date.getFullYear() + ', ' + date.getDate();
case 'ymd' : return date.toLocaleTimeString() + ', ' + date.getFullYear() + ' ' + mw.language.months.genitive[date.getMonth()] + ' ' + date.getDate();
case 'default' : return date.toLocaleString();
case 'ISO 8601': return date.toISOString();
}
}
var wd2iw = {
'metawiki': 'm'
};
function buildList(user, callback) {
var isAnon = mw.util.isIPv4Address(user) || mw.util.isIPv6Address(user);
var rq = api.get({
action: 'query',
list: 'blocks' + (isAnon ? '|globalblocks' : '|globalallusers'),
// blocks
bkip: isAnon ? user : void(window.warranty),
bkusers: isAnon ? void(window.warranty) : user,
bkprop: 'id|user|range|timestamp|expiry|by|reason|flags',
bkdir: 'older',
// globalblocks
bgip: isAnon ? user : void(window.warranty),
bgprop: isAnon ? 'id|address|range|timestamp|expiry|by|reason' : void(window.warranty),
bgdir: isAnon ? 'older' : void(window.warranty),
// globalallusers
agufrom: !isAnon ? user : void(window.warranty),
aguto: !isAnon ? user : void(window.warranty),
agulimit: !isAnon ? 1 : void(window.warranty),
aguprop: !isAnon ? 'lockinfo' : void(window.warranty)
});
rq.then(function (result) {
var list = el('ul'), item, i;
if (result.query.globalallusers && result.query.globalallusers[0] && ('locked' in result.query.globalallusers[0])) {
list.appendChild(el('li', [
"This account is globally locked",
" (", el('a', "globalauth log", { href: mw.util.getUrl('m:Special:Log/globalauth', { page: 'User:' + user + '@global' }) }), ")."
]));
}
for (i = 0; i < result.query.blocks.length; ++i) {
item = result.query.blocks[i];
list.appendChild(el('li', [
fmtDate(item.timestamp),
': ', el('a', item.by, { href: mw.util.getUrl('User:' + item.by) }),
" blocked ", el('b', item.user),
" (", el('a', 'block log', { href: mw.util.getUrl('Special:Log/block', { page: 'User:' + item.user }) }), "),",
' expiring ', el('b', item.expiry === 'infinity' ? 'never' : fmtDate(item.expiry)),
item.reason && " (", item.reason, item.reason && ")",
]));
}
if (result.query.globalblocks)
for (i = 0; i < result.query.globalblocks.length; ++i) {
item = result.query.globalblocks[i];
list.appendChild(el('li', [
fmtDate(item.timestamp),
': ', el('a', item.by, { href: mw.util.getUrl(wd2iw[item.bywiki] + ':User:' + item.by) }),
" at ", el('code', item.bywiki),
" globally blocked ", el('b', item.address),
" (", el('a', "gblblock log", { href: mw.util.getUrl(wd2iw[item.bywiki] + ':Special:Log/gblblock', { page: 'User:' + item.address }) }), "),",
" expiring ", el('b', item.expiry === 'infinity' ? "never" : fmtDate(item.expiry)),
item.reason && " (", item.reason, item.reason && ")",
]));
}
callback(list);
});
return rq;
}
function makeBox(user, relTo) {
if (!user)
return;
relTo = relTo || document.getElementById('mw-content-text');
var afterList, box = el('div', [
el('p', [el('strong', user), ' is currently blocked.']),
afterList = el('small', [
el('a', "block log" , { href: mw.util.getUrl('Special:Log/block', { page: 'User:' + user }) })
])
], {
'class': 'mw-warning-with-logexcerpt'
});
relTo.rq = buildList(user, function (list) {
relTo.rq = null;
if (!list.hasChildNodes())
return;
box.insertBefore(list, afterList);
relTo.parentNode.insertBefore(box, relTo);
});
return box;
}
var user;
var wgNamespaceNumber = mw.config.get('wgNamespaceNumber');
var wgNamespaceIds = mw.config.get('wgNamespaceIds');
var wgTitle = mw.config.get('wgTitle');
if ((wgNamespaceNumber & ~1) === wgNamespaceIds.user) {
makeBox(wgTitle.replace(/\/.*$/, ''));
} else if ((mw.config.get('wgCanonicalSpecialPageName') === 'Contributions') || (mw.config.get('wgCanonicalSpecialPageName') === 'DeletedContributions')) {
makeBox(wgTitle.indexOf('/') !== -1 ? wgTitle.replace(/^.*?\//, '') : mw.util.getParamValue('target'));
} else if (mw.config.get('wgCanonicalSpecialPageName') === 'Log') {
user = mw.util.getParamValue('user');
if (/^[^\/]+\/(block|delete)$/.test(wgTitle))
return;
if (wgTitle.indexOf('/') !== -1)
user = wgTitle.replace(/^.*\//, '');
makeBox(user);
} else if (mw.config.get('wgCanonicalSpecialPageName') === 'Block') {
var ipinput = document.getElementById('mw-bi-target');
var relTo = ipinput.form.getElementsByTagName('legend')[0].nextSibling;
var warnnode = null;
var refresh = function () {
if (relTo.rq)
relTo.rq.abort();
if (warnnode && warnnode.parentNode) {
warnnode.parentNode.removeChild(warnnode);
warnnode = null;
}
if (!ipinput.value)
return;
warnnode = makeBox(ipinput.value, relTo);
};
var tmout;
ipinput.addEventListener('input', function () {
clearTimeout(tmout);
tmout = setTimeout(refresh, 500);
}, false);
ipinput.addEventListener('change', function () {
refresh();
}, false);
refresh();
return;
}
})();
r2okjk3b3yr52txn6t4pem85ctk3im8
88919
88918
2026-08-06T23:13:15Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-BlockInfo.js]]
88918
javascript
text/javascript
// {{documentation}}
/*jshint undef:true */
/*global jQuery, mw */
(function(){
'use strict';
var api = new mw.Api();
function el(tag, child, attr, events) {
var key, node = document.createElement(tag);
if (child) {
if (typeof child !== 'object')
child = [child];
for (var i = 0; i < child.length; ++i) {
var ch = child[i];
if ((ch === void(null)) || (ch === null))
continue;
else if (typeof ch !== 'object')
ch = document.createTextNode(String(ch));
node.appendChild(ch);
}
}
if (attr) for (key in attr) {
node.setAttribute(key, String(attr[key]));
}
if (events) for (key in events) {
node.addEventListener(key, events[key], false);
}
return node;
}
function fmtDate(date) {
// XXX: uses browser's time zone instead of preferences
date = new Date(date);
switch (mw.user.options.get('date')) {
case 'dmy' : return date.toLocaleTimeString() + ', ' + date.getDate() + ' ' + mw.language.months.genitive[date.getMonth()] + ' ' + date.getFullYear();
case 'mdy' : return date.toLocaleTimeString() + ', ' + mw.language.months.genitive[date.getMonth()] + ' ' + date.getFullYear() + ', ' + date.getDate();
case 'ymd' : return date.toLocaleTimeString() + ', ' + date.getFullYear() + ' ' + mw.language.months.genitive[date.getMonth()] + ' ' + date.getDate();
case 'default' : return date.toLocaleString();
case 'ISO 8601': return date.toISOString();
}
}
var wd2iw = {
'metawiki': 'm'
};
function buildList(user, callback) {
var isAnon = mw.util.isIPv4Address(user) || mw.util.isIPv6Address(user);
var rq = api.get({
action: 'query',
list: 'blocks' + (isAnon ? '|globalblocks' : '|globalallusers'),
// blocks
bkip: isAnon ? user : void(window.warranty),
bkusers: isAnon ? void(window.warranty) : user,
bkprop: 'id|user|range|timestamp|expiry|by|reason|flags',
bkdir: 'older',
// globalblocks
bgip: isAnon ? user : void(window.warranty),
bgprop: isAnon ? 'id|address|range|timestamp|expiry|by|reason' : void(window.warranty),
bgdir: isAnon ? 'older' : void(window.warranty),
// globalallusers
agufrom: !isAnon ? user : void(window.warranty),
aguto: !isAnon ? user : void(window.warranty),
agulimit: !isAnon ? 1 : void(window.warranty),
aguprop: !isAnon ? 'lockinfo' : void(window.warranty)
});
rq.then(function (result) {
var list = el('ul'), item, i;
if (result.query.globalallusers && result.query.globalallusers[0] && ('locked' in result.query.globalallusers[0])) {
list.appendChild(el('li', [
"This account is globally locked",
" (", el('a', "globalauth log", { href: mw.util.getUrl('m:Special:Log/globalauth', { page: 'User:' + user + '@global' }) }), ")."
]));
}
for (i = 0; i < result.query.blocks.length; ++i) {
item = result.query.blocks[i];
list.appendChild(el('li', [
fmtDate(item.timestamp),
': ', el('a', item.by, { href: mw.util.getUrl('User:' + item.by) }),
" blocked ", el('b', item.user),
" (", el('a', 'block log', { href: mw.util.getUrl('Special:Log/block', { page: 'User:' + item.user }) }), "),",
' expiring ', el('b', item.expiry === 'infinity' ? 'never' : fmtDate(item.expiry)),
item.reason && " (", item.reason, item.reason && ")",
]));
}
if (result.query.globalblocks)
for (i = 0; i < result.query.globalblocks.length; ++i) {
item = result.query.globalblocks[i];
list.appendChild(el('li', [
fmtDate(item.timestamp),
': ', el('a', item.by, { href: mw.util.getUrl(wd2iw[item.bywiki] + ':User:' + item.by) }),
" at ", el('code', item.bywiki),
" globally blocked ", el('b', item.address),
" (", el('a', "gblblock log", { href: mw.util.getUrl(wd2iw[item.bywiki] + ':Special:Log/gblblock', { page: 'User:' + item.address }) }), "),",
" expiring ", el('b', item.expiry === 'infinity' ? "never" : fmtDate(item.expiry)),
item.reason && " (", item.reason, item.reason && ")",
]));
}
callback(list);
});
return rq;
}
function makeBox(user, relTo) {
if (!user)
return;
relTo = relTo || document.getElementById('mw-content-text');
var afterList, box = el('div', [
el('p', [el('strong', user), ' is currently blocked.']),
afterList = el('small', [
el('a', "block log" , { href: mw.util.getUrl('Special:Log/block', { page: 'User:' + user }) })
])
], {
'class': 'mw-warning-with-logexcerpt'
});
relTo.rq = buildList(user, function (list) {
relTo.rq = null;
if (!list.hasChildNodes())
return;
box.insertBefore(list, afterList);
relTo.parentNode.insertBefore(box, relTo);
});
return box;
}
var user;
var wgNamespaceNumber = mw.config.get('wgNamespaceNumber');
var wgNamespaceIds = mw.config.get('wgNamespaceIds');
var wgTitle = mw.config.get('wgTitle');
if ((wgNamespaceNumber & ~1) === wgNamespaceIds.user) {
makeBox(wgTitle.replace(/\/.*$/, ''));
} else if ((mw.config.get('wgCanonicalSpecialPageName') === 'Contributions') || (mw.config.get('wgCanonicalSpecialPageName') === 'DeletedContributions')) {
makeBox(wgTitle.indexOf('/') !== -1 ? wgTitle.replace(/^.*?\//, '') : mw.util.getParamValue('target'));
} else if (mw.config.get('wgCanonicalSpecialPageName') === 'Log') {
user = mw.util.getParamValue('user');
if (/^[^\/]+\/(block|delete)$/.test(wgTitle))
return;
if (wgTitle.indexOf('/') !== -1)
user = wgTitle.replace(/^.*\//, '');
makeBox(user);
} else if (mw.config.get('wgCanonicalSpecialPageName') === 'Block') {
var ipinput = document.getElementById('mw-bi-target');
var relTo = ipinput.form.getElementsByTagName('legend')[0].nextSibling;
var warnnode = null;
var refresh = function () {
if (relTo.rq)
relTo.rq.abort();
if (warnnode && warnnode.parentNode) {
warnnode.parentNode.removeChild(warnnode);
warnnode = null;
}
if (!ipinput.value)
return;
warnnode = makeBox(ipinput.value, relTo);
};
var tmout;
ipinput.addEventListener('input', function () {
clearTimeout(tmout);
tmout = setTimeout(refresh, 500);
}, false);
ipinput.addEventListener('change', function () {
refresh();
}, false);
refresh();
return;
}
})();
r2okjk3b3yr52txn6t4pem85ctk3im8
MediaWiki:Gadget-RevdelInfo.js
8
17959
88920
2025-09-17T22:20:59Z
en>Ioaxxere
0
[[mw:Migrating mw.Uri to URL]]
88920
javascript
text/javascript
// {{documentation}}
/*jshint undef:true */
/*global mw, jQuery */
(function(){
var api = new mw.Api();
function el(tag, child, attr, events) {
var node = document.createElement(tag);
if (child) {
if (typeof child !== 'object')
child = [child];
for (var i = 0; i < child.length; ++i) {
var ch = child[i];
if ((ch === void(null)) || (ch === null))
continue;
else if (typeof ch !== 'object')
ch = document.createTextNode(String(ch));
node.appendChild(ch);
}
}
if (attr) for (var key in attr) {
node.setAttribute(key, String(attr[key]));
}
if (events) for (var key in events) {
node.addEventListener(key, events[key], false);
}
return node;
}
function fmtDate(date) {
// XXX: uses browser's time zone instead of preferences
date = new Date(date);
switch (mw.user.options.get('date')) {
case 'dmy' : return date.toLocaleTimeString() + ', ' + date.getDate() + ' ' + mw.language.months.genitive[date.getMonth()] + ' ' + date.getFullYear();
case 'mdy' : return date.toLocaleTimeString() + ', ' + mw.language.months.genitive[date.getMonth()] + ' ' + date.getFullYear() + ', ' + date.getDate();
case 'ymd' : return date.toLocaleTimeString() + ', ' + date.getFullYear() + ' ' + mw.language.months.genitive[date.getMonth()] + ' ' + date.getDate();
case 'default' : return date.toLocaleString();
case 'ISO 8601': return date.toISOString();
}
}
function makeBox(rev1, rev2) {
var query = {
'action': 'query',
'list': 'logevents',
'leaction': 'delete/revision',
'letitle': mw.config.get('wgPageName'),
'continue': ''
};
var list, node = el('div', [
el('p', 'Details from the deletion log:'),
list = el('ul')
], {
'class': 'mw-warning-with-logexcerpt',
'style': 'display: none;'
});
function processResult(result) {
var events = result.query.logevents;
for (var i = 0; i < events.length; ++i) {
if ((events[i][1] === rev1) || (events[i][1] === rev2)) {
var item = events[i];
// XXX: item[2] and item[3] contain flags... might as well show these too
// XXX: change link to plaintext based on whether the user has the 'deleterevision' right
list.appendChild(el('li', [
fmtDate(item.timestamp), ": ",
el('a', item.user, { href: mw.util.getUrl('User:' + item.user) }),
" changed visibility of ", el('a', ["revision ", item[1]], { href: mw.util.getUrl(mw.config.get('wgPageName'), { oldid: item[1], unhide: '1' }) }),
item.comment && " (", item.comment, item.comment && ")"
]));
node.style.display = '';
}
}
if (result['continue']) {
for (var key in result['continue'])
query[key] = result['continue'][key];
api.get(query).then(processResult);
}
}
api.get(query).then(processResult);
return node;
}
if (mw.config.get('wgAction') !== 'view')
return;
if (!(mw.util.getParamValue('diff') || mw.util.getParamValue('oldid')))
return;
if (document.getElementById('mw-rev-deleted-no-diff') || document.getElementById('mw-rev-deleted-unhide-diff')) {
var rev1, rev2;
var ot = document.getElementById('mw-diff-otitle1');
var nt = document.getElementById('mw-diff-ntitle1');
if (ot) { ot = new URL(ot.getElementsByTagName('a')[0].href); rev1 = ot.searchParams.get("oldid"); }
if (nt) { nt = new URL(nt.getElementsByTagName('a')[0].href); rev2 = nt.searchParams.get("oldid"); }
document.getElementById('mw-content-text').appendChild(makeBox(rev1, rev2));
} else if (document.getElementById('mw-content-text').firstChild.classList.contains('mw-warning')) { // ugly, ugly hack
var rev = mw.util.getParamValue('oldid');
document.getElementById('mw-content-text').appendChild(makeBox(rev, null));
}
})();
qwg4k57088l5momuwnfv9a1chm6srrl
88921
88920
2026-08-06T23:13:24Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-RevdelInfo.js]]
88920
javascript
text/javascript
// {{documentation}}
/*jshint undef:true */
/*global mw, jQuery */
(function(){
var api = new mw.Api();
function el(tag, child, attr, events) {
var node = document.createElement(tag);
if (child) {
if (typeof child !== 'object')
child = [child];
for (var i = 0; i < child.length; ++i) {
var ch = child[i];
if ((ch === void(null)) || (ch === null))
continue;
else if (typeof ch !== 'object')
ch = document.createTextNode(String(ch));
node.appendChild(ch);
}
}
if (attr) for (var key in attr) {
node.setAttribute(key, String(attr[key]));
}
if (events) for (var key in events) {
node.addEventListener(key, events[key], false);
}
return node;
}
function fmtDate(date) {
// XXX: uses browser's time zone instead of preferences
date = new Date(date);
switch (mw.user.options.get('date')) {
case 'dmy' : return date.toLocaleTimeString() + ', ' + date.getDate() + ' ' + mw.language.months.genitive[date.getMonth()] + ' ' + date.getFullYear();
case 'mdy' : return date.toLocaleTimeString() + ', ' + mw.language.months.genitive[date.getMonth()] + ' ' + date.getFullYear() + ', ' + date.getDate();
case 'ymd' : return date.toLocaleTimeString() + ', ' + date.getFullYear() + ' ' + mw.language.months.genitive[date.getMonth()] + ' ' + date.getDate();
case 'default' : return date.toLocaleString();
case 'ISO 8601': return date.toISOString();
}
}
function makeBox(rev1, rev2) {
var query = {
'action': 'query',
'list': 'logevents',
'leaction': 'delete/revision',
'letitle': mw.config.get('wgPageName'),
'continue': ''
};
var list, node = el('div', [
el('p', 'Details from the deletion log:'),
list = el('ul')
], {
'class': 'mw-warning-with-logexcerpt',
'style': 'display: none;'
});
function processResult(result) {
var events = result.query.logevents;
for (var i = 0; i < events.length; ++i) {
if ((events[i][1] === rev1) || (events[i][1] === rev2)) {
var item = events[i];
// XXX: item[2] and item[3] contain flags... might as well show these too
// XXX: change link to plaintext based on whether the user has the 'deleterevision' right
list.appendChild(el('li', [
fmtDate(item.timestamp), ": ",
el('a', item.user, { href: mw.util.getUrl('User:' + item.user) }),
" changed visibility of ", el('a', ["revision ", item[1]], { href: mw.util.getUrl(mw.config.get('wgPageName'), { oldid: item[1], unhide: '1' }) }),
item.comment && " (", item.comment, item.comment && ")"
]));
node.style.display = '';
}
}
if (result['continue']) {
for (var key in result['continue'])
query[key] = result['continue'][key];
api.get(query).then(processResult);
}
}
api.get(query).then(processResult);
return node;
}
if (mw.config.get('wgAction') !== 'view')
return;
if (!(mw.util.getParamValue('diff') || mw.util.getParamValue('oldid')))
return;
if (document.getElementById('mw-rev-deleted-no-diff') || document.getElementById('mw-rev-deleted-unhide-diff')) {
var rev1, rev2;
var ot = document.getElementById('mw-diff-otitle1');
var nt = document.getElementById('mw-diff-ntitle1');
if (ot) { ot = new URL(ot.getElementsByTagName('a')[0].href); rev1 = ot.searchParams.get("oldid"); }
if (nt) { nt = new URL(nt.getElementsByTagName('a')[0].href); rev2 = nt.searchParams.get("oldid"); }
document.getElementById('mw-content-text').appendChild(makeBox(rev1, rev2));
} else if (document.getElementById('mw-content-text').firstChild.classList.contains('mw-warning')) { // ugly, ugly hack
var rev = mw.util.getParamValue('oldid');
document.getElementById('mw-content-text').appendChild(makeBox(rev, null));
}
})();
qwg4k57088l5momuwnfv9a1chm6srrl
MediaWiki:Gadget-CodeLinks.js
8
17960
88922
2024-05-14T20:51:32Z
en>Benwing
0
[[User:Theknightwho]] fix tracking links in modules
88922
javascript
text/javascript
/*jshint undef:true, latedef:true */
/*global mw, jQuery */
jQuery(function CodeLinksIIFE() {
'use strict';
// by John Gruber, from https://daringfireball.net/2010/07/improved_regex_for_matching_urls
var URLRegExp = /\b((?:https?:\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/i;
function processComment(node) {
var wikilinkMatch, templateMatch, URLMatch,
textNode = node.firstChild; // always a text node.
while (
(wikilinkMatch = /\[\[([^|{}\[\]\n]+)?(?:\|.*?)?]]/.exec(textNode.data))
|| (templateMatch = /(\{\{(?:#invoke:)?)([^|{}\[\]\n#]+)(?=\||}})/i.exec(textNode.data))
|| (URLMatch = URLRegExp.exec(textNode.data))
) {
var link = document.createElement('a'),
start = (wikilinkMatch || templateMatch || URLMatch).index,
linkText;
link.classList.add("code-link");
if (URLMatch) {
var URL = URLMatch[0];
link.href = URL;
linkText = URL;
} else {
var fullPageName;
if (wikilinkMatch) {
linkText = wikilinkMatch[0];
fullPageName = wikilinkMatch[1];
} else if (templateMatch) {
var prefix = templateMatch[1],
pageName = templateMatch[2];
linkText = pageName;
fullPageName = (prefix === '{{#invoke:' ? 'Module:' : 'Template:')
+ pageName;
link.title = fullPageName;
start += prefix.length;
}
link.href = mw.util.getUrl(fullPageName);
}
var beforeLink = textNode.data.substring(0, start),
afterLink = textNode.data.substring(start + linkText.length);
textNode.data = afterLink;
link.appendChild(document.createTextNode(linkText));
node.insertBefore(link, textNode);
node.insertBefore(document.createTextNode(beforeLink), link);
// ensure all matches are null at beginning of loop body; is this necessary?
wikilinkMatch = templateMatch = URLMatch = null;
}
}
function each(coll, walk) {
return Array.prototype.forEach.call(coll, walk);
}
var commentClasses = [ 'c', 'c1', 'cm' ];
each(document.getElementsByClassName('mw-highlight'), function (codeBlock) {
each(commentClasses, function (commentClass) {
each(codeBlock.getElementsByClassName(commentClass), processComment);
});
});
// Link module names after `require` and `mw.loadData`, and tracking page
// names after `require("Module:debug").track`.
var copyArray = Array.from ? Array.from.bind(Array) :
function copyArray(array) {
return Array.prototype.slice.call(array);
};
var classes = {
identifier: "n", functionName: "nb",
singleQuoteString: "s1", doubleQuoteString: "s2",
};
var trackingPageElements = [], moduleNames = [], dataModuleNames = [];
var functionNames = document.getElementsByClassName(classes.functionName);
Array.prototype.forEach.call(functionNames, function (functionName) {
var text = functionName.firstChild.nodeValue;
if (text !== "require")
return;
var next = functionName.nextElementSibling;
var nextText = next && next.firstChild && next.firstChild.nodeValue;
var hasParenthesis = nextText === "(";
if (hasParenthesis) {
next = next.nextElementSibling;
nextText = next && next.firstChild && next.firstChild.nodeValue;
}
var classList = next.classList;
if (!(classList.contains(classes.singleQuoteString)
|| classList.contains(classes.doubleQuoteString)))
return;
var string = next;
var stringValue = nextText;
if (!stringValue)
return;
next = next.nextElementSibling;
nextText = next && next.firstChild && next.firstChild.nodeValue;
if (hasParenthesis && nextText && nextText[0] !== ")")
return;
moduleNames.push(string);
// FIXME!!! Tracking code has been moved to [[Module:debug/track]]. The following needs updating
// to handle calls of the form require("Module:debug/track")("foo").
if (hasParenthesis ? nextText === ")." : nextText === "."
&& (/^["']mod(?:ule)?:(.+)["']$/i.exec(stringValue) || [])[1] === "debug") {
next = next.nextElementSibling;
nextText = next && next.firstChild && next.firstChild.nodeValue;
if (nextText !== "track")
return;
next = next.nextElementSibling;
if (!next) return;
nextText = next && next.firstChild && next.firstChild.nodeValue;
var trackWithParenthesis = false;
if (nextText === "(") {
next = next.nextElementSibling;
trackWithParenthesis = true;
}
classList = next.classList;
if (!(classList.contains(classes.singleQuoteString)
|| classList.contains(classes.doubleQuoteString)))
return;
nextText = next && next.firstChild && next.firstChild.nodeValue;
if (!nextText)
return;
// If there was a parenthesis on one side – `track("...")` rather than
// `track "..."` – make sure there's a matching parenthesis on the other side.
if (trackWithParenthesis) {
var after = next.nextElementSibling;
var afterText = after && after.firstChild && after.firstChild.nodeValue;
if (afterText !== ")")
return;
}
trackingPageElements.push(next);
}
});
var strings = copyArray(document.getElementsByClassName(classes.singleQuoteString))
.concat(copyArray(document.getElementsByClassName(classes.doubleQuoteString)));
Array.prototype.forEach.call(strings, function (string) {
if (moduleNames.indexOf(string) !== -1 || trackingPageElements.indexOf(string) !== -1)
return;
var stringValue = string.firstChild.nodeValue;
if (!/^["'](?:module|mod):/i.test(stringValue))
return;
var prev = string.previousElementSibling;
var prevText = prev && prev.firstChild && prev.firstChild.nodeValue;
if (prevText === "(") {
var next = string.nextElementSibling;
var nextText = next && next.firstChild && next.firstChild.nodeValue;
if (!(nextText && nextText[0] === ")"))
return;
prev = prev.previousElementSibling;
prevText = prev && prev.firstChild && prev.firstChild.nodeValue;
}
if (prevText !== "loadData")
return;
prev = prev.previousElementSibling;
prevText = prev && prev.firstChild && prev.firstChild.nodeValue;
if (prevText !== ".")
return;
prev = prev.previousElementSibling;
prevText = prev && prev.firstChild && prev.firstChild.nodeValue;
if (prevText !== "mw")
return;
dataModuleNames.push(string);
});
if (moduleNames.length > 0 || trackingPageElements.length > 0
|| dataModuleNames.length > 0) {
mw.loader.using("mediawiki.util").done(function () {
function addLink(element, page) {
if (!(element instanceof Element))
throw new TypeError("Expected Element object");
var link = document.createElement("a");
link.href = mw.util.getUrl(page);
// put text node from element inside link
var firstChild = element.firstChild;
if (!(firstChild instanceof Text))
throw new TypeError("Expected Text object");
link.appendChild(firstChild);
element.appendChild(link); // put link inside syntax-highlighted string
}
// Link module names to module pages, or to the section in the Scribunto
// manual.
moduleNames.concat(dataModuleNames).forEach(function (module) {
var link = document.createElement("a");
var stringValue = module.firstChild.nodeValue;
var moduleName = stringValue.substring(1, stringValue.length - 1);
var linkPage = /^mod(?:ule)?:/i.test(moduleName)
? moduleName
: "mw:Extension:Scribunto/Lua reference manual#" + moduleName;
addLink(module, linkPage);
});
// Link tracking pages to [[Special:WhatLinksHere]].
trackingPageElements.forEach(function (trackingPage) {
var text = trackingPage.firstChild && trackingPage.firstChild.nodeValue;
if (!text)
return;
var trackingCode = text.substring(1, text.length - 1);
addLink(trackingPage, "Special:WhatLinksHere/Wiktionary:Tracking/" + trackingCode);
});
});
}
});
65v4x8jlyzdth8j2uvj9sfa5wzt9tr0
88923
88922
2026-08-06T23:13:31Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-CodeLinks.js]]
88922
javascript
text/javascript
/*jshint undef:true, latedef:true */
/*global mw, jQuery */
jQuery(function CodeLinksIIFE() {
'use strict';
// by John Gruber, from https://daringfireball.net/2010/07/improved_regex_for_matching_urls
var URLRegExp = /\b((?:https?:\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/i;
function processComment(node) {
var wikilinkMatch, templateMatch, URLMatch,
textNode = node.firstChild; // always a text node.
while (
(wikilinkMatch = /\[\[([^|{}\[\]\n]+)?(?:\|.*?)?]]/.exec(textNode.data))
|| (templateMatch = /(\{\{(?:#invoke:)?)([^|{}\[\]\n#]+)(?=\||}})/i.exec(textNode.data))
|| (URLMatch = URLRegExp.exec(textNode.data))
) {
var link = document.createElement('a'),
start = (wikilinkMatch || templateMatch || URLMatch).index,
linkText;
link.classList.add("code-link");
if (URLMatch) {
var URL = URLMatch[0];
link.href = URL;
linkText = URL;
} else {
var fullPageName;
if (wikilinkMatch) {
linkText = wikilinkMatch[0];
fullPageName = wikilinkMatch[1];
} else if (templateMatch) {
var prefix = templateMatch[1],
pageName = templateMatch[2];
linkText = pageName;
fullPageName = (prefix === '{{#invoke:' ? 'Module:' : 'Template:')
+ pageName;
link.title = fullPageName;
start += prefix.length;
}
link.href = mw.util.getUrl(fullPageName);
}
var beforeLink = textNode.data.substring(0, start),
afterLink = textNode.data.substring(start + linkText.length);
textNode.data = afterLink;
link.appendChild(document.createTextNode(linkText));
node.insertBefore(link, textNode);
node.insertBefore(document.createTextNode(beforeLink), link);
// ensure all matches are null at beginning of loop body; is this necessary?
wikilinkMatch = templateMatch = URLMatch = null;
}
}
function each(coll, walk) {
return Array.prototype.forEach.call(coll, walk);
}
var commentClasses = [ 'c', 'c1', 'cm' ];
each(document.getElementsByClassName('mw-highlight'), function (codeBlock) {
each(commentClasses, function (commentClass) {
each(codeBlock.getElementsByClassName(commentClass), processComment);
});
});
// Link module names after `require` and `mw.loadData`, and tracking page
// names after `require("Module:debug").track`.
var copyArray = Array.from ? Array.from.bind(Array) :
function copyArray(array) {
return Array.prototype.slice.call(array);
};
var classes = {
identifier: "n", functionName: "nb",
singleQuoteString: "s1", doubleQuoteString: "s2",
};
var trackingPageElements = [], moduleNames = [], dataModuleNames = [];
var functionNames = document.getElementsByClassName(classes.functionName);
Array.prototype.forEach.call(functionNames, function (functionName) {
var text = functionName.firstChild.nodeValue;
if (text !== "require")
return;
var next = functionName.nextElementSibling;
var nextText = next && next.firstChild && next.firstChild.nodeValue;
var hasParenthesis = nextText === "(";
if (hasParenthesis) {
next = next.nextElementSibling;
nextText = next && next.firstChild && next.firstChild.nodeValue;
}
var classList = next.classList;
if (!(classList.contains(classes.singleQuoteString)
|| classList.contains(classes.doubleQuoteString)))
return;
var string = next;
var stringValue = nextText;
if (!stringValue)
return;
next = next.nextElementSibling;
nextText = next && next.firstChild && next.firstChild.nodeValue;
if (hasParenthesis && nextText && nextText[0] !== ")")
return;
moduleNames.push(string);
// FIXME!!! Tracking code has been moved to [[Module:debug/track]]. The following needs updating
// to handle calls of the form require("Module:debug/track")("foo").
if (hasParenthesis ? nextText === ")." : nextText === "."
&& (/^["']mod(?:ule)?:(.+)["']$/i.exec(stringValue) || [])[1] === "debug") {
next = next.nextElementSibling;
nextText = next && next.firstChild && next.firstChild.nodeValue;
if (nextText !== "track")
return;
next = next.nextElementSibling;
if (!next) return;
nextText = next && next.firstChild && next.firstChild.nodeValue;
var trackWithParenthesis = false;
if (nextText === "(") {
next = next.nextElementSibling;
trackWithParenthesis = true;
}
classList = next.classList;
if (!(classList.contains(classes.singleQuoteString)
|| classList.contains(classes.doubleQuoteString)))
return;
nextText = next && next.firstChild && next.firstChild.nodeValue;
if (!nextText)
return;
// If there was a parenthesis on one side – `track("...")` rather than
// `track "..."` – make sure there's a matching parenthesis on the other side.
if (trackWithParenthesis) {
var after = next.nextElementSibling;
var afterText = after && after.firstChild && after.firstChild.nodeValue;
if (afterText !== ")")
return;
}
trackingPageElements.push(next);
}
});
var strings = copyArray(document.getElementsByClassName(classes.singleQuoteString))
.concat(copyArray(document.getElementsByClassName(classes.doubleQuoteString)));
Array.prototype.forEach.call(strings, function (string) {
if (moduleNames.indexOf(string) !== -1 || trackingPageElements.indexOf(string) !== -1)
return;
var stringValue = string.firstChild.nodeValue;
if (!/^["'](?:module|mod):/i.test(stringValue))
return;
var prev = string.previousElementSibling;
var prevText = prev && prev.firstChild && prev.firstChild.nodeValue;
if (prevText === "(") {
var next = string.nextElementSibling;
var nextText = next && next.firstChild && next.firstChild.nodeValue;
if (!(nextText && nextText[0] === ")"))
return;
prev = prev.previousElementSibling;
prevText = prev && prev.firstChild && prev.firstChild.nodeValue;
}
if (prevText !== "loadData")
return;
prev = prev.previousElementSibling;
prevText = prev && prev.firstChild && prev.firstChild.nodeValue;
if (prevText !== ".")
return;
prev = prev.previousElementSibling;
prevText = prev && prev.firstChild && prev.firstChild.nodeValue;
if (prevText !== "mw")
return;
dataModuleNames.push(string);
});
if (moduleNames.length > 0 || trackingPageElements.length > 0
|| dataModuleNames.length > 0) {
mw.loader.using("mediawiki.util").done(function () {
function addLink(element, page) {
if (!(element instanceof Element))
throw new TypeError("Expected Element object");
var link = document.createElement("a");
link.href = mw.util.getUrl(page);
// put text node from element inside link
var firstChild = element.firstChild;
if (!(firstChild instanceof Text))
throw new TypeError("Expected Text object");
link.appendChild(firstChild);
element.appendChild(link); // put link inside syntax-highlighted string
}
// Link module names to module pages, or to the section in the Scribunto
// manual.
moduleNames.concat(dataModuleNames).forEach(function (module) {
var link = document.createElement("a");
var stringValue = module.firstChild.nodeValue;
var moduleName = stringValue.substring(1, stringValue.length - 1);
var linkPage = /^mod(?:ule)?:/i.test(moduleName)
? moduleName
: "mw:Extension:Scribunto/Lua reference manual#" + moduleName;
addLink(module, linkPage);
});
// Link tracking pages to [[Special:WhatLinksHere]].
trackingPageElements.forEach(function (trackingPage) {
var text = trackingPage.firstChild && trackingPage.firstChild.nodeValue;
if (!text)
return;
var trackingCode = text.substring(1, text.length - 1);
addLink(trackingPage, "Special:WhatLinksHere/Wiktionary:Tracking/" + trackingCode);
});
});
}
});
65v4x8jlyzdth8j2uvj9sfa5wzt9tr0
MediaWiki:Gadget-purgetab.js
8
17961
88924
2019-09-25T03:58:47Z
en>Krinkle
0
Maintenance: [[mw:RL/MGU]] - Updated deprecated user.tokens key
88924
javascript
text/javascript
// {{documentation}}
jQuery(document).ready(function() {
if (!mw.config.get('wgArticleId'))
return;
var link;
var portletIdToAdd = $("#p-cactions").length != 0 ? "p-cactions" : "p-views";
link = mw.util.addPortletLink(
portletIdToAdd, mw.util.getUrl(mw.config.get('wgPageName'), {
'action': 'purge'
}),
(mw.config.get('skin') == "vector") ? "Purge" : "*",
'ca-purge', "Purge cache for this page", '*'
);
var api = new mw.Api();
link = mw.util.addPortletLink(
portletIdToAdd, mw.util.getUrl(mw.config.get('wgPageName'), {
'action': 'purge',
'forcelinkupdate': 1
}), // not sure if raw link works...
(mw.config.get('skin') == "vector") ? "Hard purge" : "**",
'ca-purge-forcelinkupdate', "Purge with forced link table update", ','
);
link.addEventListener('click', function(ev) {
api.post({
action: 'purge',
pageids: mw.config.get('wgArticleId'),
forcelinkupdate: 1
}).then(function() {
location.reload();
}, function(code, details) {
var mesg;
switch (code) {
case 'http':
mesg = 'HTTP error: ' + details.xhr.statusText;
break;
case 'ok-but-empty':
mesg = 'Received empty response.';
break;
default:
mesg = details.error.info;
}
mw.util.jsMessage('<b>Hard purge failed</b>: ' + mesg);
console.error(arguments);
});
ev.preventDefault();
}, false);
link = mw.util.addPortletLink(
portletIdToAdd, 'javascript:void window.warranty',
(mw.config.get('skin') == "vector") ? "Null edit" : "***",
'ca-nulledit', "Null edit", '0'
);
link.addEventListener('click', function(ev) {
api.post({
action: 'edit',
pageid: mw.config.get('wgArticleId'),
appendtext: '',
watchlist: 'nochange',
nocreate: '1',
token: mw.user.tokens.get('csrfToken')
}).then(function() {
location.reload();
}, function(code, details) {
var mesg;
switch (code) {
case 'http':
mesg = 'HTTP error: ' + details.xhr.statusText;
break;
case 'ok-but-empty':
mesg = 'Received empty response.';
break;
default:
mesg = details.error.info;
}
mw.util.jsMessage('<b>Null edit failed</b>: ' + mesg);
console.error(arguments);
});
ev.preventDefault();
}, false);
});
0l8v0diur942q6kq5oa89o3eoji1t0e
88925
88924
2026-08-06T23:13:40Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-purgetab.js]]
88924
javascript
text/javascript
// {{documentation}}
jQuery(document).ready(function() {
if (!mw.config.get('wgArticleId'))
return;
var link;
var portletIdToAdd = $("#p-cactions").length != 0 ? "p-cactions" : "p-views";
link = mw.util.addPortletLink(
portletIdToAdd, mw.util.getUrl(mw.config.get('wgPageName'), {
'action': 'purge'
}),
(mw.config.get('skin') == "vector") ? "Purge" : "*",
'ca-purge', "Purge cache for this page", '*'
);
var api = new mw.Api();
link = mw.util.addPortletLink(
portletIdToAdd, mw.util.getUrl(mw.config.get('wgPageName'), {
'action': 'purge',
'forcelinkupdate': 1
}), // not sure if raw link works...
(mw.config.get('skin') == "vector") ? "Hard purge" : "**",
'ca-purge-forcelinkupdate', "Purge with forced link table update", ','
);
link.addEventListener('click', function(ev) {
api.post({
action: 'purge',
pageids: mw.config.get('wgArticleId'),
forcelinkupdate: 1
}).then(function() {
location.reload();
}, function(code, details) {
var mesg;
switch (code) {
case 'http':
mesg = 'HTTP error: ' + details.xhr.statusText;
break;
case 'ok-but-empty':
mesg = 'Received empty response.';
break;
default:
mesg = details.error.info;
}
mw.util.jsMessage('<b>Hard purge failed</b>: ' + mesg);
console.error(arguments);
});
ev.preventDefault();
}, false);
link = mw.util.addPortletLink(
portletIdToAdd, 'javascript:void window.warranty',
(mw.config.get('skin') == "vector") ? "Null edit" : "***",
'ca-nulledit', "Null edit", '0'
);
link.addEventListener('click', function(ev) {
api.post({
action: 'edit',
pageid: mw.config.get('wgArticleId'),
appendtext: '',
watchlist: 'nochange',
nocreate: '1',
token: mw.user.tokens.get('csrfToken')
}).then(function() {
location.reload();
}, function(code, details) {
var mesg;
switch (code) {
case 'http':
mesg = 'HTTP error: ' + details.xhr.statusText;
break;
case 'ok-but-empty':
mesg = 'Received empty response.';
break;
default:
mesg = details.error.info;
}
mw.util.jsMessage('<b>Null edit failed</b>: ' + mesg);
console.error(arguments);
});
ev.preventDefault();
}, false);
});
0l8v0diur942q6kq5oa89o3eoji1t0e
MediaWiki:Gadget-popups.js
8
17962
88926
2023-12-03T04:46:35Z
en>Erutuon
0
added semicolon because of Firefox's parsing error in minified JavaScript at the end of this line
88926
javascript
text/javascript
// {{documentation}}
// _________________________________________________________________________________________
// | |
// | === WARNING: GLOBAL GADGET FILE === |
// | Changes to this page affect many users. |
// | Please discuss changes on the [[Wiktionary:Grease Pit]] before editing. |
// |_________________________________________________________________________________________|
mw.loader.load('//en.wikipedia.org/w/index.php?action=raw&title=MediaWiki:Gadget-navpop.css&ctype=text/css', "text/css");
// Forked from [[:w:MediaWiki:Gadget-popups.js]]
$(function () {
//////////////////////////////////////////////////
// Globals
//
// Trying to shove as many of these as possible into the pg (popup globals) object
var pg = {
re: {}, // regexps
ns: {}, // namespaces
string: {}, // translatable strings
wiki: {}, // local site info
user: {}, // current user info
misc: {}, // YUCK PHOOEY
option: {}, // options, see newOption etc
optionDefault: {}, // default option values
flag: {}, // misc flags
cache: {}, // page and image cache
structures: {}, // navlink structures
timer: {}, // all sorts of timers (too damn many)
counter: {}, // .. and all sorts of counters
current: {}, // state info
fn: {}, // functions
endoflist: null
};
/* Bail if the gadget/script is being loaded twice */
if( window.pg ) {
return;
}
/* Export to global context */
window.pg = pg;
/// Local Variables: ///
/// mode:c ///
/// End: ///
// ENDFILE: main.js
// STARTFILE: actions.js
function setupTooltips(container, remove, force, popData) {
log('setupTooltips, container='+container+', remove='+remove);
if (!container) {
//<NOLITE>
// the main initial call
if (getValueOf('popupOnEditSelection') && document && document.editform && document.editform.wpTextbox1) {
document.editform.wpTextbox1.onmouseup=doSelectionPopup;
}
//</NOLITE>
// article/content is a structure-dependent thing
container = defaultPopupsContainer();
}
if (!remove && !force && container.ranSetupTooltipsAlready) { return; }
container.ranSetupTooltipsAlready = !remove;
var anchors;
anchors=container.getElementsByTagName('A');
setupTooltipsLoop(anchors, 0, 250, 100, remove, popData);
}
function defaultPopupsContainer() {
if (getValueOf('popupOnlyArticleLinks')) {
return document.getElementById('mw_content') ||
document.getElementById('content') ||
document.getElementById('article') || document;
}
return document;
}
function setupTooltipsLoop(anchors,begin,howmany,sleep, remove, popData) {
log(simplePrintf('setupTooltipsLoop(%s,%s,%s,%s,%s)', arguments));
var finish=begin+howmany;
var loopend = Math.min(finish, anchors.length);
var j=loopend - begin;
log ('setupTooltips: anchors.length=' + anchors.length + ', begin=' + begin +
', howmany=' + howmany + ', loopend=' + loopend + ', remove=' + remove);
var doTooltip= remove ? removeTooltip : addTooltip;
// try a faster (?) loop construct
if (j > 0) {
do {
var a=anchors[loopend - j];
if (typeof a==='undefined' || !a || !a.href) {
log('got null anchor at index ' + loopend - j);
continue;
}
doTooltip(a, popData);
} while (--j);
}
if (finish < anchors.length) {
setTimeout(function() {
setupTooltipsLoop(anchors,finish,howmany,sleep,remove,popData);},
sleep);
} else {
if ( !remove && ! getValueOf('popupTocLinks')) { rmTocTooltips(); }
pg.flag.finishedLoading=true;
}
}
// eliminate popups from the TOC
// This also kills any onclick stuff that used to be going on in the toc
function rmTocTooltips() {
var toc=document.getElementById('toc');
if (toc) {
var tocLinks=toc.getElementsByTagName('A');
var tocLen = tocLinks.length;
for (var j=0; j<tocLen; ++j) {
removeTooltip(tocLinks[j], true);
}
}
}
function addTooltip(a, popData) {
if ( !isPopupLink(a) ) { return; }
a.onmouseover=mouseOverWikiLink;
a.onmouseout= mouseOutWikiLink;
a.onmousedown = killPopup;
a.hasPopup = true;
a.popData = popData;
}
function removeTooltip(a) {
if ( !a.hasPopup ) { return; }
a.onmouseover = null;
a.onmouseout = null;
if (a.originalTitle) { a.title = a.originalTitle; }
a.hasPopup=false;
}
function removeTitle(a) {
if (!a.originalTitle) {
a.originalTitle=a.title;
}
a.title='';
}
function restoreTitle(a) {
if ( a.title || !a.originalTitle ) { return; }
a.title = a.originalTitle;
}
function registerHooks(np) {
var popupMaxWidth=getValueOf('popupMaxWidth');
if (typeof popupMaxWidth === 'number') {
var setMaxWidth = function () {
np.mainDiv.style.maxWidth = popupMaxWidth + 'px';
np.maxWidth = popupMaxWidth;
};
np.addHook(setMaxWidth, 'unhide', 'before');
}
//<NOLITE>
np.addHook(addPopupShortcuts, 'unhide', 'after');
np.addHook(rmPopupShortcuts, 'hide', 'before');
//</NOLITE>
}
function removeModifierKeyHandler(a) {
//remove listeners for modifier key if any that were added in mouseOverWikiLink
document.removeEventListener('keydown', a.modifierKeyHandler, false);
document.removeEventListener('keyup', a.modifierKeyHandler, false);
}
function mouseOverWikiLink(evt) {
if (!evt && window.event) {evt=window.event;}
// if the modifier is needed, listen for it,
// we will remove the listener when we mouseout of this link or kill popup.
if (getValueOf('popupModifier')) {
// if popupModifierAction = enable, we should popup when the modifier is pressed
// if popupModifierAction = disable, we should popup unless the modifier is pressed
var action = getValueOf('popupModifierAction');
var key = action=='disable' ? 'keyup' : 'keydown';
var a = this;
a.modifierKeyHandler = function(evt) {
mouseOverWikiLink2(a, evt);
};
document.addEventListener(key, a.modifierKeyHandler, false);
}
return mouseOverWikiLink2(this, evt);
}
/**
* Gets the references list item that the provided footnote link targets. This
* is typically a li element within the ol.references element inside the reflist.
* @param {Element} a - A footnote link.
* @returns {Element|boolean} The targeted element, or false if one can't be found.
*/
function footnoteTarget(a) {
var aTitle=Title.fromAnchor(a);
// We want ".3A" rather than "%3A" or "?" here, so use the anchor property directly
var anch = aTitle.anchor;
if ( ! /^(cite_note-|_note-|endnote)/.test(anch) ) { return false; }
var lTitle=Title.fromURL(location.href);
if ( lTitle.toString(true) !== aTitle.toString(true) ) { return false; }
var el=document.getElementById(anch);
while ( el && typeof el.nodeName === 'string') {
var nt = el.nodeName.toLowerCase();
if ( nt === 'li' ) { return el; }
else if ( nt === 'body' ) { return false; }
else if ( el.parentNode ) { el=el.parentNode; }
else { return false; }
}
return false;
}
function footnotePreview(x, navpop) {
setPopupHTML('<hr />' + x.innerHTML, 'popupPreview', navpop.idNumber);
}
function modifierPressed(evt) {
var mod=getValueOf('popupModifier');
if (!mod) { return false; }
if (!evt && window.event) {evt=window.event;}
return ( evt && mod && evt[mod.toLowerCase() + 'Key'] );
}
// Checks if the correct modifier pressed/unpressed if needed
function isCorrectModifier(a,evt) {
if (!getValueOf('popupModifier')) { return true; }
// if popupModifierAction = enable, we should popup when the modifier is pressed
// if popupModifierAction = disable, we should popup unless the modifier is pressed
var action = getValueOf('popupModifierAction');
return ( action == 'enable' && modifierPressed(evt) ||
action == 'disable' && !modifierPressed(evt) );
}
function mouseOverWikiLink2(a, evt) {
if (!isCorrectModifier(a,evt)) { return; }
if ( getValueOf('removeTitles') ) { removeTitle(a); }
if ( a==pg.current.link && a.navpopup && a.navpopup.isVisible() ) { return; }
pg.current.link=a;
if (getValueOf('simplePopups') && !pg.option.popupStructure) {
// reset *default value* of popupStructure
setDefault('popupStructure', 'original');
}
var article=(new Title()).fromAnchor(a);
// set global variable (ugh) to hold article (wikipage)
pg.current.article = article;
if (!a.navpopup) {
a.navpopup=newNavpopup(a, article);
pg.current.linksHash[a.href] = a.navpopup;
pg.current.links.push(a);
}
if (a.navpopup.pending === null || a.navpopup.pending !== 0) {
// either fresh popups or those with unfinshed business are redone from scratch
simplePopupContent(a, article);
}
a.navpopup.showSoonIfStable(a.navpopup.delay);
clearInterval(pg.timer.checkPopupPosition);
pg.timer.checkPopupPosition=setInterval(checkPopupPosition, 600);
if(getValueOf('simplePopups')) {
if (getValueOf('popupPreviewButton') && !a.simpleNoMore) {
var d=document.createElement('div');
d.className='popupPreviewButtonDiv';
var s=document.createElement('span');
d.appendChild(s);
s.className='popupPreviewButton';
s['on' + getValueOf('popupPreviewButtonEvent')] = function() {
a.simpleNoMore=true;
d.style.display = "none";
nonsimplePopupContent(a,article);
};
s.innerHTML=popupString('show preview');
setPopupHTML(d, 'popupPreview', a.navpopup.idNumber);
}
}
if (a.navpopup.pending !== 0 ) {
nonsimplePopupContent(a, article);
}
}
// simplePopupContent: the content that do not require additional download
// (it is shown even when simplePopups is true)
function simplePopupContent(a, article) {
/* FIXME hack */ a.navpopup.hasPopupMenu=false;
a.navpopup.setInnerHTML(popupHTML(a));
fillEmptySpans({navpopup:a.navpopup});
if (getValueOf('popupDraggable'))
{
var dragHandle = getValueOf('popupDragHandle') || null;
if (dragHandle && dragHandle != 'all') {
dragHandle += a.navpopup.idNumber;
}
setTimeout(function(){a.navpopup.makeDraggable(dragHandle);}, 150);
}
//<NOLITE>
if (getValueOf('popupRedlinkRemoval') && a.className=='new') {
setPopupHTML('<br>'+popupRedlinkHTML(article), 'popupRedlink', a.navpopup.idNumber);
}
//</NOLITE>
}
function debugData(navpopup) {
if(getValueOf('popupDebugging') && navpopup.idNumber) {
setPopupHTML('idNumber='+navpopup.idNumber + ', pending=' + navpopup.pending,
'popupError', navpopup.idNumber);
}
}
function newNavpopup(a, article) {
var navpopup = new Navpopup();
navpopup.fuzz=5;
navpopup.delay=getValueOf('popupDelay')*1000;
// increment global counter now
navpopup.idNumber = ++pg.idNumber;
navpopup.parentAnchor = a;
navpopup.parentPopup = (a.popData && a.popData.owner);
navpopup.article = article;
registerHooks(navpopup);
return navpopup;
}
// Should we show nonsimple context?
// If simplePopups is set to true, then we do not show nonsimple context,
// but if a bottom "show preview" was clicked we do show nonsimple context
function shouldShowNonSimple(a) {
return !getValueOf('simplePopups') || a.simpleNoMore;
}
// Should we show nonsimple context govern by the option (e.g. popupUserInfo)?
// If the user explicitly asked for nonsimple context by setting the option to true,
// then we show it even in nonsimple mode.
function shouldShow(a,option) {
if (shouldShowNonSimple(a)) {
return getValueOf(option);
} else {
return (typeof window[option] != 'undefined' ) && window[option];
}
}
function nonsimplePopupContent(a, article) {
var diff=null, history=null;
var params=parseParams(a.href);
var oldid=(typeof params.oldid=='undefined' ? null : params.oldid);
//<NOLITE>
if(shouldShow(a,'popupPreviewDiffs')) {
diff=params.diff;
}
if(shouldShow(a,'popupPreviewHistory')) {
history=(params.action=='history');
}
//</NOLITE>
a.navpopup.pending=0;
var referenceElement = footnoteTarget(a);
if (referenceElement) {
footnotePreview(referenceElement, a.navpopup);
//<NOLITE>
} else if ( diff || diff === 0 ) {
loadDiff(article, oldid, diff, a.navpopup);
} else if ( history ) {
loadAPIPreview('history', article, a.navpopup);
} else if ( shouldShowNonSimple(a) && pg.re.contribs.test(a.href) ) {
loadAPIPreview('contribs', article, a.navpopup);
} else if ( shouldShowNonSimple(a) && pg.re.backlinks.test(a.href) ) {
loadAPIPreview('backlinks', article, a.navpopup);
} else if ( // FIXME should be able to get all preview combinations with options
article.namespaceId()==pg.nsImageId &&
( shouldShow(a,'imagePopupsForImages') || ! anchorContainsImage(a) )
) {
loadAPIPreview('imagepagepreview', article, a.navpopup);
loadImage(article, a.navpopup);
//</NOLITE>
} else {
if (article.namespaceId() == pg.nsCategoryId &&
shouldShow(a,'popupCategoryMembers')) {
loadAPIPreview('category', article, a.navpopup);
} else if ((article.namespaceId() == pg.nsUserId || article.namespaceId() == pg.nsUsertalkId) &&
shouldShow(a,'popupUserInfo')) {
loadAPIPreview('userinfo', article, a.navpopup);
}
if (shouldShowNonSimple(a)) startArticlePreview(article, oldid, a.navpopup);
}
}
function pendingNavpopTask(navpop) {
if (navpop && navpop.pending === null) { navpop.pending=0; }
++navpop.pending;
debugData(navpop);
}
function completedNavpopTask(navpop) {
if (navpop && navpop.pending) { --navpop.pending; }
debugData(navpop);
}
function startArticlePreview(article, oldid, navpop) {
navpop.redir=0;
loadPreview(article, oldid, navpop);
}
function loadPreview(article, oldid, navpop) {
if (!navpop.redir) { navpop.originalArticle=article; }
article.oldid = oldid;
loadAPIPreview('revision', article, navpop);
}
function loadPreviewFromRedir(redirMatch, navpop) {
// redirMatch is a regex match
var target = new Title().fromWikiText(redirMatch[2]);
// overwrite (or add) anchor from original target
// mediawiki does overwrite; eg [[User:Lupin/foo3#Done]]
if ( navpop.article.anchor ) { target.anchor = navpop.article.anchor; }
navpop.redir++;
navpop.redirTarget=target;
//<NOLITE>
var warnRedir = redirLink(target, navpop.article);
setPopupHTML(warnRedir, 'popupWarnRedir', navpop.idNumber);
//</NOLITE>
navpop.article=target;
fillEmptySpans({redir: true, redirTarget: target, navpopup:navpop});
return loadPreview(target, null, navpop);
}
function insertPreview(download) {
if (!download.owner) { return; }
var redirMatch = pg.re.redirect.exec(download.data);
if (download.owner.redir === 0 && redirMatch) {
loadPreviewFromRedir(redirMatch, download.owner);
return;
}
if (download.owner.visible || !getValueOf('popupLazyPreviews')) {
insertPreviewNow(download);
} else {
var id=(download.owner.redir) ? 'PREVIEW_REDIR_HOOK' : 'PREVIEW_HOOK';
download.owner.addHook( function(){insertPreviewNow(download); return true;},
'unhide', 'after', id );
}
}
function insertPreviewNow(download) {
if (!download.owner) { return; }
var wikiText=download.data;
var navpop=download.owner;
var art=navpop.redirTarget || navpop.originalArticle;
//<NOLITE>
makeFixDabs(wikiText, navpop);
if (getValueOf('popupSummaryData')) {
getPageInfo(wikiText, download);
setPopupTrailer(getPageInfo(wikiText, download), navpop.idNumber);
}
var imagePage='';
if (art.namespaceId()==pg.nsImageId) { imagePage=art.toString(); }
else { imagePage=getValidImageFromWikiText(wikiText); }
if(imagePage) { loadImage(Title.fromWikiText(imagePage), navpop); }
//</NOLITE>
if (getValueOf('popupPreviews')) { insertArticlePreview(download, art, navpop); }
}
function insertArticlePreview(download, art, navpop) {
if (download && typeof download.data == typeof ''){
if (art.namespaceId()==pg.nsTemplateId && getValueOf('popupPreviewRawTemplates')) {
// FIXME compare/consolidate with diff escaping code for wikitext
var h='<hr /><span style="font-family: monospace;">' + download.data.entify().split('\\n').join('<br />\\n') + '</span>';
setPopupHTML(h, 'popupPreview', navpop.idNumber);
}
else {
var p=prepPreviewmaker(download.data, art, navpop);
p.showPreview();
}
}
}
function prepPreviewmaker(data, article, navpop) {
// deal with tricksy anchors
var d=anchorize(data, article.anchorString());
var urlBase=joinPath([pg.wiki.articlebase, article.urlString()]);
var p=new Previewmaker(d, urlBase, navpop);
return p;
}
// Try to imitate the way mediawiki generates HTML anchors from section titles
function anchorize(d, anch) {
if (!anch) { return d; }
var anchRe=RegExp('(?:=+\\s*' + literalizeRegex(anch).replace(/[_ ]/g, '[_ ]') + '\\s*=+|\\{\\{\\s*'+getValueOf('popupAnchorRegexp')+'\\s*(?:\\|[^|}]*)*?\\s*'+literalizeRegex(anch)+'\\s*(?:\\|[^}]*)?}})');
var match=d.match(anchRe);
if(match && match.length > 0 && match[0]) { return d.substring(d.indexOf(match[0])); }
// now try to deal with == foo [[bar|baz]] boom == -> #foo_baz_boom
var lines=d.split('\n');
for (var i=0; i<lines.length; ++i) {
lines[i]=lines[i].replace(RegExp('[[]{2}([^|\\]]*?[|])?(.*?)[\\]]{2}', 'g'), '$2')
.replace(/'''([^'])/g, '$1').replace(RegExp("''([^'])", 'g'), '$1');
if (lines[i].match(anchRe)) {
return d.split('\n').slice(i).join('\n').replace(RegExp('^[^=]*'), '');
}
}
return d;
}
function killPopup() {
removeModifierKeyHandler(this);
if (getValueOf('popupShortcutKeys')) { rmPopupShortcuts(); }
if (!pg) { return; }
if (pg.current.link && pg.current.link.navpopup) { pg.current.link.navpopup.banish(); }
pg.current.link=null;
abortAllDownloads();
if (pg.timer.checkPopupPosition) {
clearInterval(pg.timer.checkPopupPosition);
pg.timer.checkPopupPosition=null;
}
return true; // preserve default action
}
// ENDFILE: actions.js
// STARTFILE: domdrag.js
/**
@fileoverview
The {@link Drag} object, which enables objects to be dragged around.
<pre>
*************************************************
dom-drag.js
09.25.2001
www.youngpup.net
**************************************************
10.28.2001 - fixed minor bug where events
sometimes fired off the handle, not the root.
*************************************************
Pared down, some hooks added by [[User:Lupin]]
Copyright Aaron Boodman.
Saying stupid things daily since March 2001.
</pre>
*/
/**
Creates a new Drag object. This is used to make various DOM elements draggable.
@constructor
*/
function Drag () {
/**
Condition to determine whether or not to drag. This function should take one parameter, an Event.
To disable this, set it to <code>null</code>.
@type Function
*/
this.startCondition = null;
/**
Hook to be run when the drag finishes. This is passed the final coordinates of
the dragged object (two integers, x and y). To disables this, set it to <code>null</code>.
@type Function
*/
this.endHook = null;
}
/**
Gets an event in a cross-browser manner.
@param {Event} e
@private
*/
Drag.prototype.fixE = function(e) {
if (typeof e == 'undefined') { e = window.event; }
if (typeof e.layerX == 'undefined') { e.layerX = e.offsetX; }
if (typeof e.layerY == 'undefined') { e.layerY = e.offsetY; }
return e;
};
/**
Initialises the Drag instance by telling it which object you want to be draggable, and what you want to drag it by.
@param {DOMElement} o The "handle" by which <code>oRoot</code> is dragged.
@param {DOMElement} oRoot The object which moves when <code>o</code> is dragged, or <code>o</code> if omitted.
*/
Drag.prototype.init = function(o, oRoot) {
var dragObj = this;
this.obj = o;
o.onmousedown = function(e) { dragObj.start.apply( dragObj, [e]); };
o.dragging = false;
o.popups_draggable = true;
o.hmode = true;
o.vmode = true;
o.root = oRoot ? oRoot : o ;
if (isNaN(parseInt(o.root.style.left, 10))) { o.root.style.left = "0px"; }
if (isNaN(parseInt(o.root.style.top, 10))) { o.root.style.top = "0px"; }
o.root.onthisStart = function(){};
o.root.onthisEnd = function(){};
o.root.onthis = function(){};
};
/**
Starts the drag.
@private
@param {Event} e
*/
Drag.prototype.start = function(e) {
var o = this.obj; // = this;
e = this.fixE(e);
if (this.startCondition && !this.startCondition(e)) { return; }
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom, 10);
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10);
o.root.onthisStart(x, y);
o.lastMouseX = e.clientX;
o.lastMouseY = e.clientY;
var dragObj = this;
o.onmousemoveDefault = document.onmousemove;
o.dragging = true;
document.onmousemove = function(e) { dragObj.drag.apply( dragObj, [e] ); };
document.onmouseup = function(e) { dragObj.end.apply( dragObj, [e] ); };
return false;
};
/**
Does the drag.
@param {Event} e
@private
*/
Drag.prototype.drag = function(e) {
e = this.fixE(e);
var o = this.obj;
var ey = e.clientY;
var ex = e.clientX;
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom, 10);
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10 );
var nx, ny;
nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));
this.obj.root.style[o.hmode ? "left" : "right"] = nx + "px";
this.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px";
this.obj.lastMouseX = ex;
this.obj.lastMouseY = ey;
this.obj.root.onthis(nx, ny);
return false;
};
/**
Ends the drag.
@private
*/
Drag.prototype.end = function() {
document.onmousemove=this.obj.onmousemoveDefault;
document.onmouseup = null;
this.obj.dragging = false;
if (this.endHook) {
this.endHook( parseInt(this.obj.root.style[this.obj.hmode ? "left" : "right"], 10),
parseInt(this.obj.root.style[this.obj.vmode ? "top" : "bottom"], 10));
}
};
// ENDFILE: domdrag.js
// STARTFILE: structures.js
//<NOLITE>
pg.structures.original={};
pg.structures.original.popupLayout=function () {
return ['popupError', 'popupImage', 'popupTopLinks', 'popupTitle',
'popupUserData', 'popupData', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks',
'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
pg.structures.original.popupRedirSpans=function () {
return ['popupRedir', 'popupWarnRedir', 'popupRedirTopLinks',
'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'];
};
pg.structures.original.popupTitle=function (x) {
log ('defaultstructure.popupTitle');
if (!getValueOf('popupNavLinks')) {
return navlinkStringToHTML('<b><<mainlink>></b>',x.article,x.params);
}
return '';
};
pg.structures.original.popupTopLinks=function (x) {
log ('defaultstructure.popupTopLinks');
if (getValueOf('popupNavLinks')) { return navLinksHTML(x.article, x.hint, x.params); }
return '';
};
pg.structures.original.popupImage=function(x) {
log ('original.popupImage, x.article='+x.article+', x.navpop.idNumber='+x.navpop.idNumber);
return imageHTML(x.article, x.navpop.idNumber);
};
pg.structures.original.popupRedirTitle=pg.structures.original.popupTitle;
pg.structures.original.popupRedirTopLinks=pg.structures.original.popupTopLinks;
function copyStructure(oldStructure, newStructure) {
pg.structures[newStructure]={};
for (var prop in pg.structures[oldStructure]) {
pg.structures[newStructure][prop]=pg.structures[oldStructure][prop];
}
}
copyStructure('original', 'nostalgia');
pg.structures.nostalgia.popupTopLinks=function(x) {
var str='';
str += '<b><<mainlink|shortcut= >></b>';
// user links
// contribs - log - count - email - block
// count only if applicable; block only if popupAdminLinks
str += 'if(user){<br><<contribs|shortcut=c>>';
str+='if(wikimedia){*<<count|shortcut=#>>}';
str+='if(ipuser){}else{*<<email|shortcut=E>>}if(admin){*<<block|shortcut=b>>}}';
// editing links
// talkpage -> edit|new - history - un|watch - article|edit
// other page -> edit - history - un|watch - talk|edit|new
var editstr='<<edit|shortcut=e>>';
var editOldidStr='if(oldid){<<editOld|shortcut=e>>|<<revert|shortcut=v|rv>>|<<edit|cur>>}else{' +
editstr + '}';
var historystr='<<history|shortcut=h>>';
var watchstr='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
str += '<br>if(talk){' +
editOldidStr+'|<<new|shortcut=+>>' + '*' + historystr+'*'+watchstr + '*' +
'<b><<article|shortcut=a>></b>|<<editArticle|edit>>' +
'}else{' + // not a talk page
editOldidStr + '*' + historystr + '*' + watchstr + '*' +
'<b><<talk|shortcut=t>></b>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>}';
// misc links
str += '<br><<whatLinksHere|shortcut=l>>*<<relatedChanges|shortcut=r>>';
str += 'if(admin){<br>}else{*}<<move|shortcut=m>>';
// admin links
str += 'if(admin){*<<unprotect|unprotectShort>>|<<protect|shortcut=p>>*' +
'<<undelete|undeleteShort>>|<<delete|shortcut=d>>}';
return navlinkStringToHTML(str, x.article, x.params);
};
pg.structures.nostalgia.popupRedirTopLinks=pg.structures.nostalgia.popupTopLinks;
/** -- fancy -- **/
copyStructure('original', 'fancy');
pg.structures.fancy.popupTitle=function (x) {
return navlinkStringToHTML('<font size=+0><<mainlink>></font>',x.article,x.params);
};
pg.structures.fancy.popupTopLinks=function(x) {
var hist='<<history|shortcut=h|hist>>|<<lastEdit|shortcut=/|last>>|<<editors|shortcut=E|eds>>';
var watch='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
var move='<<move|shortcut=m|move>>';
return navlinkStringToHTML('if(talk){' +
'<<edit|shortcut=e>>|<<new|shortcut=+|+>>*' + hist + '*' +
'<<article|shortcut=a>>|<<editArticle|edit>>' + '*' + watch + '*' + move +
'}else{<<edit|shortcut=e>>*' + hist +
'*<<talk|shortcut=t|>>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>' +
'*' + watch + '*' + move+'}<br>', x.article, x.params);
};
pg.structures.fancy.popupOtherLinks=function(x) {
var admin='<<unprotect|unprotectShort>>|<<protect|shortcut=p>>*<<undelete|undeleteShort>>|<<delete|shortcut=d|del>>';
var user='<<contribs|shortcut=c>>if(wikimedia){|<<count|shortcut=#|#>>}';
user+='if(ipuser){|<<arin>>}else{*<<email|shortcut=E|'+
popupString('email')+'>>}if(admin){*<<block|shortcut=b>>}';
var normal='<<whatLinksHere|shortcut=l|links here>>*<<relatedChanges|shortcut=r|related>>';
return navlinkStringToHTML('<br>if(user){' + user + '*}if(admin){'+admin+'if(user){<br>}else{*}}' + normal,
x.article, x.params);
};
pg.structures.fancy.popupRedirTitle=pg.structures.fancy.popupTitle;
pg.structures.fancy.popupRedirTopLinks=pg.structures.fancy.popupTopLinks;
pg.structures.fancy.popupRedirOtherLinks=pg.structures.fancy.popupOtherLinks;
/** -- fancy2 -- **/
// hack for [[User:MacGyverMagic]]
copyStructure('fancy', 'fancy2');
pg.structures.fancy2.popupTopLinks=function(x) { // hack out the <br> at the end and put one at the beginning
return '<br>'+pg.structures.fancy.popupTopLinks(x).replace(RegExp('<br>$','i'),'');
};
pg.structures.fancy2.popupLayout=function () { // move toplinks to after the title
return ['popupError', 'popupImage', 'popupTitle', 'popupUserData', 'popupData', 'popupTopLinks', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks', 'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
/** -- menus -- **/
copyStructure('original', 'menus');
pg.structures.menus.popupLayout=function () {
return ['popupError', 'popupImage', 'popupTopLinks', 'popupTitle', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks', 'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupUserData', 'popupData', 'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
pg.structures.menus.popupTopLinks = function (x, shorter) {
// FIXME maybe this stuff should be cached
var s=[];
var dropdiv='<div class="popup_drop">';
var enddiv='</div>';
var hist='<<history|shortcut=h>>';
if (!shorter) { hist = '<menurow>' + hist +
'|<<historyfeed|rss>>|<<editors|shortcut=E>></menurow>'; }
var lastedit='<<lastEdit|shortcut=/|show last edit>>';
var thank='if(diff){<<thank|send thanks>>}';
var jsHistory='<<lastContrib|last set of edits>><<sinceMe|changes since mine>>';
var linkshere='<<whatLinksHere|shortcut=l|what links here>>';
var related='<<relatedChanges|shortcut=r|related changes>>';
var search='<menurow><<search|shortcut=s>>if(wikimedia){|<<globalsearch|shortcut=g|global>>}' +
'|<<google|shortcut=G|web>></menurow>';
var watch='<menurow><<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>></menurow>';
var protect='<menurow><<unprotect|unprotectShort>>|' +
'<<protect|shortcut=p>>|<<protectlog|log>></menurow>';
var del='<menurow><<undelete|undeleteShort>>|<<delete|shortcut=d>>|' +
'<<deletelog|log>></menurow>';
var move='<<move|shortcut=m|move page>>';
var nullPurge='<menurow><<nullEdit|shortcut=n|null edit>>|<<purge|shortcut=P>></menurow>';
var viewOptions='<menurow><<view|shortcut=v>>|<<render|shortcut=S>>|<<raw>></menurow>';
var editRow='if(oldid){' +
'<menurow><<edit|shortcut=e>>|<<editOld|shortcut=e|this revision>></menurow>' +
'<menurow><<revert|shortcut=v>>|<<undo>></menurow>' + '}else{<<edit|shortcut=e>>}';
var markPatrolled='if(rcid){<<markpatrolled|mark patrolled>>}';
var newTopic='if(talk){<<new|shortcut=+|new topic>>}';
var protectDelete='if(admin){' + protect + del + '}';
if (getValueOf('popupActionsMenu')) {
s.push( '<<mainlink>>*' + dropdiv + menuTitle('actions'));
} else {
s.push( dropdiv + '<<mainlink>>');
}
s.push( '<menu>');
s.push( editRow + markPatrolled + newTopic + hist + lastedit + thank );
if (!shorter) { s.push(jsHistory); }
s.push( move + linkshere + related);
if (!shorter) { s.push(nullPurge + search); }
if (!shorter) { s.push(viewOptions); }
s.push('<hr />' + watch + protectDelete);
s.push('<hr />' +
'if(talk){<<article|shortcut=a|view article>><<editArticle|edit article>>}' +
'else{<<talk|shortcut=t|talk page>><<editTalk|edit talk>>' +
'<<newTalk|shortcut=+|new topic>>}</menu>' + enddiv);
// user menu starts here
var email='<<email|shortcut=E|email user>>';
var contribs= 'if(wikimedia){<menurow>}<<contribs|shortcut=c|contributions>>if(wikimedia){</menurow>}' +
'if(admin){<menurow><<deletedContribs>></menurow>}';
s.push('if(user){*' + dropdiv + menuTitle('user'));
s.push('<menu>');
s.push('<menurow><<userPage|shortcut=u|user page>>|<<userSpace|space>></menurow>');
s.push('<<userTalk|shortcut=t|user talk>><<editUserTalk|edit user talk>>' +
'<<newUserTalk|shortcut=+|leave comment>>');
if(!shorter) { s.push( 'if(ipuser){<<arin>>}else{' + email + '}' ); }
else { s.push( 'if(ipuser){}else{' + email + '}' ); }
s.push('<hr />' + contribs + '<<userlog|shortcut=L|user log>>');
s.push('if(wikimedia){<<count|shortcut=#|edit counter>>}');
s.push('if(admin){<menurow><<unblock|unblockShort>>|<<block|shortcut=b|block user>></menurow>}');
s.push('<<blocklog|shortcut=B|block log>>');
s.push('</menu>' + enddiv + '}');
// popups menu starts here
if (getValueOf('popupSetupMenu') && !x.navpop.hasPopupMenu /* FIXME: hack */) {
x.navpop.hasPopupMenu=true;
s.push('*' + dropdiv + menuTitle('popupsMenu') + '<menu>');
s.push('<<togglePreviews|toggle previews>>');
s.push('<<purgePopups|reset>>');
s.push('<<disablePopups|disable>>');
s.push('</menu>'+enddiv);
}
return navlinkStringToHTML(s.join(''), x.article, x.params);
};
function menuTitle(s) {
return '<a href="#" noPopup=1>' + popupString(s) + '</a>';
}
pg.structures.menus.popupRedirTitle=pg.structures.menus.popupTitle;
pg.structures.menus.popupRedirTopLinks=pg.structures.menus.popupTopLinks;
copyStructure('menus', 'shortmenus');
pg.structures.shortmenus.popupTopLinks=function(x) {
return pg.structures.menus.popupTopLinks(x,true);
};
pg.structures.shortmenus.popupRedirTopLinks=pg.structures.shortmenus.popupTopLinks;
//</NOLITE>
pg.structures.lite={};
pg.structures.lite.popupLayout=function () {
return ['popupTitle', 'popupPreview' ];
};
pg.structures.lite.popupTitle=function (x) {
log (x.article + ': structures.lite.popupTitle');
//return navlinkStringToHTML('<b><<mainlink>></b>',x.article,x.params);
return '<div><span class="popup_mainlink"><b>' + x.article.toString() + '</b></span></div>';
};
// ENDFILE: structures.js
// STARTFILE: autoedit.js
//<NOLITE>
function substitute(data,cmdBody) {
// alert('sub\nfrom: '+cmdBody.from+'\nto: '+cmdBody.to+'\nflags: '+cmdBody.flags);
var fromRe=RegExp(cmdBody.from, cmdBody.flags);
return data.replace(fromRe, cmdBody.to);
}
function execCmds(data, cmdList) {
for (var i=0; i<cmdList.length; ++i) {
data=cmdList[i].action(data, cmdList[i]);
}
return data;
}
function parseCmd(str) {
// returns a list of commands
if (!str.length) { return []; }
var p=false;
switch (str.charAt(0)) {
case 's':
p=parseSubstitute(str);
break;
default:
return false;
}
if (p) { return [p].concat(parseCmd(p.remainder)); }
return false;
}
function unEscape(str, sep) {
return str.split('\\\\').join('\\').split('\\'+sep).join(sep).split('\\n').join('\n');
}
function parseSubstitute(str) {
// takes a string like s/a/b/flags;othercmds and parses it
var from,to,flags,tmp;
if (str.length<4) { return false; }
var sep=str.charAt(1);
str=str.substring(2);
tmp=skipOver(str,sep);
if (tmp) { from=tmp.segment; str=tmp.remainder; }
else { return false; }
tmp=skipOver(str,sep);
if (tmp) { to=tmp.segment; str=tmp.remainder; }
else { return false; }
flags='';
if (str.length) {
tmp=skipOver(str,';') || skipToEnd(str, ';');
if (tmp) {flags=tmp.segment; str=tmp.remainder; }
}
return {action: substitute, from: from, to: to, flags: flags, remainder: str};
}
function skipOver(str,sep) {
var endSegment=findNext(str,sep);
if (endSegment<0) { return false; }
var segment=unEscape(str.substring(0,endSegment), sep);
return {segment: segment, remainder: str.substring(endSegment+1)};
}
/*eslint-disable*/
function skipToEnd(str,sep) {
return {segment: str, remainder: ''};
}
/*eslint-enable */
function findNext(str, ch) {
for (var i=0; i<str.length; ++i) {
if (str.charAt(i)=='\\') { i+=2; }
if (str.charAt(i)==ch) { return i; }
}
return -1;
}
function setCheckbox(param, box) {
var val=mw.util.getParamValue(param);
if (val) {
switch (val) {
case '1': case 'yes': case 'true':
box.checked=true;
break;
case '0': case 'no': case 'false':
box.checked=false;
}
}
}
function autoEdit() {
setupPopups( function () {
if (mw.util.getParamValue('autoimpl') !== popupString('autoedit_version') ) { return false; }
if (mw.util.getParamValue('autowatchlist') && mw.util.getParamValue('actoken')===autoClickToken()) {
pg.fn.modifyWatchlist(mw.util.getParamValue('title'), mw.util.getParamValue('action'));
}
if (!document.editform) { return false; }
if (autoEdit.alreadyRan) { return false; }
autoEdit.alreadyRan=true;
var cmdString=mw.util.getParamValue('autoedit');
if (cmdString) {
try {
var editbox=document.editform.wpTextbox1;
var cmdList=parseCmd(cmdString);
var input=editbox.value;
var output=execCmds(input, cmdList);
editbox.value=output;
} catch (dang) { return; }
// wikEd user script compatibility
if (typeof(wikEdUseWikEd) != 'undefined') {
if (wikEdUseWikEd === true) {
WikEdUpdateFrame();
}
}
}
setCheckbox('autominor', document.editform.wpMinoredit);
setCheckbox('autowatch', document.editform.wpWatchthis);
var rvid = mw.util.getParamValue('autorv');
if (rvid) {
var url=pg.wiki.apiwikibase + '?action=query&format=json&formatversion=2&prop=revisions&revids='+rvid;
startDownload(url, null, autoEdit2);
} else { autoEdit2(); }
} );
}
function autoEdit2(d) {
var summary=mw.util.getParamValue('autosummary');
var summaryprompt=mw.util.getParamValue('autosummaryprompt');
var summarynotice='';
if (d && d.data && mw.util.getParamValue('autorv')) {
var s = getRvSummary(summary, d.data);
if (s === false) {
summaryprompt=true;
summarynotice=popupString('Failed to get revision information, please edit manually.\n\n');
summary = simplePrintf(summary, [mw.util.getParamValue('autorv'), '(unknown)', '(unknown)']);
} else { summary = s; }
}
if (summaryprompt) {
var txt= summarynotice +
popupString('Enter a non-empty edit summary or press cancel to abort');
var response=prompt(txt, summary);
if (response) { summary=response; }
else { return; }
}
if (summary) { document.editform.wpSummary.value=summary; }
// Attempt to avoid possible premature clicking of the save button
// (maybe delays in updates to the DOM are to blame?? or a red herring)
setTimeout(autoEdit3, 100);
}
function autoClickToken() {
return mw.user.sessionId();
}
function autoEdit3() {
if( mw.util.getParamValue('actoken') != autoClickToken()) { return; }
var btn=mw.util.getParamValue('autoclick');
if (btn) {
if (document.editform && document.editform[btn]) {
var button=document.editform[btn];
var msg=tprintf('The %s button has been automatically clicked. Please wait for the next page to load.',
[ button.value ]);
bannerMessage(msg);
document.title='('+document.title+')';
button.click();
} else {
alert(tprintf('Could not find button %s. Please check the settings in your javascript file.',
[ btn ]));
}
}
}
function bannerMessage(s) {
var headings=document.getElementsByTagName('h1');
if (headings) {
var div=document.createElement('div');
div.innerHTML='<font size=+1><b>' + s + '</b></font>';
headings[0].parentNode.insertBefore(div, headings[0]);
}
}
function getRvSummary(template, json) {
try {
var o=getJsObj(json);
var edit = anyChild(o.query.pages).revisions[0];
var timestamp = edit.timestamp.split(/[A-Z]/g).join(' ').replace(/^ *| *$/g, '');
return simplePrintf(template, [edit.revid, timestamp, edit.userhidden ? '(hidden)' : edit.user ]);
} catch (badness) {
return false;
}
}
//</NOLITE>
// ENDFILE: autoedit.js
// STARTFILE: downloader.js
/**
@fileoverview
{@link Downloader}, a xmlhttprequest wrapper, and helper functions.
*/
/**
Creates a new Downloader
@constructor
@class The Downloader class. Create a new instance of this class to download stuff.
@param {String} url The url to download. This can be omitted and supplied later.
*/
function Downloader(url) {
if (typeof XMLHttpRequest!='undefined') { this.http = new XMLHttpRequest(); }
/**
The url to download
@type String
*/
this.url = url;
/**
A universally unique ID number
@type integer
*/
this.id=null;
/**
Modification date, to be culled from the incoming headers
@type Date
@private
*/
this.lastModified = null;
/**
What to do when the download completes successfully
@type Function
@private
*/
this.callbackFunction = null;
/**
What to do on failure
@type Function
@private
*/
this.onFailure = null;
/**
Flag set on <code>abort</code>
@type boolean
*/
this.aborted = false;
/**
HTTP method. See https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html for possibilities.
@type String
*/
this.method='GET';
/**
Async flag.
@type boolean
*/
this.async=true;
}
new Downloader();
/** Submits the http request. */
Downloader.prototype.send = function (x) {
if (!this.http) { return null; }
return this.http.send(x);
};
/** Aborts the download, setting the <code>aborted</code> field to true. */
Downloader.prototype.abort = function () {
if (!this.http) { return null; }
this.aborted=true;
return this.http.abort();
};
/** Returns the downloaded data. */
Downloader.prototype.getData = function () {if (!this.http) { return null; } return this.http.responseText;};
/** Prepares the download. */
Downloader.prototype.setTarget = function () {
if (!this.http) { return null; }
this.http.open(this.method, this.url, this.async);
this.http.setRequestHeader( 'Api-User-Agent', pg.misc.userAgent );
};
/** Gets the state of the download. */
Downloader.prototype.getReadyState=function () {if (!this.http) { return null; } return this.http.readyState;};
pg.misc.downloadsInProgress = { };
/** Starts the download.
Note that setTarget {@link Downloader#setTarget} must be run first
*/
Downloader.prototype.start=function () {
if (!this.http) { return; }
pg.misc.downloadsInProgress[this.id] = this;
this.http.send(null);
};
/** Gets the 'Last-Modified' date from the download headers.
Should be run after the download completes.
Returns <code>null</code> on failure.
@return {Date}
*/
Downloader.prototype.getLastModifiedDate=function () {
if(!this.http) { return null; }
var lastmod=null;
try {
lastmod=this.http.getResponseHeader('Last-Modified');
} catch (err) {}
if (lastmod) { return new Date(lastmod); }
return null;
};
/** Sets the callback function.
@param {Function} f callback function, called as <code>f(this)</code> on success
*/
Downloader.prototype.setCallback = function (f) {
if(!this.http) { return; }
this.http.onreadystatechange = f;
};
Downloader.prototype.getStatus = function() { if (!this.http) { return null; } return this.http.status; };
//////////////////////////////////////////////////
// helper functions
/** Creates a new {@link Downloader} and prepares it for action.
@param {String} url The url to download
@param {integer} id The ID of the {@link Downloader} object
@param {Function} callback The callback function invoked on success
@return {String/Downloader} the {@link Downloader} object created, or 'ohdear' if an unsupported browser
*/
function newDownload(url, id, callback, onfailure) {
var d=new Downloader(url);
if (!d.http) { return 'ohdear'; }
d.id=id;
d.setTarget();
if (!onfailure) {
onfailure=2;
}
var f = function () {
if (d.getReadyState() == 4) {
delete pg.misc.downloadsInProgress[this.id];
try {
if ( d.getStatus() == 200 ) {
d.data=d.getData();
d.lastModified=d.getLastModifiedDate();
callback(d);
} else if (typeof onfailure == typeof 1) {
if (onfailure > 0) {
// retry
newDownload(url, id, callback, onfailure - 1);
}
} else if (typeof onfailure === 'function') {
onfailure(d,url,id,callback);
}
} catch (somerr) { /* ignore it */ }
}
};
d.setCallback(f);
return d;
}
/** Simulates a download from cached data.
The supplied data is put into a {@link Downloader} as if it had downloaded it.
@param {String} url The url.
@param {integer} id The ID.
@param {Function} callback The callback, which is invoked immediately as <code>callback(d)</code>,
where <code>d</code> is the new {@link Downloader}.
@param {String} data The (cached) data.
@param {Date} lastModified The (cached) last modified date.
*/
function fakeDownload(url, id, callback, data, lastModified, owner) {
var d=newDownload(url,callback);
d.owner=owner;
d.id=id; d.data=data;
d.lastModified=lastModified;
return callback(d);
}
/**
Starts a download.
@param {String} url The url to download
@param {integer} id The ID of the {@link Downloader} object
@param {Function} callback The callback function invoked on success
@return {String/Downloader} the {@link Downloader} object created, or 'ohdear' if an unsupported browser
*/
function startDownload(url, id, callback) {
var d=newDownload(url, id, callback);
if (typeof d == typeof '' ) { return d; }
d.start();
return d;
}
/**
Aborts all downloads which have been started.
*/
function abortAllDownloads() {
for ( var x in pg.misc.downloadsInProgress ) {
try {
pg.misc.downloadsInProgress[x].aborted=true;
pg.misc.downloadsInProgress[x].abort();
delete pg.misc.downloadsInProgress[x];
} catch (e) {}
}
}
// ENDFILE: downloader.js
// STARTFILE: livepreview.js
// TODO: location is often not correct (eg relative links in previews)
// NOTE: removed md5 and image and math parsing. was broken, lots of bytes.
/**
* InstaView - a Mediawiki to HTML converter in JavaScript
* Version 0.6.1
* Copyright (C) Pedro Fayolle 2005-2006
* https://en.wikipedia.org/wiki/User:Pilaf
* Distributed under the BSD license
*
* Changelog:
*
* 0.6.1
* - Fixed problem caused by \r characters
* - Improved inline formatting parser
*
* 0.6
* - Changed name to InstaView
* - Some major code reorganizations and factored out some common functions
* - Handled conversion of relative links (i.e. [[/foo]])
* - Fixed misrendering of adjacent definition list items
* - Fixed bug in table headings handling
* - Changed date format in signatures to reflect Mediawiki's
* - Fixed handling of [[:Image:...]]
* - Updated MD5 function (hopefully it will work with UTF-8)
* - Fixed bug in handling of links inside images
*
* To do:
* - Better support for math tags
* - Full support for <nowiki>
* - Parser-based (as opposed to RegExp-based) inline wikicode handling (make it one-pass and bullet-proof)
* - Support for templates (through AJAX)
* - Support for coloured links (AJAX)
*/
var Insta = {};
function setupLivePreview() {
// options
Insta.conf =
{
baseUrl: '',
user: {},
wiki: {
lang: pg.wiki.lang,
interwiki: pg.wiki.interwiki,
default_thumb_width: 180
},
paths: {
articles: pg.wiki.articlePath + '/',
// Only used for Insta previews with images. (not in popups)
math: '/math/',
images: '//upload.wikimedia.org/wikipedia/en/', // FIXME getImageUrlStart(pg.wiki.hostname),
images_fallback: '//upload.wikimedia.org/wikipedia/commons/',
},
locale: {
user: mw.config.get('wgFormattedNamespaces')[pg.nsUserId],
image: mw.config.get('wgFormattedNamespaces')[pg.nsImageId],
category: mw.config.get('wgFormattedNamespaces')[pg.nsCategoryId],
// shouldn't be used in popup previews, i think
months: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
}
};
// options with default values or backreferences
Insta.conf.user.name = Insta.conf.user.name || 'Wikipedian';
Insta.conf.user.signature = '[['+Insta.conf.locale.user+':'+Insta.conf.user.name+'|'+Insta.conf.user.name+']]';
//Insta.conf.paths.images = '//upload.wikimedia.org/wikipedia/' + Insta.conf.wiki.lang + '/';
// define constants
Insta.BLOCK_IMAGE = new RegExp('^\\[\\[(?:File|Image|'+Insta.conf.locale.image+
'):.*?\\|.*?(?:frame|thumbnail|thumb|none|right|left|center)', 'i');
}
Insta.dump = function(from, to)
{
if (typeof from == 'string') { from = document.getElementById(from); }
if (typeof to == 'string') { to = document.getElementById(to); }
to.innerHTML = this.convert(from.value);
};
Insta.convert = function(wiki)
{
var ll = (typeof wiki == 'string')? wiki.replace(/\r/g,'').split(/\n/): wiki, // lines of wikicode
o = '', // output
p = 0, // para flag
$r; // result of passing a regexp to $()
// some shorthands
function remain() { return ll.length; }
function sh() { return ll.shift(); } // shift
function ps(s) { o += s; } // push
// similar to C's printf, uses ? as placeholders, ?? to escape question marks
function f()
{
var i=1, a=arguments, f=a[0], o='', c, p;
for (; i<a.length; i++) {
if ((p=f.indexOf('?'))+1) {
// allow character escaping
i -= c = f.charAt(p+1)=='?' ? 1 : 0;
o += f.substring(0,p) + (c ? '?' : a[i]);
f = f.substr(p+1+c);
} else { break; }
}
return o+f;
}
function html_entities(s) {
return s.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
}
// Wiki text parsing to html is a nightmare.
// The below functions deliberately don't escape the ampersand since this would make it more difficult,
// and we don't absolutely need to for how we need it.
// This means that any unescaped ampersands in wikitext will remain unescaped and can cause invalid HTML.
// Browsers should all be able to handle it though.
// We also escape significant wikimarkup characters to prevent further matching on the processed text
function htmlescape_text(s) {
return s.replace(/</g,"<").replace(/>/g,">").replace(/:/g,":").replace(/\[/g,"[").replace(/]/g,"]");
}
function htmlescape_attr(s) {
return htmlescape_text(s).replace(/'/g,"'").replace(/"/g,""");
}
// return the first non matching character position between two strings
function str_imatch(a, b)
{
for (var i=0, l=Math.min(a.length, b.length); i<l; i++) {
if (a.charAt(i)!=b.charAt(i)) { break; }
}
return i;
}
// compare current line against a string or regexp
// if passed a string it will compare only the first string.length characters
// if passed a regexp the result is stored in $r
function $(c) { return (typeof c == 'string') ? (ll[0].substr(0,c.length)==c) : ($r = ll[0].match(c)); }
function $$(c) { return ll[0]==c; } // compare current line against a string
function _(p) { return ll[0].charAt(p); } // return char at pos p
function endl(s) { ps(s); sh(); }
function parse_list()
{
var prev='';
while (remain() && $(/^([*#:;]+)(.*)$/)) {
var l_match = $r;
sh();
var ipos = str_imatch(prev, l_match[1]);
// close uncontinued lists
for (var prevPos=prev.length-1; prevPos >= ipos; prevPos--) {
var pi = prev.charAt(prevPos);
if (pi=='*') { ps('</ul>'); }
else if (pi=='#') { ps('</ol>'); }
// close a dl only if the new item is not a dl item (:, ; or empty)
else if($.inArray(l_match[1].charAt(prevPos), ['','*','#'])) { ps('</dl>'); }
}
// open new lists
for (var matchPos=ipos; matchPos<l_match[1].length; matchPos++) {
var li = l_match[1].charAt(matchPos);
if (li=='*') { ps('<ul>'); }
else if (li=='#') { ps('<ol>'); }
// open a new dl only if the prev item is not a dl item (:, ; or empty)
else if ($.inArray(prev.charAt(matchPos), ['','*','#'])) { ps('<dl>'); }
}
switch (l_match[1].charAt(l_match[1].length-1)) {
case '*': case '#':
ps('<li>' + parse_inline_nowiki(l_match[2]));
break;
case ';':
ps('<dt>');
var dt_match = l_match[2].match(/(.*?)(:.*?)$/);
// handle ;dt :dd format
if (dt_match) {
ps(parse_inline_nowiki(dt_match[1]));
ll.unshift(dt_match[2]);
} else ps(parse_inline_nowiki(l_match[2]));
break;
case ':':
ps('<dd>' + parse_inline_nowiki(l_match[2]));
}
prev=l_match[1];
}
// close remaining lists
for (var i=prev.length-1; i>=0; i--) {
ps(f('</?>', (prev.charAt(i)=='*')? 'ul': ((prev.charAt(i)=='#')? 'ol': 'dl')));
}
}
function parse_table()
{
endl(f('<table>', $(/^\{\|( .*)$/)? $r[1]: ''));
for (;remain();) if ($('|')) switch (_(1)) {
case '}':
endl('</table>');
return;
case '-':
endl(f('<tr>', $(/\|-*(.*)/)[1]));
break;
default:
parse_table_data();
}
else if ($('!')) { parse_table_data(); }
else { sh(); }
}
function parse_table_data()
{
var td_line, match_i;
// 1: "|+", '|' or '+'
// 2: ??
// 3: attributes ??
// TODO: finish commenting this regexp
var td_match = sh().match(/^(\|\+|\||!)((?:([^[|]*?)\|(?!\|))?(.*))$/);
if (td_match[1] == '|+') ps('<caption');
else ps('<t' + ((td_match[1]=='|')?'d':'h'));
if (typeof td_match[3] != 'undefined') {
//ps(' ' + td_match[3])
match_i = 4;
} else match_i = 2;
ps('>');
if (td_match[1] != '|+') {
// use || or !! as a cell separator depending on context
// NOTE: when split() is passed a regexp make sure to use non-capturing brackets
td_line = td_match[match_i].split((td_match[1] == '|')? '||': /(?:\|\||!!)/);
ps(parse_inline_nowiki(td_line.shift()));
while (td_line.length) ll.unshift(td_match[1] + td_line.pop());
} else ps(td_match[match_i]);
var tc = 0, td = [];
while (remain()) {
td.push(sh());
if ($('|')) {
if (!tc) break; // we're at the outer-most level (no nested tables), skip to td parse
else if (_(1)=='}') tc--;
}
else if (!tc && $('!')) break;
else if ($('{|')) tc++;
}
if (td.length) ps(Insta.convert(td));
}
function parse_pre()
{
ps('<pre>');
do {
endl(parse_inline_nowiki(ll[0].substring(1)) + "\n");
} while (remain() && $(' '));
ps('</pre>');
}
function parse_block_image()
{
ps(parse_image(sh()));
}
function parse_image(str)
{
//<NOLITE>
// get what's in between "[[Image:" and "]]"
var tag = str.substring(str.indexOf(':') + 1, str.length - 2);
/* eslint-disable no-unused-vars */
var width;
var attr = [], filename, caption = '';
var thumb=0, frame=0, center=0;
var align='';
/* eslint-enable no-unused-vars */
if (tag.match(/\|/)) {
// manage nested links
var nesting = 0;
var last_attr;
for (var i = tag.length-1; i > 0; i--) {
if (tag.charAt(i) == '|' && !nesting) {
last_attr = tag.substr(i+1);
tag = tag.substring(0, i);
break;
} else switch (tag.substr(i-1, 2)) {
case ']]':
nesting++;
i--;
break;
case '[[':
nesting--;
i--;
}
}
attr = tag.split(/\s*\|\s*/);
attr.push(last_attr);
filename = attr.shift();
var w_match;
for (;attr.length; attr.shift()) {
w_match = attr[0].match(/^(\d*)(?:[px]*\d*)?px$/);
if (w_match) width = w_match[1];
else switch(attr[0]) {
case 'thumb':
case 'thumbnail':
thumb=true;
frame=true;
break;
case 'frame':
frame=true;
break;
case 'none':
case 'right':
case 'left':
center=false;
align=attr[0];
break;
case 'center':
center=true;
align='none';
break;
default:
if (attr.length == 1) caption = attr[0];
}
}
} else filename = tag;
return '';
//</NOLITE>
}
function parse_inline_nowiki(str)
{
var start, lastend=0;
var substart=0, nestlev=0, open, close, subloop;
var html='';
while (-1 != (start = str.indexOf('<nowiki>', substart))) {
html += parse_inline_wiki(str.substring(lastend, start));
start += 8;
substart = start;
subloop = true;
do {
open = str.indexOf('<nowiki>', substart);
close = str.indexOf('</nowiki>', substart);
if (close<=open || open==-1) {
if (close==-1) {
return html + html_entities(str.substr(start));
}
substart = close+9;
if (nestlev) {
nestlev--;
} else {
lastend = substart;
html += html_entities(str.substring(start, lastend-9));
subloop = false;
}
} else {
substart = open+8;
nestlev++;
}
} while (subloop);
}
return html + parse_inline_wiki(str.substr(lastend));
}
function parse_inline_images(str)
{
//<NOLITE>
var start, substart=0, nestlev=0;
var loop, close, open, wiki, html;
while (-1 != (start=str.indexOf('[[', substart))) {
if(str.substr(start+2).match(RegExp('^(Image|File|' + Insta.conf.locale.image + '):','i'))) {
loop=true;
substart=start;
do {
substart+=2;
close=str.indexOf(']]',substart);
open=str.indexOf('[[',substart);
if (close<=open||open==-1) {
if (close==-1) return str;
substart=close;
if (nestlev) {
nestlev--;
} else {
wiki=str.substring(start,close+2);
html=parse_image(wiki);
str=str.replace(wiki,html);
substart=start+html.length;
loop=false;
}
} else {
substart=open;
nestlev++;
}
} while (loop);
} else break;
}
//</NOLITE>
return str;
}
// the output of this function doesn't respect the FILO structure of HTML
// but since most browsers can handle it I'll save myself the hassle
function parse_inline_formatting(str)
{
var em,st,i,li,o='';
while ((i=str.indexOf("''",li))+1) {
o += str.substring(li,i);
li=i+2;
if (str.charAt(i+2)=="'") {
li++;
st=!st;
o+=st?'<strong>':'</strong>';
} else {
em=!em;
o+=em?'<em>':'</em>';
}
}
return o+str.substr(li);
}
function parse_inline_wiki(str)
{
str = parse_inline_images(str);
str = parse_inline_formatting(str);
// math
str = str.replace(/<(?:)math>(.*?)<\/math>/ig, '');
// Build a Mediawiki-formatted date string
var date = new Date();
var minutes = date.getUTCMinutes();
if (minutes < 10) minutes = '0' + minutes;
date = f("?:?, ? ? ? (UTC)", date.getUTCHours(), minutes, date.getUTCDate(), Insta.conf.locale.months[date.getUTCMonth()], date.getUTCFullYear());
// text formatting
return str.
// signatures
replace(/~{5}(?!~)/g, date).
replace(/~{4}(?!~)/g, Insta.conf.user.name+' '+date).
replace(/~{3}(?!~)/g, Insta.conf.user.name).
// [[:Category:...]], [[:Image:...]], etc...
replace(RegExp('\\[\\[:((?:'+Insta.conf.locale.category+'|Image|File|'+Insta.conf.locale.image+'|'+Insta.conf.wiki.interwiki+'):[^|]*?)\\]\\](\\w*)','gi'), function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($1) + htmlescape_text($2));}).
// remove straight category and interwiki tags
replace(RegExp('\\[\\[(?:'+Insta.conf.locale.category+'|'+Insta.conf.wiki.interwiki+'):.*?\\]\\]','gi'),'').
// [[:Category:...|Links]], [[:Image:...|Links]], etc...
replace(RegExp('\\[\\[:((?:'+Insta.conf.locale.category+'|Image|File|'+Insta.conf.locale.image+'|'+Insta.conf.wiki.interwiki+'):.*?)\\|([^\\]]+?)\\]\\](\\w*)','gi'), function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($2) + htmlescape_text($3));}).
// [[/Relative links]]
replace(/\[\[(\/[^|]*?)\]\]/g, function($0,$1){return f("<a href='?'>?</a>", Insta.conf.baseUrl + htmlescape_attr($1), htmlescape_text($1)); }).
// [[/Replaced|Relative links]]
replace(/\[\[(\/.*?)\|(.+?)\]\]/g, function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.baseUrl + htmlescape_attr($1), htmlescape_text($2)); }).
// [[Common links]]
replace(/\[\[([^[|]*?)\]\](\w*)/g, function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($1) + htmlescape_text($2)); }).
// [[Replaced|Links]]
replace(/\[\[([^[]*?)\|([^\]]+?)\]\](\w*)/g, function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($2) + htmlescape_text($3)); }).
// [[Stripped:Namespace|Namespace]]
replace(/\[\[([^\]]*?:)?(.*?)( *\(.*?\))?\|\]\]/g, function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1) + htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($2)); }).
// External links
replace(/\[(https?|news|ftp|mailto|gopher|irc):(\/*)([^\]]*?) (.*?)\]/g, function($0,$1,$2,$3,$4){return f("<a class='external' href='?:?'>?</a>", htmlescape_attr($1), htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($4)); }).
replace(/\[http:\/\/(.*?)\]/g, function($0,$1){return f("<a class='external' href='http://?'>[#]</a>", htmlescape_attr($1)); }).
replace(/\[(news|ftp|mailto|gopher|irc):(\/*)(.*?)\]/g, function($0,$1,$2,$3){return f("<a class='external' href='?:?'>?:?</a>", htmlescape_attr($1), htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($1), htmlescape_text($2) + htmlescape_text($3)); }).
replace(/(^| )(https?|news|ftp|mailto|gopher|irc):(\/*)([^ $]*[^.,!?;: $])/g, function($0,$1,$2,$3,$4){return f("?<a class='external' href='?:?'>?:?</a>", htmlescape_text($1), htmlescape_attr($2), htmlescape_attr($3) + htmlescape_attr($4), htmlescape_text($2), htmlescape_text($3) + htmlescape_text($4)); }).
replace('__NOTOC__','').
replace('__NOEDITSECTION__','');
}
// begin parsing
for (;remain();) if ($(/^(={1,6})(.*)\1(.*)$/)) {
p=0;
endl(f('<h?>?</h?>?', $r[1].length, parse_inline_nowiki($r[2]), $r[1].length, $r[3]));
} else if ($(/^[*#:;]/)) {
p=0;
parse_list();
} else if ($(' ')) {
p=0;
parse_pre();
} else if ($('{|')) {
p=0;
parse_table();
} else if ($(/^----+$/)) {
p=0;
endl('<hr />');
} else if ($(Insta.BLOCK_IMAGE)) {
p=0;
parse_block_image();
} else {
// handle paragraphs
if ($$('')) {
p = (remain()>1 && ll[1]===(''));
if (p) endl('<p><br>');
} else {
if(!p) {
ps('<p>');
p=1;
}
ps(parse_inline_nowiki(ll[0]) + ' ');
}
sh();
}
return o;
};
function wiki2html(txt,baseurl) {
Insta.conf.baseUrl=baseurl;
return Insta.convert(txt);
}
// ENDFILE: livepreview.js
// STARTFILE: pageinfo.js
//<NOLITE>
function popupFilterPageSize(data) {
return formatBytes(data.length);
}
function popupFilterCountLinks(data) {
var num=countLinks(data);
return String(num) + ' ' + ((num!=1)?popupString('wikiLinks'):popupString('wikiLink'));
}
function popupFilterCountImages(data) {
var num=countImages(data);
return String(num) + ' ' + ((num!=1)?popupString('images'):popupString('image'));
}
function popupFilterCountCategories(data) {
var num=countCategories(data);
return String(num) + ' ' + ((num!=1)?popupString('categories'):popupString('category'));
}
function popupFilterLastModified(data,download) {
var lastmod=download.lastModified;
var now=new Date();
var age=now-lastmod;
if (lastmod && getValueOf('popupLastModified')) {
return (tprintf('%s old', [formatAge(age)])).replace(RegExp(' ','g'), ' ');
}
return '';
}
function formatAge(age) {
// coerce into a number
var a=0+age, aa=a;
var seclen = 1000;
var minlen = 60*seclen;
var hourlen = 60*minlen;
var daylen = 24*hourlen;
var weeklen = 7*daylen;
var numweeks = (a-a%weeklen)/weeklen; a = a-numweeks*weeklen; var sweeks = addunit(numweeks, 'week');
var numdays = (a-a%daylen)/daylen; a = a-numdays*daylen; var sdays = addunit(numdays, 'day');
var numhours = (a-a%hourlen)/hourlen; a = a-numhours*hourlen; var shours = addunit(numhours,'hour');
var nummins = (a-a%minlen)/minlen; a = a-nummins*minlen; var smins = addunit(nummins, 'minute');
var numsecs = (a-a%seclen)/seclen; a = a-numsecs*seclen; var ssecs = addunit(numsecs, 'second');
if (aa > 4*weeklen) { return sweeks; }
if (aa > weeklen) { return sweeks + ' ' + sdays; }
if (aa > daylen) { return sdays + ' ' + shours; }
if (aa > 6*hourlen) { return shours; }
if (aa > hourlen) { return shours + ' ' + smins; }
if (aa > 10*minlen) { return smins; }
if (aa > minlen) { return smins + ' ' + ssecs; }
return ssecs;
}
function addunit(num,str) { return '' + num + ' ' + ((num!=1) ? popupString(str+'s') : popupString(str)) ;}
function runPopupFilters(list, data, download) {
var ret=[];
for (var i=0; i<list.length; ++i) {
if (list[i] && typeof list[i] == 'function') {
var s=list[i](data, download, download.owner.article);
if (s) { ret.push(s); }
}
}
return ret;
}
function getPageInfo(data, download) {
if (!data || data.length === 0) { return popupString('Empty page'); }
var popupFilters=getValueOf('popupFilters') || [];
var extraPopupFilters = getValueOf('extraPopupFilters') || [];
var pageInfoArray = runPopupFilters(popupFilters.concat(extraPopupFilters), data, download);
var pageInfo=pageInfoArray.join(', ');
if (pageInfo !== '' ) { pageInfo = upcaseFirst(pageInfo); }
return pageInfo;
}
// this could be improved!
function countLinks(wikiText) { return wikiText.split('[[').length - 1; }
// if N = # matches, n = # brackets, then
// String.parenSplit(regex) intersperses the N+1 split elements
// with Nn other elements. So total length is
// L= N+1 + Nn = N(n+1)+1. So N=(L-1)/(n+1).
function countImages(wikiText) {
return (wikiText.parenSplit(pg.re.image).length - 1) / (pg.re.imageBracketCount + 1);
}
function countCategories(wikiText) {
return (wikiText.parenSplit(pg.re.category).length - 1) / (pg.re.categoryBracketCount + 1);
}
function popupFilterStubDetect(data, download, article) {
var counts=stubCount(data, article);
if (counts.real) { return popupString('stub'); }
if (counts.sect) { return popupString('section stub'); }
return '';
}
function popupFilterDisambigDetect(data, download, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return ''; }
return (isDisambig(data, article)) ? popupString('disambig') : '';
}
function formatBytes(num) {
return (num > 949) ? (Math.round(num/100)/10+popupString('kB')) : (num +' ' + popupString('bytes')) ;
}
//</NOLITE>
// ENDFILE: pageinfo.js
// STARTFILE: titles.js
/**
@fileoverview Defines the {@link Title} class, and associated crufty functions.
<code>Title</code> deals with article titles and their various
forms. {@link Stringwrapper} is the parent class of
<code>Title</code>, which exists simply to make things a little
neater.
*/
/**
Creates a new Stringwrapper.
@constructor
@class the Stringwrapper class. This base class is not really
useful on its own; it just wraps various common string operations.
*/
function Stringwrapper() {
/**
Wrapper for this.toString().indexOf()
@param {String} x
@type integer
*/
this.indexOf=function(x){return this.toString().indexOf(x);};
/**
Returns this.value.
@type String
*/
this.toString=function(){return this.value;};
/**
Wrapper for {@link String#parenSplit} applied to this.toString()
@param {RegExp} x
@type Array
*/
this.parenSplit=function(x){return this.toString().parenSplit(x);};
/**
Wrapper for this.toString().substring()
@param {String} x
@param {String} y (optional)
@type String
*/
this.substring=function(x,y){
if (typeof y=='undefined') { return this.toString().substring(x); }
return this.toString().substring(x,y);
};
/**
Wrapper for this.toString().split()
@param {String} x
@type Array
*/
this.split=function(x){return this.toString().split(x);};
/**
Wrapper for this.toString().replace()
@param {String} x
@param {String} y
@type String
*/
this.replace=function(x,y){ return this.toString().replace(x,y); };
}
/**
Creates a new <code>Title</code>.
@constructor
@class The Title class. Holds article titles and converts them into
various forms. Also deals with anchors, by which we mean the bits
of the article URL after a # character, representing locations
within an article.
@param {String} value The initial value to assign to the
article. This must be the canonical title (see {@link
Title#value}. Omit this in the constructor and use another function
to set the title if this is unavailable.
*/
function Title(val) {
/**
The canonical article title. This must be in UTF-8 with no
entities, escaping or nasties. Also, underscores should be
replaced with spaces.
@type String
@private
*/
this.value=null;
/**
The canonical form of the anchor. This should be exactly as
it appears in the URL, i.e. with the .C3.0A bits in.
@type String
*/
this.anchor='';
this.setUtf(val);
}
Title.prototype=new Stringwrapper();
/**
Returns the canonical representation of the article title, optionally without anchor.
@param {boolean} omitAnchor
@fixme Decide specs for anchor
@return String The article title and the anchor.
*/
Title.prototype.toString=function(omitAnchor) {
return this.value + ( (!omitAnchor && this.anchor) ? '#' + this.anchorString() : '' );
};
Title.prototype.anchorString=function() {
if (!this.anchor) { return ''; }
var split=this.anchor.parenSplit(/((?:[.][0-9A-F]{2})+)/);
var len=split.length;
for (var j=1; j<len; j+=2) {
// FIXME s/decodeURI/decodeURIComponent/g ?
split[j]=decodeURIComponent(split[j].split('.').join('%')).split('_').join(' ');
}
return split.join('');
};
Title.prototype.urlAnchor=function() {
var split=this.anchor.parenSplit('/((?:[%][0-9A-F]{2})+)/');
var len=split.length;
for (var j=1; j<len; j+=2) {
split[j]=split[j].split('%').join('.');
}
return split.join('');
};
Title.prototype.anchorFromUtf=function(str) {
this.anchor=encodeURIComponent(str.split(' ').join('_'))
.split('%3A').join(':').split("'").join('%27').split('%').join('.');
};
Title.fromURL=function(h) {
return new Title().fromURL(h);
};
Title.prototype.fromURL=function(h) {
if (typeof h != 'string') {
this.value=null;
return this;
}
// NOTE : playing with decodeURI, encodeURI, escape, unescape,
// we seem to be able to replicate the IE borked encoding
// IE doesn't do this new-fangled utf-8 thing.
// and it's worse than that.
// IE seems to treat the query string differently to the rest of the url
// the query is treated as bona-fide utf8, but the first bit of the url is pissed around with
// we fix up & for all browsers, just in case.
var splitted=h.split('?');
splitted[0]=splitted[0].split('&').join('%26');
h=splitted.join('?');
var contribs=pg.re.contribs.exec(h);
if (contribs) {
if (contribs[1]=='title=') { contribs[3]=contribs[3].split('+').join(' '); }
var u=new Title(contribs[3]);
this.setUtf(this.decodeNasties(mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + u.stripNamespace()));
return this;
}
var email=pg.re.email.exec(h);
if (email) {
this.setUtf(this.decodeNasties(mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + new Title(email[3]).stripNamespace()));
return this;
}
var backlinks=pg.re.backlinks.exec(h);
if (backlinks) {
this.setUtf(this.decodeNasties(new Title(backlinks[3])));
return this;
}
//A dummy title object for a Special:Diff link.
var specialdiff=pg.re.specialdiff.exec(h);
if (specialdiff) {
this.setUtf(this.decodeNasties(new Title(mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId] + ':Diff')));
return this;
}
// no more special cases to check --
// hopefully it's not a disguised user-related or specially treated special page
var m=pg.re.main.exec(h);
if(m === null) { this.value=null; }
else {
var fromBotInterface = /[?](.+[&])?title=/.test(h);
if (fromBotInterface) {
m[2]=m[2].split('+').join('_');
}
var extracted = m[2] + (m[3] ? '#' + m[3] : '');
if (pg.flag.isSafari && /%25[0-9A-Fa-f]{2}/.test(extracted)) {
// Fix Safari issue
// Safari sometimes encodes % as %25 in UTF-8 encoded strings like %E5%A3 -> %25E5%25A3.
this.setUtf(decodeURIComponent(unescape(extracted)));
} else {
this.setUtf(this.decodeNasties(extracted));
}
}
return this;
};
Title.prototype.decodeNasties=function(txt) {
var ret= this.decodeEscapes(decodeURI(txt));
ret = ret.replace(/[_ ]*$/, '');
return ret;
};
Title.prototype.decodeEscapes=function(txt) {
var split=txt.parenSplit(/((?:[%][0-9A-Fa-f]{2})+)/);
var len=split.length;
for (var i=1; i<len; i=i+2) {
// FIXME is decodeURIComponent better?
split[i]=unescape(split[i]);
}
return split.join('');
};
Title.fromAnchor=function(a) {
return new Title().fromAnchor(a);
};
Title.prototype.fromAnchor=function(a) {
if (!a) { this.value=null; return this; }
return this.fromURL(a.href);
};
Title.fromWikiText=function(txt) {
return new Title().fromWikiText(txt);
};
Title.prototype.fromWikiText=function(txt) {
// FIXME - testing needed
txt=myDecodeURI(txt);
this.setUtf(txt);
return this;
};
Title.prototype.hintValue=function(){
if(!this.value) { return ''; }
return safeDecodeURI(this.value);
};
//<NOLITE>
Title.prototype.toUserName=function(withNs) {
if (this.namespaceId() != pg.nsUserId && this.namespaceId() != pg.nsUsertalkId) {
this.value=null;
return;
}
this.value = (withNs ? mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' : '') + this.stripNamespace().split('/')[0];
};
Title.prototype.userName=function(withNs) {
var t=(new Title(this.value));
t.toUserName(withNs);
if (t.value) { return t; }
return null;
};
Title.prototype.toTalkPage=function() {
// convert article to a talk page, or if we can't, return null
// In other words: return null if this ALREADY IS a talk page
// and return the corresponding talk page otherwise
//
// Per https://www.mediawiki.org/wiki/Manual:Namespace#Subject_and_talk_namespaces
// * All discussion namespaces have odd-integer indices
// * The discussion namespace index for a specific namespace with index n is n + 1
if (this.value === null) { return null; }
var namespaceId = this.namespaceId();
if (namespaceId>=0 && namespaceId % 2 === 0) //non-special and subject namespace
{
var localizedNamespace = mw.config.get('wgFormattedNamespaces')[namespaceId+1];
if (typeof localizedNamespace!=='undefined')
{
if (localizedNamespace === '') {
this.value = this.stripNamespace();
} else {
this.value = localizedNamespace.split(' ').join('_') + ':' + this.stripNamespace();
}
return this.value;
}
}
this.value=null;
return null;
};
//</NOLITE>
// Return canonical, localized namespace
Title.prototype.namespace=function() {
return mw.config.get('wgFormattedNamespaces')[this.namespaceId()];
};
Title.prototype.namespaceId=function() {
var n=this.value.indexOf(':');
if (n<0) { return 0; } //mainspace
var namespaceId = mw.config.get('wgNamespaceIds')[this.value.substring(0,n).split(' ').join('_').toLowerCase()];
if (typeof namespaceId=='undefined') return 0; //mainspace
return namespaceId;
};
//<NOLITE>
Title.prototype.talkPage=function() {
var t=new Title(this.value);
t.toTalkPage();
if (t.value) { return t; }
return null;
};
Title.prototype.isTalkPage=function() {
if (this.talkPage()===null) { return true; }
return false;
};
Title.prototype.toArticleFromTalkPage=function() {
//largely copy/paste from toTalkPage above.
if (this.value === null) { return null; }
var namespaceId = this.namespaceId();
if (namespaceId >= 0 && namespaceId % 2 == 1) //non-special and talk namespace
{
var localizedNamespace = mw.config.get('wgFormattedNamespaces')[namespaceId-1];
if (typeof localizedNamespace!=='undefined')
{
if (localizedNamespace === '') {
this.value = this.stripNamespace();
} else {
this.value = localizedNamespace.split(' ').join('_') + ':' + this.stripNamespace();
}
return this.value;
}
}
this.value=null;
return null;
};
Title.prototype.articleFromTalkPage=function() {
var t=new Title(this.value);
t.toArticleFromTalkPage();
if (t.value) { return t; }
return null;
};
Title.prototype.articleFromTalkOrArticle=function() {
var t=new Title(this.value);
if ( t.toArticleFromTalkPage() ) { return t; }
return this;
};
Title.prototype.isIpUser=function() {
return pg.re.ipUser.test(this.userName());
};
//</NOLITE>
Title.prototype.stripNamespace=function(){ // returns a string, not a Title
var n=this.value.indexOf(':');
if (n<0) { return this.value; }
var namespaceId = this.namespaceId();
if (namespaceId === pg.nsMainspaceId) return this.value;
return this.value.substring(n+1);
};
Title.prototype.setUtf=function(value){
if (!value) { this.value=''; return; }
var anch=value.indexOf('#');
if(anch < 0) { this.value=value.split('_').join(' '); this.anchor=''; return; }
this.value=value.substring(0,anch).split('_').join(' ');
this.anchor=value.substring(anch+1);
this.ns=null; // wait until namespace() is called
};
Title.prototype.setUrl=function(urlfrag) {
var anch=urlfrag.indexOf('#');
this.value=safeDecodeURI(urlfrag.substring(0,anch));
this.anchor=this.value.substring(anch+1);
};
Title.prototype.append=function(x){
this.setUtf(this.value + x);
};
Title.prototype.urlString=function(x) {
if(!x) { x={}; }
var v=this.toString(true);
if (!x.omitAnchor && this.anchor) { v+= '#' + this.urlAnchor(); }
if (!x.keepSpaces) { v=v.split(' ').join('_'); }
return encodeURI(v).split('&').join('%26').split('?').join('%3F').split('+').join('%2B');
};
Title.prototype.removeAnchor=function() {
return new Title(this.toString(true));
};
Title.prototype.toUrl=function() {
return pg.wiki.titlebase + this.urlString();
};
function parseParams(url) {
var specialDiff = pg.re.specialdiff.exec(url);
if (specialDiff)
{
var split= specialDiff[1].split('/');
if (split.length==1) return {oldid:split[0], diff: 'prev'};
else if (split.length==2) return {oldid: split[0], diff: split[1]};
}
var ret={};
if (url.indexOf('?')==-1) { return ret; }
url = url.split('#')[0];
var s=url.split('?').slice(1).join();
var t=s.split('&');
for (var i=0; i<t.length; ++i) {
var z=t[i].split('=');
z.push(null);
ret[z[0]]=z[1];
}
//Diff revision with no oldid is interpreted as a diff to the previous revision by MediaWiki
if (ret.diff && typeof(ret.oldid)==='undefined')
{
ret.oldid = "prev";
}
//Documentation seems to say something different, but oldid can also accept prev/next, and Echo is emitting such URLs. Simple fixup during parameter decoding:
if (ret.oldid && (ret.oldid==='prev' || ret.oldid==='next' || ret.oldid==='cur'))
{
var helper = ret.diff;
ret.diff = ret.oldid;
ret.oldid = helper;
}
return ret;
}
// (a) myDecodeURI (first standard decodeURI, then pg.re.urlNoPopup)
// (b) change spaces to underscores
// (c) encodeURI (just the straight one, no pg.re.urlNoPopup)
function myDecodeURI (str) {
var ret;
// FIXME decodeURIComponent??
try { ret=decodeURI(str.toString()); }
catch (summat) { return str; }
for (var i=0; i<pg.misc.decodeExtras.length; ++i) {
var from=pg.misc.decodeExtras[i].from;
var to=pg.misc.decodeExtras[i].to;
ret=ret.split(from).join(to);
}
return ret;
}
function safeDecodeURI(str) { var ret=myDecodeURI(str); return ret || str; }
///////////
// TESTS //
///////////
//<NOLITE>
function isDisambig(data, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return false; }
return ! article.isTalkPage() && pg.re.disambig.test(data);
}
function stubCount(data, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return false; }
var sectStub=0;
var realStub=0;
if (pg.re.stub.test(data)) {
var s=data.parenSplit(pg.re.stub);
for (var i=1; i<s.length; i=i+2) {
if (s[i]) { ++sectStub; }
else { ++realStub; }
}
}
return { real: realStub, sect: sectStub };
}
function isValidImageName(str){ // extend as needed...
return ( str.indexOf('{') == -1 );
}
function isInStrippableNamespace(article) {
// Does the namespace allow subpages
// Note, would be better if we had access to wgNamespacesWithSubpages
return ( article.namespaceId() !== 0 );
}
function isInMainNamespace(article) { return article.namespaceId() === 0; }
function anchorContainsImage(a) {
// iterate over children of anchor a
// see if any are images
if (a === null) { return false; }
var kids=a.childNodes;
for (var i=0; i<kids.length; ++i) { if (kids[i].nodeName=='IMG') { return true; } }
return false;
}
//</NOLITE>
function isPopupLink(a) {
// NB for performance reasons, TOC links generally return true
// they should be stripped out later
if (!markNopopupSpanLinks.done) { markNopopupSpanLinks(); }
if (a.inNopopupSpan) { return false; }
// FIXME is this faster inline?
if (a.onmousedown || a.getAttribute('nopopup')) { return false; }
var h=a.href;
if (h === document.location.href+'#') { return false; }
if (!pg.re.basenames.test(h)) { return false; }
if (!pg.re.urlNoPopup.test(h)) { return true; }
return (
(pg.re.email.test(h) || pg.re.contribs.test(h) || pg.re.backlinks.test(h) || pg.re.specialdiff.test(h)) &&
h.indexOf('&limit=') == -1 );
}
function markNopopupSpanLinks() {
if( !getValueOf('popupOnlyArticleLinks'))
fixVectorMenuPopups();
var s = $('.nopopups').toArray();
for (var i=0; i<s.length; ++i) {
var as=s[i].getElementsByTagName('a');
for (var j=0; j<as.length; ++j) {
as[j].inNopopupSpan=true;
}
}
markNopopupSpanLinks.done=true;
}
function fixVectorMenuPopups() {
$('div.vectorMenu h3:first a:first').prop('inNopopupSpan', true);
}
// ENDFILE: titles.js
// STARTFILE: getpage.js
//////////////////////////////////////////////////
// Wiki-specific downloading
//
// Schematic for a getWiki call
//
// getPageWithCaching
// |
// false | true
// getPage<-[findPictureInCache]->-onComplete(a fake download)
// \.
// (async)->addPageToCache(download)->-onComplete(download)
// check cache to see if page exists
function getPageWithCaching(url, onComplete, owner) {
log('getPageWithCaching, url='+url);
var i=findInPageCache(url);
var d;
if (i > -1) {
d=fakeDownload(url, owner.idNumber, onComplete,
pg.cache.pages[i].data, pg.cache.pages[i].lastModified,
owner);
} else {
d=getPage(url, onComplete, owner);
if (d && owner && owner.addDownload) {
owner.addDownload(d);
d.owner=owner;
}
}
}
function getPage(url, onComplete, owner) {
log('getPage');
var callback= function (d) { if (!d.aborted) {addPageToCache(d); onComplete(d);} };
return startDownload(url, owner.idNumber, callback);
}
function findInPageCache(url) {
for (var i=0; i<pg.cache.pages.length; ++i) {
if (url==pg.cache.pages[i].url) { return i; }
}
return -1;
}
function addPageToCache(download) {
log('addPageToCache '+download.url);
var page = {url: download.url, data: download.data, lastModified: download.lastModified};
return pg.cache.pages.push(page);
}
// ENDFILE: getpage.js
// STARTFILE: parensplit.js
//////////////////////////////////////////////////
// parenSplit
// String.prototype.parenSplit should do what ECMAscript says String.prototype.split does,
// interspersing paren matches (regex capturing groups) between the split elements.
// i.e. 'abc'.split(/(b)/)) should return ['a','b','c'], not ['a','c']
if (String('abc'.split(/(b)/))!='a,b,c') {
// broken String.split, e.g. konq, IE < 10
String.prototype.parenSplit=function (re) {
re=nonGlobalRegex(re);
var s=this;
var m=re.exec(s);
var ret=[];
while (m && s) {
// without the following loop, we have
// 'ab'.parenSplit(/a|(b)/) != 'ab'.split(/a|(b)/)
for(var i=0; i<m.length; ++i) {
if (typeof m[i]=='undefined') m[i]='';
}
ret.push(s.substring(0,m.index));
ret = ret.concat(m.slice(1));
s=s.substring(m.index + m[0].length);
m=re.exec(s);
}
ret.push(s);
return ret;
};
} else {
String.prototype.parenSplit=function (re) { return this.split(re); };
String.prototype.parenSplit.isNative=true;
}
function nonGlobalRegex(re) {
var s=re.toString();
var flags='';
for (var j=s.length; s.charAt(j) != '/'; --j) {
if (s.charAt(j) != 'g') { flags += s.charAt(j); }
}
var t=s.substring(1,j);
return RegExp(t,flags);
}
// ENDFILE: parensplit.js
// STARTFILE: tools.js
// IE madness with encoding
// ========================
//
// suppose throughout that the page is in utf8, like wikipedia
//
// if a is an anchor DOM element and a.href should consist of
//
// http://host.name.here/wiki/foo?bar=baz
//
// then IE gives foo as "latin1-encoded" utf8; we have foo = decode_utf8(decodeURI(foo_ie))
// but IE gives bar=baz correctly as plain utf8
//
// ---------------------------------
//
// IE's xmlhttp doesn't understand utf8 urls. Have to use encodeURI here.
//
// ---------------------------------
//
// summat else
// Source: http://aktuell.de.selfhtml.org/artikel/javascript/utf8b64/utf8.htm
//<NOLITE>
function getJsObj(json) {
try {
var json_ret = JSON.parse(json);
if( json_ret.warnings ) {
for( var w=0; w < json_ret.warnings.length; w++ ) {
if( json_ret.warnings[w]['*'] ) {
log( json_ret.warnings[w]['*'] );
} else {
log( json_ret.warnings[w]['warnings'] );
}
}
} else if ( json_ret.error ) {
errlog( json_ret.error.code + ': ' + json_ret.error.info );
}
return json_ret;
} catch (someError) {
errlog('Something went wrong with getJsObj, json='+json);
return 1;
}
}
function anyChild(obj) {
for (var p in obj) {
return obj[p];
}
return null;
}
//</NOLITE>
function upcaseFirst(str) {
if (typeof str != typeof '' || str === '') return '';
return str.charAt(0).toUpperCase() + str.substring(1);
}
function findInArray(arr, foo) {
if (!arr || !arr.length) { return -1; }
var len=arr.length;
for (var i=0; i<len; ++i) { if (arr[i]==foo) { return i; } }
return -1;
}
/* eslint-disable no-unused-vars */
function nextOne (array, value) {
// NB if the array has two consecutive entries equal
// then this will loop on successive calls
var i=findInArray(array, value);
if (i<0) { return null; }
return array[i+1];
}
/* eslint-enable no-unused-vars */
function literalizeRegex(str){
return mw.util.escapeRegExp(str);
}
String.prototype.entify=function() {
//var shy='­';
return this.split('&').join('&').split('<').join('<').split('>').join('>'/*+shy*/).split('"').join('"');
};
// Array filter function
function removeNulls(val) { return val !== null; }
function joinPath(list) {
return list.filter(removeNulls).join('/');
}
function simplePrintf(str, subs) {
if (!str || !subs) { return str; }
var ret=[];
var s=str.parenSplit(/(%s|\$[0-9]+)/);
var i=0;
do {
ret.push(s.shift());
if ( !s.length ) { break; }
var cmd=s.shift();
if (cmd == '%s') {
if ( i < subs.length ) { ret.push(subs[i]); } else { ret.push(cmd); }
++i;
} else {
var j=parseInt( cmd.replace('$', ''), 10 ) - 1;
if ( j > -1 && j < subs.length ) { ret.push(subs[j]); } else { ret.push(cmd); }
}
} while (s.length > 0);
return ret.join('');
}
/* eslint-disable no-unused-vars */
function isString(x) { return (typeof x === 'string' || x instanceof String); }
function isNumber(x) { return (typeof x === 'number' || x instanceof Number); }
function isRegExp(x) { return x instanceof RegExp; }
function isArray (x) { return x instanceof Array; }
function isObject(x) { return x instanceof Object; }
function isFunction(x) {
return !isRegExp(x) && (typeof x === 'function' || x instanceof Function);
}
/* eslint-enable no-unused-vars */
function repeatString(s,mult) {
var ret='';
for (var i=0; i<mult; ++i) { ret += s; }
return ret;
}
function zeroFill(s, min) {
min = min || 2;
var t=s.toString();
return repeatString('0', min - t.length) + t;
}
function map(f, o) {
if (isArray(o)) { return map_array(f,o); }
return map_object(f,o);
}
function map_array(f,o) {
var ret=[];
for (var i=0; i<o.length; ++i) {
ret.push(f(o[i]));
}
return ret;
}
function map_object(f,o) {
var ret={};
for (var i in o) { ret[o]=f(o[i]); }
return ret;
}
pg.escapeQuotesHTML = function ( text ) {
return text
.replace(/&/g, "&")
.replace(/"/g, """)
.replace(/</g, "<")
.replace(/>/g, ">");
};
// ENDFILE: tools.js
// STARTFILE: dab.js
//<NOLITE>
//////////////////////////////////////////////////
// Dab-fixing code
//
function retargetDab(newTarget, oldTarget, friendlyCurrentArticleName, titleToEdit) {
log('retargetDab: newTarget='+newTarget + ' oldTarget=' + oldTarget);
return changeLinkTargetLink(
{newTarget: newTarget,
text: newTarget.split(' ').join(' '),
hint: tprintf('disambigHint', [newTarget]),
summary: simplePrintf(
getValueOf('popupFixDabsSummary'), [friendlyCurrentArticleName, newTarget ]),
clickButton: getValueOf('popupDabsAutoClick'), minor: true, oldTarget: oldTarget,
watch: getValueOf('popupWatchDisambiggedPages'),
title: titleToEdit});
}
function listLinks(wikitext, oldTarget, titleToEdit) {
// mediawiki strips trailing spaces, so we do the same
// testcase: https://en.wikipedia.org/w/index.php?title=Radial&oldid=97365633
var reg=RegExp('\\[\\[([^|]*?) *(\\||\\]\\])', 'gi');
var ret=[];
var splitted=wikitext.parenSplit(reg);
// ^[a-z]+ should match interwiki links, hopefully (case-insensitive)
// and ^[a-z]* should match those and [[:Category...]] style links too
var omitRegex=RegExp('^[a-z]*:|^[Ss]pecial:|^[Ii]mage|^[Cc]ategory');
var friendlyCurrentArticleName= oldTarget.toString();
var wikPos = getValueOf('popupDabWiktionary');
for (var i=1; i<splitted.length; i=i+3) {
if (typeof splitted[i] == typeof 'string' && splitted[i].length>0 && !omitRegex.test(splitted[i])) {
ret.push( retargetDab(splitted[i], oldTarget, friendlyCurrentArticleName, titleToEdit) );
} /* if */
} /* for loop */
ret = rmDupesFromSortedList(ret.sort());
if (wikPos) {
var wikTarget='wiktionary:' +
friendlyCurrentArticleName.replace( RegExp('^(.+)\\s+[(][^)]+[)]\\s*$'), '$1' );
var meth;
if (wikPos.toLowerCase() == 'first') { meth = 'unshift'; }
else { meth = 'push'; }
ret[meth]( retargetDab(wikTarget, oldTarget, friendlyCurrentArticleName, titleToEdit) );
}
ret.push(changeLinkTargetLink(
{ newTarget: null,
text: popupString('remove this link').split(' ').join(' '),
hint: popupString("remove all links to this disambig page from this article"),
clickButton: getValueOf('popupDabsAutoClick'), oldTarget: oldTarget,
summary: simplePrintf(getValueOf('popupRmDabLinkSummary'), [friendlyCurrentArticleName]),
watch: getValueOf('popupWatchDisambiggedPages'),
title: titleToEdit
}));
return ret;
}
function rmDupesFromSortedList(list) {
var ret=[];
for (var i=0; i<list.length; ++i) {
if (ret.length === 0 || list[i]!=ret[ret.length-1]) { ret.push(list[i]); }
}
return ret;
}
function makeFixDab(data, navpop) {
// grab title from parent popup if there is one; default exists in changeLinkTargetLink
var titleToEdit=(navpop.parentPopup && navpop.parentPopup.article.toString());
var list=listLinks(data, navpop.originalArticle, titleToEdit);
if (list.length === 0) { log('listLinks returned empty list'); return null; }
var html='<hr />' + popupString('Click to disambiguate this link to:') + '<br>';
html+=list.join(', ');
return html;
}
function makeFixDabs(wikiText, navpop) {
if (getValueOf('popupFixDabs') && isDisambig(wikiText, navpop.article) &&
Title.fromURL(location.href).namespaceId() != pg.nsSpecialId &&
navpop.article.talkPage() ) {
setPopupHTML(makeFixDab(wikiText, navpop), 'popupFixDab', navpop.idNumber);
}
}
function popupRedlinkHTML(article) {
return changeLinkTargetLink(
{ newTarget: null, text: popupString('remove this link').split(' ').join(' '),
hint: popupString("remove all links to this page from this article"),
clickButton: getValueOf('popupRedlinkAutoClick'),
oldTarget: article.toString(),
summary: simplePrintf(getValueOf('popupRedlinkSummary'), [article.toString()])});
}
//</NOLITE>
// ENDFILE: dab.js
// STARTFILE: htmloutput.js
// this has to use a timer loop as we don't know if the DOM element exists when we want to set the text
function setPopupHTML (str, elementId, popupId, onSuccess, append) {
if (typeof popupId === 'undefined') {
//console.error('popupId is not defined in setPopupHTML, html='+str.substring(0,100));
popupId = pg.idNumber;
}
var popupElement=document.getElementById(elementId+popupId);
if (popupElement) {
if (!append) { popupElement.innerHTML=''; }
if (isString(str)) {
popupElement.innerHTML+=str;
} else {
popupElement.appendChild(str);
}
if (onSuccess) { onSuccess(); }
setTimeout(checkPopupPosition, 100);
return true;
} else {
// call this function again in a little while...
setTimeout(function(){
setPopupHTML(str,elementId,popupId,onSuccess);
}, 600);
}
return null;
}
//<NOLITE>
function setPopupTrailer(str,id) {return setPopupHTML(str, 'popupData', id);}
//</NOLITE>
// args.navpopup is mandatory
// optional: args.redir, args.redirTarget
// FIXME: ye gods, this is ugly stuff
function fillEmptySpans(args) {
// if redir is present and true then redirTarget is mandatory
var redir=true;
var rcid;
if (typeof args != 'object' || typeof args.redir == 'undefined' || !args.redir) { redir=false; }
var a=args.navpopup.parentAnchor;
var article, hint=null, oldid=null, params={};
if (redir && typeof args.redirTarget == typeof {}) {
article=args.redirTarget;
//hint=article.hintValue();
} else {
article=(new Title()).fromAnchor(a);
hint=a.originalTitle || article.hintValue();
params=parseParams(a.href);
oldid=(getValueOf('popupHistoricalLinks')) ? params.oldid : null;
rcid=params.rcid;
}
var x={ article:article, hint: hint, oldid: oldid, rcid: rcid, navpop:args.navpopup, params:params };
var structure=pg.structures[getValueOf('popupStructure')];
if (typeof structure != 'object') {
setPopupHTML('popupError', 'Unknown structure (this should never happen): '+
pg.option.popupStructure, args.navpopup.idNumber);
return;
}
var spans=flatten(pg.misc.layout);
var numspans = spans.length;
var redirs=pg.misc.redirSpans;
for (var i=0; i<numspans; ++i) {
var found = redirs && (redirs.indexOf( spans[i] ) !== -1);
//log('redir='+redir+', found='+found+', spans[i]='+spans[i]);
if ( (found && !redir) || (!found && redir) ) {
//log('skipping this set of the loop');
continue;
}
var structurefn=structure[spans[i]];
var setfn = setPopupHTML;
if (getValueOf('popupActiveNavlinks') &&
(spans[i].indexOf('popupTopLinks')===0 || spans[i].indexOf('popupRedirTopLinks')===0)
) {
setfn = setPopupTipsAndHTML;
}
switch (typeof structurefn) {
case 'function':
log('running '+spans[i]+'({article:'+x.article+', hint:'+x.hint+', oldid: '+x.oldid+'})');
setfn(structurefn(x), spans[i], args.navpopup.idNumber);
break;
case 'string':
setfn(structurefn, spans[i], args.navpopup.idNumber);
break;
default:
errlog('unknown thing with label '+spans[i] + ' (span index was ' + i + ')');
break;
}
}
}
// flatten an array
function flatten(list, start) {
var ret=[];
if (typeof start == 'undefined') { start=0; }
for (var i=start; i<list.length; ++i) {
if (typeof list[i] == typeof []) {
return ret.concat(flatten(list[i])).concat(flatten(list, i+1));
}
else { ret.push(list[i]); }
}
return ret;
}
// Generate html for whole popup
function popupHTML (a) {
getValueOf('popupStructure');
var structure=pg.structures[pg.option.popupStructure];
if (typeof structure != 'object') {
//return 'Unknown structure: '+pg.option.popupStructure;
// override user choice
pg.option.popupStructure=pg.optionDefault.popupStructure;
return popupHTML(a);
}
if (typeof structure.popupLayout != 'function') { return 'Bad layout'; }
pg.misc.layout=structure.popupLayout();
if (typeof structure.popupRedirSpans === 'function') { pg.misc.redirSpans=structure.popupRedirSpans(); }
else { pg.misc.redirSpans=[]; }
return makeEmptySpans(pg.misc.layout, a.navpopup);
}
function makeEmptySpans (list, navpop) {
var ret='';
for (var i=0; i<list.length; ++i) {
if (typeof list[i] == typeof '') {
ret += emptySpanHTML(list[i], navpop.idNumber, 'div');
} else if (typeof list[i] == typeof [] && list[i].length > 0 ) {
ret = ret.parenSplit(RegExp('(</[^>]*?>$)')).join(makeEmptySpans(list[i], navpop));
} else if (typeof list[i] == typeof {} && list[i].nodeType ) {
ret += emptySpanHTML(list[i].name, navpop.idNumber, list[i].nodeType);
}
}
return ret;
}
function emptySpanHTML(name, id, tag, classname) {
tag = tag || 'span';
if (!classname) { classname = emptySpanHTML.classAliases[name]; }
classname = classname || name;
if (name == getValueOf('popupDragHandle')) { classname += ' popupDragHandle'; }
return simplePrintf('<%s id="%s" class="%s"></%s>', [tag, name + id, classname, tag]);
}
emptySpanHTML.classAliases={ 'popupSecondPreview': 'popupPreview' };
// generate html for popup image
// <a id="popupImageLinkn"><img id="popupImagen">
// where n=idNumber
function imageHTML(article, idNumber) {
return simplePrintf('<a id="popupImageLink$1">' +
'<img align="right" valign="top" id="popupImg$1" style="display: none;"></img>' +
'</a>', [ idNumber ]);
}
function popTipsSoonFn(id, when, popData) {
if (!when) { when=250; }
var popTips=function(){ setupTooltips(document.getElementById(id), false, true, popData); };
return function() { setTimeout( popTips, when, popData ); };
}
function setPopupTipsAndHTML(html, divname, idnumber, popData) {
setPopupHTML(html, divname, idnumber,
getValueOf('popupSubpopups') ?
popTipsSoonFn(divname + idnumber, null, popData) :
null);
}
// ENDFILE: htmloutput.js
// STARTFILE: mouseout.js
//////////////////////////////////////////////////
// fuzzy checks
function fuzzyCursorOffMenus(x,y, fuzz, parent) {
if (!parent) { return null; }
var uls=parent.getElementsByTagName('ul');
for (var i=0; i<uls.length; ++i) {
if (uls[i].className=='popup_menu') {
if (uls[i].offsetWidth > 0) return false;
} // else {document.title+='.';}
}
return true;
}
function checkPopupPosition () { // stop the popup running off the right of the screen
// FIXME avoid pg.current.link
if (pg.current.link && pg.current.link.navpopup)
pg.current.link.navpopup.limitHorizontalPosition();
}
function mouseOutWikiLink () {
//console ('mouseOutWikiLink');
var a=this;
removeModifierKeyHandler(a);
if (a.navpopup === null || typeof a.navpopup === 'undefined') return;
if ( ! a.navpopup.isVisible() ) {
a.navpopup.banish();
return;
}
restoreTitle(a);
Navpopup.tracker.addHook(posCheckerHook(a.navpopup));
}
function posCheckerHook(navpop) {
return function() {
if (!navpop.isVisible()) { return true; /* remove this hook */ }
if (Navpopup.tracker.dirty) {
return false;
}
var x=Navpopup.tracker.x, y=Navpopup.tracker.y;
var mouseOverNavpop = navpop.isWithin(x,y,navpop.fuzz, navpop.mainDiv) ||
!fuzzyCursorOffMenus(x,y,navpop.fuzz, navpop.mainDiv);
// FIXME it'd be prettier to do this internal to the Navpopup objects
var t=getValueOf('popupHideDelay');
if (t) { t = t * 1000; }
if (!t) {
if(!mouseOverNavpop) {
if(navpop.parentAnchor) {
restoreTitle( navpop.parentAnchor );
}
navpop.banish();
return true; /* remove this hook */
}
return false;
}
// we have a hide delay set
var d=+(new Date());
if ( !navpop.mouseLeavingTime ) {
navpop.mouseLeavingTime = d;
return false;
}
if ( mouseOverNavpop ) {
navpop.mouseLeavingTime=null;
return false;
}
if (d - navpop.mouseLeavingTime > t) {
navpop.mouseLeavingTime=null;
navpop.banish(); return true; /* remove this hook */
}
return false;
};
}
function runStopPopupTimer(navpop) {
// at this point, we should have left the link but remain within the popup
// so we call this function again until we leave the popup.
if (!navpop.stopPopupTimer) {
navpop.stopPopupTimer=setInterval(posCheckerHook(navpop), 500);
navpop.addHook(function(){clearInterval(navpop.stopPopupTimer);},
'hide', 'before');
}
}
// ENDFILE: mouseout.js
// STARTFILE: previewmaker.js
/**
@fileoverview
Defines the {@link Previewmaker} object, which generates short previews from wiki markup.
*/
/**
Creates a new Previewmaker
@constructor
@class The Previewmaker class. Use an instance of this to generate short previews from Wikitext.
@param {String} wikiText The Wikitext source of the page we wish to preview.
@param {String} baseUrl The url we should prepend when creating relative urls.
@param {Navpopup} owner The navpop associated to this preview generator
*/
function Previewmaker(wikiText, baseUrl, owner) {
/** The wikitext which is manipulated to generate the preview. */
this.originalData=wikiText;
this.baseUrl=baseUrl;
this.owner=owner;
this.maxCharacters=getValueOf('popupMaxPreviewCharacters');
this.maxSentences=getValueOf('popupMaxPreviewSentences');
this.setData();
}
Previewmaker.prototype.setData=function() {
var maxSize=Math.max(10000, 2*this.maxCharacters);
this.data=this.originalData.substring(0,maxSize);
};
/** Remove HTML comments
@private
*/
Previewmaker.prototype.killComments = function () {
// this also kills one trailing newline, eg [[diamyo]]
this.data=this.data.replace(RegExp('^<!--[^$]*?-->\\n|\\n<!--[^$]*?-->(?=\\n)|<!--[^$]*?-->', 'g'), '');
};
/**
@private
*/
Previewmaker.prototype.killDivs = function () {
// say goodbye, divs (can be nested, so use * not *?)
this.data=this.data.replace(RegExp('< *div[^>]* *>[\\s\\S]*?< */ *div *>',
'gi'), '');
};
/**
@private
*/
Previewmaker.prototype.killGalleries = function () {
this.data=this.data.replace(RegExp('< *gallery[^>]* *>[\\s\\S]*?< */ *gallery *>',
'gi'), '');
};
/**
@private
*/
Previewmaker.prototype.kill = function(opening, closing, subopening, subclosing, repl) {
var oldk=this.data;
var k=this.killStuff(this.data, opening, closing, subopening, subclosing, repl);
while (k.length < oldk.length) {
oldk=k;
k=this.killStuff(k, opening, closing, subopening, subclosing, repl);
}
this.data=k;
};
/**
@private
*/
Previewmaker.prototype.killStuff = function (txt, opening, closing, subopening, subclosing, repl) {
var op=this.makeRegexp(opening);
var cl=this.makeRegexp(closing, '^');
var sb=subopening ? this.makeRegexp(subopening, '^') : null;
var sc=subclosing ? this.makeRegexp(subclosing, '^') : cl;
if (!op || !cl) {
alert('Navigation Popups error: op or cl is null! something is wrong.');
return;
}
if (!op.test(txt)) { return txt; }
var ret='';
var opResult = op.exec(txt);
ret = txt.substring(0,opResult.index);
txt=txt.substring(opResult.index+opResult[0].length);
var depth = 1;
while (txt.length > 0) {
var removal=0;
if (depth==1 && cl.test(txt)) {
depth--;
removal=cl.exec(txt)[0].length;
} else if (depth > 1 && sc.test(txt)) {
depth--;
removal=sc.exec(txt)[0].length;
}else if (sb && sb.test(txt)) {
depth++;
removal=sb.exec(txt)[0].length;
}
if ( !removal ) { removal = 1; }
txt=txt.substring(removal);
if (depth === 0) { break; }
}
return ret + (repl || '') + txt;
};
/**
@private
*/
Previewmaker.prototype.makeRegexp = function (x, prefix, suffix) {
prefix = prefix || '';
suffix = suffix || '';
var reStr='';
var flags='';
if (isString(x)) {
reStr=prefix + literalizeRegex(x) + suffix;
} else if (isRegExp(x)) {
var s=x.toString().substring(1);
var sp=s.split('/');
flags=sp[sp.length-1];
sp[sp.length-1]='';
s=sp.join('/');
s=s.substring(0,s.length-1);
reStr= prefix + s + suffix;
} else {
log ('makeRegexp failed');
}
log ('makeRegexp: got reStr=' + reStr + ', flags=' + flags);
return RegExp(reStr, flags);
};
/**
@private
*/
Previewmaker.prototype.killBoxTemplates = function () {
// taxobox removal... in fact, there's a saudiprincebox_begin, so let's be more general
// also, have float_begin, ... float_end
this.kill(RegExp('[{][{][^{}\\s|]*?(float|box)[_ ](begin|start)', 'i'), /[}][}]\s*/, '{{');
// infoboxes etc
// from [[User:Zyxw/popups.js]]: kill frames too
this.kill(RegExp('[{][{][^{}\\s|]*?(infobox|elementbox|frame)[_ ]', 'i'), /[}][}]\s*/, '{{');
};
/**
@private
*/
Previewmaker.prototype.killTemplates = function () {
// the plus sign is to avoid transcluding [[Template:', ']]
this.kill('{'+'{', '}'+'}', '{', '}', ' ');
};
/**
@private
*/
Previewmaker.prototype.killTables = function () {
// tables are bad, too
// this can be slow, but it's an inprovement over a browser hang
// torture test: [[Comparison_of_Intel_Central_Processing_Units]]
this.kill('{|', /[|]}\s*/, '{|');
this.kill(/<table.*?>/i, /<\/table.*?>/i, /<table.*?>/i);
// remove lines starting with a pipe for the hell of it (?)
this.data=this.data.replace(RegExp('^[|].*$', 'mg'), '');
};
/**
@private
*/
Previewmaker.prototype.killImages = function () {
var forbiddenNamespaceAliases = [];
jQuery.each(mw.config.get('wgNamespaceIds'), function(_localizedNamespaceLc, _namespaceId) {
if (_namespaceId!=pg.nsImageId && _namespaceId!=pg.nsCategoryId) return;
forbiddenNamespaceAliases.push(_localizedNamespaceLc.split(' ').join('[ _]')); //todo: escape regexp fragments!
});
// images and categories are a nono
this.kill(RegExp('[[][[]\\s*(' + forbiddenNamespaceAliases.join('|') + ')\\s*:', 'i'),
/\]\]\s*/, '[', ']');
};
/**
@private
*/
Previewmaker.prototype.killHTML = function () {
// kill <ref ...>...</ref>
this.kill(/<ref\b[^/>]*?>/i, /<\/ref>/i);
// let's also delete entire lines starting with <. it's worth a try.
this.data=this.data.replace(RegExp('(^|\\n) *<.*', 'g'), '\n');
// and those pesky html tags, but not <nowiki> or <blockquote>
var splitted=this.data.parenSplit(/(<[\w\W]*?(?:>|$|(?=<)))/);
var len=splitted.length;
for (var i=1; i<len; i=i+2) {
switch (splitted[i]) {
case '<nowiki>':
case '</nowiki>':
case '<blockquote>':
case '</blockquote>':
break;
default:
splitted[i]='';
}
}
this.data=splitted.join('');
};
/**
@private
*/
Previewmaker.prototype.killChunks = function() { // heuristics alert
// chunks of italic text? you crazy, man?
var italicChunkRegex=new RegExp
("((^|\\n)\\s*:*\\s*''[^']([^']|'''|'[^']){20}(.|\\n[^\\n])*''[.!?\\s]*\\n)+", 'g');
// keep stuff separated, though, so stick in \n (fixes [[Union Jack]]?
this.data=this.data.replace(italicChunkRegex, '\n');
};
/**
@private
*/
Previewmaker.prototype.mopup = function () {
// we simply *can't* be doing with horizontal rules right now
this.data=this.data.replace(RegExp('^-{4,}','mg'),'');
// no indented lines
this.data=this.data.replace(RegExp('(^|\\n) *:[^\\n]*','g'), '');
// replace __TOC__, __NOTOC__ and whatever else there is
// this'll probably do
this.data=this.data.replace(RegExp('^__[A-Z_]*__ *$', 'gmi'),'');
};
/**
@private
*/
Previewmaker.prototype.firstBit = function () {
// dont't be givin' me no subsequent paragraphs, you hear me?
/// first we "normalize" section headings, removing whitespace after, adding before
var d=this.data;
if (getValueOf('popupPreviewCutHeadings')) {
this.data=this.data.replace(RegExp('\\s*(==+[^=]*==+)\\s*', 'g'), '\n\n$1 ');
/// then we want to get rid of paragraph breaks whose text ends badly
this.data=this.data.replace(RegExp('([:;]) *\\n{2,}', 'g'), '$1\n');
this.data=this.data.replace(RegExp('^[\\s\\n]*'), '');
var stuff=(RegExp('^([^\\n]|\\n[^\\n\\s])*')).exec(this.data);
if (stuff) { d = stuff[0]; }
if (!getValueOf('popupPreviewFirstParOnly')) { d = this.data; }
/// now put \n\n after sections so that bullets and numbered lists work
d=d.replace(RegExp('(==+[^=]*==+)\\s*', 'g'), '$1\n\n');
}
// Split sentences. Superfluous sentences are RIGHT OUT.
// note: exactly 1 set of parens here needed to make the slice work
d = d.parenSplit(RegExp('([!?.]+["'+"'"+']*\\s)','g'));
// leading space is bad, mmkay?
d[0]=d[0].replace(RegExp('^\\s*'), '');
var notSentenceEnds=RegExp('([^.][a-z][.] *[a-z]|etc|sic|Dr|Mr|Mrs|Ms|St|no|op|cit|\\[[^\\]]*|\\s[A-Zvclm])$', 'i');
d = this.fixSentenceEnds(d, notSentenceEnds);
this.fullLength=d.join('').length;
var n=this.maxSentences;
var dd=this.firstSentences(d,n);
do {
dd=this.firstSentences(d,n); --n;
} while ( dd.length > this.maxCharacters && n !== 0 );
this.data = dd;
};
/**
@private
*/
Previewmaker.prototype.fixSentenceEnds = function(strs, reg) {
// take an array of strings, strs
// join strs[i] to strs[i+1] & strs[i+2] if strs[i] matches regex reg
for (var i=0; i<strs.length-2; ++i) {
if (reg.test(strs[i])) {
var a=[];
for (var j=0; j<strs.length; ++j) {
if (j<i) a[j]=strs[j];
if (j==i) a[i]=strs[i]+strs[i+1]+strs[i+2];
if (j>i+2) a[j-2]=strs[j];
}
return this.fixSentenceEnds(a,reg);
}
}
return strs;
};
/**
@private
*/
Previewmaker.prototype.firstSentences = function(strs, howmany) {
var t=strs.slice(0, 2*howmany);
return t.join('');
};
/**
@private
*/
Previewmaker.prototype.killBadWhitespace = function() {
// also cleans up isolated '''', eg [[Suntory Sungoliath]]
this.data=this.data.replace(RegExp('^ *\'+ *$', 'gm'), '');
};
/**
Runs the various methods to generate the preview.
The preview is stored in the <code>html</html> field.
@private
*/
Previewmaker.prototype.makePreview = function() {
if (this.owner.article.namespaceId()!=pg.nsTemplateId &&
this.owner.article.namespaceId()!=pg.nsImageId ) {
this.killComments();
this.killDivs();
this.killGalleries();
this.killBoxTemplates();
if (getValueOf('popupPreviewKillTemplates')) {
this.killTemplates();
} else {
this.killMultilineTemplates();
}
this.killTables();
this.killImages();
this.killHTML();
this.killChunks();
this.mopup();
this.firstBit();
this.killBadWhitespace();
}
else
{
this.killHTML();
}
this.html=wiki2html(this.data, this.baseUrl); // needs livepreview
this.fixHTML();
this.stripLongTemplates();
};
/**
@private
*/
Previewmaker.prototype.esWiki2HtmlPart = function(data) {
var reLinks = /(?:\[\[([^|\]]*)(?:\|([^|\]]*))*]]([a-z]*))/gi; //match a wikilink
reLinks.lastIndex = 0; //reset regex
var match;
var result = "";
var postfixIndex = 0;
while ((match = reLinks.exec(data))) //match all wikilinks
{
//FIXME: the way that link is built here isn't perfect. It is clickable, but popups preview won't recognize it in some cases.
result += pg.escapeQuotesHTML(data.substring(postfixIndex, match.index)) +
'<a href="'+Insta.conf.paths.articles+pg.escapeQuotesHTML(match[1])+'">'+pg.escapeQuotesHTML((match[2]?match[2]:match[1])+match[3])+"</a>";
postfixIndex = reLinks.lastIndex;
}
//append the rest
result += pg.escapeQuotesHTML(data.substring(postfixIndex));
return result;
};
Previewmaker.prototype.editSummaryPreview=function() {
var reAes = /\/\* *(.*?) *\*\//g; //match the first section marker
reAes.lastIndex = 0; //reset regex
var match;
match = reAes.exec(this.data);
if (match)
{
//we have a section link. Split it, process it, combine it.
var prefix = this.data.substring(0,match.index-1);
var section = match[1];
var postfix = this.data.substring(reAes.lastIndex);
var start = "<span class='autocomment'>";
var end = "</span>";
if (prefix.length>0) start = this.esWiki2HtmlPart(prefix) + " " + start + "- ";
if (postfix.length>0) end = ": " + end + this.esWiki2HtmlPart(postfix);
var t=new Title().fromURL(this.baseUrl);
t.anchorFromUtf(section);
var sectionLink = Insta.conf.paths.articles + pg.escapeQuotesHTML(t.toString(true)) + '#' + pg.escapeQuotesHTML(t.anchor);
return start + '<a href="'+sectionLink+'">→</a> '+pg.escapeQuotesHTML(section) + end;
}
//else there's no section link, htmlify the whole thing.
return this.esWiki2HtmlPart(this.data);
};
//<NOLITE>
/** Test function for debugging preview problems one step at a time.
*/
/*eslint-disable */
function previewSteps(txt) {
try {
txt=txt || document.editform.wpTextbox1.value;
} catch (err) {
if (pg.cache.pages.length > 0) {
txt=pg.cache.pages[pg.cache.pages.length-1].data;
} else {
alert('provide text or use an edit page');
}
}
txt=txt.substring(0,10000);
var base=pg.wiki.articlebase + Title.fromURL(document.location.href).urlString();
var p=new Previewmaker(txt, base, pg.current.link.navpopup);
if (this.owner.article.namespaceId() != pg.nsTemplateId) {
p.killComments(); if (!confirm('done killComments(). Continue?\n---\n' + p.data)) { return; }
p.killDivs(); if (!confirm('done killDivs(). Continue?\n---\n' + p.data)) { return; }
p.killGalleries(); if (!confirm('done killGalleries(). Continue?\n---\n' + p.data)) { return; }
p.killBoxTemplates(); if (!confirm('done killBoxTemplates(). Continue?\n---\n' + p.data)) { return; }
if (getValueOf('popupPreviewKillTemplates')) {
p.killTemplates(); if (!confirm('done killTemplates(). Continue?\n---\n' + p.data)) { return; }
} else {
p.killMultilineTemplates(); if (!confirm('done killMultilineTemplates(). Continue?\n---\n' + p.data)) { return; }
}
p.killTables(); if (!confirm('done killTables(). Continue?\n---\n' + p.data)) { return; }
p.killImages(); if (!confirm('done killImages(). Continue?\n---\n' + p.data)) { return; }
p.killHTML(); if (!confirm('done killHTML(). Continue?\n---\n' + p.data)) { return; }
p.killChunks(); if (!confirm('done killChunks(). Continue?\n---\n' + p.data)) { return; }
p.mopup(); if (!confirm('done mopup(). Continue?\n---\n' + p.data)) { return; }
p.firstBit(); if (!confirm('done firstBit(). Continue?\n---\n' + p.data)) { return; }
p.killBadWhitespace(); if (!confirm('done killBadWhitespace(). Continue?\n---\n' + p.data)) { return; }
}
p.html=wiki2html(p.data, base); // needs livepreview
p.fixHTML(); if (!confirm('done fixHTML(). Continue?\n---\n' + p.html)) { return; }
p.stripLongTemplates(); if (!confirm('done stripLongTemplates(). Continue?\n---\n' + p.html)) { return; }
alert('finished preview - end result follows.\n---\n' + p.html);
}
/*eslint-enable */
//</NOLITE>
/**
Works around livepreview bugs.
@private
*/
Previewmaker.prototype.fixHTML = function() {
if(!this.html) return;
var ret = this.html;
// fix question marks in wiki links
// maybe this'll break some stuff :-(
ret=ret.replace(RegExp('(<a href="' + pg.wiki.articlePath + '/[^"]*)[?](.*?")', 'g'), '$1%3F$2');
ret=ret.replace(RegExp('(<a href=\'' + pg.wiki.articlePath + '/[^\']*)[?](.*?\')', 'g'), '$1%3F$2');
// FIXME fix up % too
this.html=ret;
};
/**
Generates the preview and displays it in the current popup.
Does nothing if the generated preview is invalid or consists of whitespace only.
Also activates wikilinks in the preview for subpopups if the popupSubpopups option is true.
*/
Previewmaker.prototype.showPreview = function () {
this.makePreview();
if (typeof this.html != typeof '') return;
if (RegExp('^\\s*$').test(this.html)) return;
setPopupHTML('<hr />', 'popupPrePreviewSep', this.owner.idNumber);
setPopupTipsAndHTML(this.html, 'popupPreview', this.owner.idNumber, { owner: this.owner });
var more = (this.fullLength > this.data.length) ? this.moreLink() : '';
setPopupHTML(more, 'popupPreviewMore', this.owner.idNumber);
};
/**
@private
*/
Previewmaker.prototype.moreLink=function() {
var a=document.createElement('a');
a.className='popupMoreLink';
a.innerHTML=popupString('more...');
var savedThis=this;
a.onclick=function() {
savedThis.maxCharacters+=2000;
savedThis.maxSentences+=20;
savedThis.setData();
savedThis.showPreview();
};
return a;
};
/**
@private
*/
Previewmaker.prototype.stripLongTemplates = function() {
// operates on the HTML!
this.html=this.html.replace(RegExp('^.{0,1000}[{][{][^}]*?(<(p|br)( /)?>\\s*){2,}([^{}]*?[}][}])?', 'gi'), '');
this.html=this.html.split('\n').join(' '); // workaround for <pre> templates
this.html=this.html.replace(RegExp('[{][{][^}]*<pre>[^}]*[}][}]','gi'), '');
};
/**
@private
*/
Previewmaker.prototype.killMultilineTemplates = function() {
this.kill('{{{', '}}}');
this.kill(RegExp('\\s*[{][{][^{}]*\\n'), '}}', '{{');
};
// ENDFILE: previewmaker.js
// STARTFILE: querypreview.js
function loadAPIPreview(queryType, article, navpop) {
var art=new Title(article).urlString();
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&';
var htmlGenerator=function(/*a, d*/){alert('invalid html generator');};
var usernameart = '';
switch (queryType) {
case 'history':
url += 'titles=' + art + '&prop=revisions&rvlimit=' +
getValueOf('popupHistoryPreviewLimit');
htmlGenerator=APIhistoryPreviewHTML;
break;
case 'category':
url += 'list=categorymembers&cmtitle=' + art;
htmlGenerator=APIcategoryPreviewHTML;
break;
case 'userinfo':
var username = new Title( article ).userName();
usernameart = encodeURIComponent( username );
if (pg.re.ipUser.test(username)) {
url += 'list=blocks&bkprop=range|restrictions&bkip=' + usernameart;
} else {
url += 'list=users|usercontribs&usprop=blockinfo|groups|editcount|registration|gender&ususers=' + usernameart + "&meta=globaluserinfo&guiprop=groups|unattached&guiuser="+ usernameart + "&uclimit=1&ucprop=timestamp&ucuser=" + usernameart;
}
htmlGenerator=APIuserInfoPreviewHTML;
break;
case 'contribs':
usernameart = encodeURIComponent( new Title( article ).userName() );
url += 'list=usercontribs&ucuser=' + usernameart +
'&uclimit=' + getValueOf('popupContribsPreviewLimit');
htmlGenerator=APIcontribsPreviewHTML;
break;
case 'imagepagepreview':
var trail='';
if (getValueOf('popupImageLinks')) { trail = '&list=imageusage&iutitle=' + art; }
url += 'titles=' + art + '&prop=revisions|imageinfo&rvprop=content' + trail;
htmlGenerator=APIimagepagePreviewHTML;
break;
case 'backlinks':
url += 'list=backlinks&bltitle=' + art;
htmlGenerator=APIbacklinksPreviewHTML;
break;
case 'revision':
if (article.oldid) {
url += 'revids=' + article.oldid;
} else {
url += 'titles=' + article.removeAnchor().urlString();
}
url += '&prop=revisions|pageprops|info|images|categories&rvprop=ids|timestamp|flags|comment|user|content&cllimit=max&imlimit=max';
htmlGenerator=APIrevisionPreviewHTML;
break;
}
pendingNavpopTask(navpop);
var callback=function(d){
log( "callback of API functions was hit" );
showAPIPreview(queryType, htmlGenerator(article,d,navpop), navpop.idNumber, navpop, d);
};
var go = function(){
getPageWithCaching(url, callback, navpop);
return true;
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'before', 'DOWNLOAD_'+queryType+'_QUERY_DATA'); }
}
function linkList(list) {
list.sort(function(x,y) { return (x==y ? 0 : (x<y ? -1 : 1)); });
var buf=[];
for (var i=0; i<list.length; ++i) {
buf.push(wikiLink({article: new Title(list[i]),
text: list[i].split(' ').join(' '),
action: 'view'}));
}
return buf.join(', ');
}
function getTimeOffset() {
var tz = mw.user.options.get('timecorrection');
if(tz) {
if( tz.indexOf('|') > -1 ) {
// New format
return parseInt(tz.split('|')[1],10);
} else if ( tz.indexOf(':') > -1 ) {
// Old format
return( parseInt(tz,10)*60 + parseInt(tz.split(':')[1],10) );
}
}
return 0;
}
/*
* Creates a HTML table that's shown in the history and user-contribs popups.
* @param {Object[]} h - a list of revisions, returned from the API
* @param {boolean} reallyContribs - true only if we're displaying user contributions
*/
function editPreviewTable(article, h, reallyContribs, timeOffset) {
var html=['<table>'];
var day=null;
var curart=article;
var page=null;
var makeFirstColumnLinks;
if(reallyContribs) {
// We're showing user contributions, so make (diff | hist) links
makeFirstColumnLinks = function(currentRevision) {
var result = '(';
result += '<a href="' + pg.wiki.titlebase +
new Title(currentRevision.title).urlString() + '&diff=prev' +
'&oldid=' + currentRevision.revid + '">' + popupString('diff') + '</a>';
result += ' | ';
result += '<a href="' + pg.wiki.titlebase +
new Title(currentRevision.title).urlString() + '&action=history">' +
popupString('hist') + '</a>';
result += ')';
return result;
};
} else {
// It's a regular history page, so make (cur | last) links
var firstRevid = h[0].revid;
makeFirstColumnLinks = function(currentRevision) {
var result = '(';
result += '<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&diff=' + firstRevid + '&oldid=' + currentRevision.revid + '">' + popupString('cur') + '</a>';
result += ' | ';
result += '<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&diff=prev&oldid=' + currentRevision.revid + '">' + popupString('last') + '</a>';
result += ')';
return result;
};
}
for (var i=0; i<h.length; ++i) {
if (reallyContribs) {
page = h[i].title;
curart = new Title(page);
}
var minor = h[i].minor ? '<b>m </b>' : '';
var editDate = adjustDate(getDateFromTimestamp(h[i].timestamp), timeOffset);
var thisDay = dayFormat(editDate);
var thisTime = timeFormat(editDate);
if (thisDay == day) {
thisDay = '';
} else {
day = thisDay;
}
if (thisDay) {
html.push( '<tr><td colspan=3><span class="popup_history_date">' +
thisDay+'</span></td></tr>' );
}
html.push('<tr class="popup_history_row_' + ( (i%2) ? 'odd' : 'even') + '">');
html.push('<td>' + makeFirstColumnLinks(h[i]) + '</td>');
html.push('<td>' +
'<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&oldid=' + h[i].revid + '">' + thisTime + '</a></td>');
var col3url='', col3txt='';
if (!reallyContribs) {
var user=h[i].user;
if( !h[i].userhidden ) {
if( pg.re.ipUser.test(user) ) {
col3url=pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId] + ':Contributions&target=' + new Title(user).urlString();
} else {
col3url=pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + new Title(user).urlString();
}
col3txt=pg.escapeQuotesHTML(user);
} else {
col3url=getValueOf('popupRevDelUrl');
col3txt=pg.escapeQuotesHTML( popupString('revdel'));
}
} else {
col3url=pg.wiki.titlebase + curart.urlString();
col3txt=pg.escapeQuotesHTML(page);
}
html.push('<td>' + (reallyContribs ? minor : '') +
'<a href="' + col3url + '">' + col3txt + '</a></td>');
var comment='';
var c=h[i].comment || h[i].content;
if (c) {
comment=new Previewmaker(c, new Title(curart).toUrl()).editSummaryPreview();
} else if ( h[i].commenthidden ) {
comment=popupString('revdel');
}
html.push('<td>' + (!reallyContribs ? minor : '') + comment + '</td>');
html.push('</tr>');
html=[html.join('')];
}
html.push('</table>');
return html.join('');
}
function getDateFromTimestamp(t) {
var s=t.split(/[^0-9]/);
switch(s.length) {
case 0: return null;
case 1: return new Date(s[0]);
case 2: return new Date(s[0], s[1]-1);
case 3: return new Date(s[0], s[1]-1, s[2]);
case 4: return new Date(s[0], s[1]-1, s[2], s[3]);
case 5: return new Date(s[0], s[1]-1, s[2], s[3], s[4]);
case 6: return new Date(s[0], s[1]-1, s[2], s[3], s[4], s[5]);
default: return new Date(s[0], s[1]-1, s[2], s[3], s[4], s[5], s[6]);
}
}
function adjustDate(d, offset) {
// offset is in minutes
var o=offset * 60 * 1000;
return new Date( +d + o);
}
function dayFormat(editDate, utc) {
if (utc) { return map(zeroFill, [editDate.getUTCFullYear(), editDate.getUTCMonth()+1, editDate.getUTCDate()]).join('-'); }
return map(zeroFill, [editDate.getFullYear(), editDate.getMonth()+1, editDate.getDate()]).join('-');
}
function timeFormat(editDate, utc) {
if (utc) { return map(zeroFill, [editDate.getUTCHours(), editDate.getUTCMinutes(), editDate.getUTCSeconds()]).join(':'); }
return map(zeroFill, [editDate.getHours(), editDate.getMinutes(), editDate.getSeconds()]).join(':');
}
function showAPIPreview(queryType, html, id, navpop, download) {
// DJ: done
var target='popupPreview';
completedNavpopTask(navpop);
switch (queryType) {
case 'imagelinks':
case 'category':
target='popupPostPreview'; break;
case 'userinfo':
target='popupUserData'; break;
case 'revision':
insertPreview(download);
return;
}
setPopupTipsAndHTML(html, target, id);
}
function APIrevisionPreviewHTML(article, download) {
try{
var jsObj=getJsObj(download.data);
var page=anyChild(jsObj.query.pages);
if( page.missing ) {
// TODO we need to fix this proper later on
download.owner = null;
return;
}
var content = (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
if( typeof content === 'string' )
{
download.data = content;
download.lastModified = new Date(page.revisions[0].timestamp);
}
} catch(someError) {
return 'Revision preview failed :(';
}
}
function APIbacklinksPreviewHTML(article, download/*, navpop*/ ) {
try {
var jsObj=getJsObj(download.data);
var list=jsObj.query.backlinks;
var html=[];
if (!list) { return popupString('No backlinks found'); }
for ( var i=0; i < list.length; i++ ) {
var t=new Title(list[i].title);
html.push('<a href="' + pg.wiki.titlebase + t.urlString() + '">' + t + '</a>');
}
html=html.join(', ');
if (jsObj['continue'] && jsObj['continue'].blcontinue) {
html += popupString(' and more');
}
return html;
} catch (someError) {
return 'backlinksPreviewHTML went wonky';
}
}
pg.fn.APIsharedImagePagePreviewHTML = function APIsharedImagePagePreviewHTML(obj) {
log( "APIsharedImagePagePreviewHTML" );
var popupid = obj.requestid;
if( obj.query && obj.query.pages )
{
var page=anyChild(obj.query.pages );
var content = (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
if( typeof content === 'string' )
{
/* Not entirely safe, but the best we can do */
var p=new Previewmaker(content, pg.current.link.navpopup.article, pg.current.link.navpopup);
p.makePreview();
setPopupHTML( p.html, "popupSecondPreview", popupid );
}
}
};
function APIimagepagePreviewHTML(article, download, navpop) {
try {
var jsObj=getJsObj(download.data);
var page=anyChild(jsObj.query.pages);
var content= (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
var ret='';
var alt='';
try{alt=navpop.parentAnchor.childNodes[0].alt;} catch(e){}
if (alt) {
ret = ret + '<hr /><b>' + popupString('Alt text:') + '</b> ' + pg.escapeQuotesHTML(alt);
}
if (typeof content === 'string') {
var p=prepPreviewmaker(content, article, navpop);
p.makePreview();
if (p.html) { ret += '<hr />' + p.html; }
if (getValueOf('popupSummaryData')) {
var info=getPageInfo(content, download);
log(info);
setPopupTrailer(info, navpop.idNumber);
}
}
if (page && page.imagerepository == "shared" ) {
var art=new Title(article);
var encart = encodeURIComponent( "File:" + art.stripNamespace() );
var shared_url = pg.wiki.apicommonsbase + '?format=json&formatversion=2' +
'&callback=pg.fn.APIsharedImagePagePreviewHTML' +
'&requestid=' + navpop.idNumber +
'&action=query&prop=revisions&rvprop=content&titles=' + encart;
ret = ret +'<hr />' + popupString( 'Image from Commons') +
': <a href="' + pg.wiki.commonsbase + '?title=' + encart + '">' +
popupString( 'Description page') + '</a>';
mw.loader.load( shared_url );
}
showAPIPreview('imagelinks', APIimagelinksPreviewHTML(article,download), navpop.idNumber, download);
return ret;
} catch (someError) {
return 'API imagepage preview failed :(';
}
}
function APIimagelinksPreviewHTML(article, download) {
try {
var jsobj=getJsObj(download.data);
var list=jsobj.query.imageusage;
if (list) {
var ret=[];
for (var i=0; i < list.length; i++) {
ret.push(list[i].title);
}
if (ret.length === 0) { return popupString('No image links found'); }
return '<h2>' + popupString('File links') + '</h2>' + linkList(ret);
} else {
return popupString('No image links found');
}
} catch(someError) {
return 'Image links preview generation failed :(';
}
}
function APIcategoryPreviewHTML(article, download) {
try{
var jsobj=getJsObj(download.data);
var list=jsobj.query.categorymembers;
var ret=[];
for (var p=0; p < list.length; p++) {
ret.push(list[p].title);
}
if (ret.length === 0) { return popupString('Empty category'); }
ret = '<h2>' + tprintf('Category members (%s shown)', [ret.length]) + '</h2>' +linkList(ret);
if (jsobj['continue'] && jsobj['continue'].cmcontinue) {
ret += popupString(' and more');
}
return ret;
} catch(someError) {
return 'Category preview failed :(';
}
}
function APIuserInfoPreviewHTML(article, download) {
var ret=[];
var queryobj = {};
try{
queryobj=getJsObj(download.data).query;
} catch(someError) { return 'Userinfo preview failed :('; }
var user=anyChild(queryobj.users);
if (user) {
var globaluserinfo=queryobj.globaluserinfo;
if (user.invalid === '') {
ret.push( popupString( 'Invalid user') );
} else if (user.missing === '') {
ret.push( popupString( 'Not a registered username') );
}
if( user.blockedby ) {
if( user.blockpartial ) {
ret.push('<b>' + popupString('Has blocks') + '</b>');
} else {
ret.push('<b>' + popupString('BLOCKED') + '</b>');
}
}
if( globaluserinfo && ( 'locked' in globaluserinfo || 'hidden' in globaluserinfo ) ) {
var lockedSulAccountIsAttachedToThis = true;
for( var i=0; globaluserinfo.unattached && i < globaluserinfo.unattached.length; i++) {
if ( globaluserinfo.unattached[i].wiki === mw.config.get('wgDBname') ) {
lockedSulAccountIsAttachedToThis=false;
break;
}
}
if (lockedSulAccountIsAttachedToThis) {
if ( 'locked' in globaluserinfo ) ret.push('<b><i>' + popupString('LOCKED') + '</i></b>');
if ( 'hidden' in globaluserinfo ) ret.push('<b><i>' + popupString('HIDDEN') + '</i></b>');
}
}
if( getValueOf('popupShowGender') && user.gender ) {
switch( user.gender ) {
case "male": ret.push( popupString( "\u2642" ) ); break;
case "female": ret.push( popupString( "\u2640" ) ); break;
}
}
if( user.groups ) {
for( var j=0; j < user.groups.length; j++) {
var currentGroup = user.groups[j];
if( ["*", "user", "autoconfirmed", "extendedconfirmed"].indexOf( currentGroup ) === -1 ) {
ret.push( pg.escapeQuotesHTML(user.groups[j]) );
}
}
}
if( globaluserinfo && globaluserinfo.groups ) {
for( var k=0; k < globaluserinfo.groups.length; k++) {
ret.push( '<i>'+pg.escapeQuotesHTML(globaluserinfo.groups[k])+'</i>' );
}
}
if( user.registration )
ret.push( pg.escapeQuotesHTML((user.editcount?user.editcount:'0') + popupString(' edits since: ') + (user.registration?dayFormat(getDateFromTimestamp(user.registration)):'')) );
}
if (queryobj.usercontribs && queryobj.usercontribs.length) {
ret.push( popupString('last edit on ') + dayFormat(getDateFromTimestamp(queryobj.usercontribs[0].timestamp)) );
}
if (queryobj.blocks) {
ret.push( popupString( 'IP user') ); //we only request list=blocks for IPs
for (var l=0; l<queryobj.blocks.length; l++) {
console.log(queryobj);
var rbstr = queryobj.blocks[l].rangestart === queryobj.blocks[l].rangeend ? 'BLOCK' : 'RANGEBLOCK';
rbstr = (!Array.isArray(queryobj.blocks[l].restrictions) ? 'Has ' + rbstr.toLowerCase() + 's' : rbstr + 'ED');
ret.push('<b>' + popupString(rbstr) + '</b>' );
}
}
ret = '<hr />' + ret.join( ', ' );
return ret;
}
function APIcontribsPreviewHTML(article, download, navpop) {
return APIhistoryPreviewHTML(article, download, navpop, true);
}
function APIhistoryPreviewHTML(article, download, navpop, reallyContribs) {
try {
var jsobj=getJsObj(download.data);
var edits = [];
if( reallyContribs ) {
edits=jsobj.query.usercontribs;
} else {
edits=anyChild(jsobj.query.pages).revisions;
}
var ret=editPreviewTable(article, edits, reallyContribs, getTimeOffset());
return ret;
} catch (someError) {
return 'History preview failed :-(';
}
}
//</NOLITE>
// ENDFILE: querypreview.js
// STARTFILE: debug.js
////////////////////////////////////////////////////////////////////
// Debugging functions
////////////////////////////////////////////////////////////////////
function setupDebugging() {
//<NOLITE>
if (window.popupDebug) { // popupDebug is set from .version
window.log=function(x) { //if(gMsg!='')gMsg += '\n'; gMsg+=time() + ' ' + x; };
window.console.log(x);
};
window.errlog=function(x) {
window.console.error(x);
};
log('Initializing logger');
} else {
//</NOLITE>
window.log = function() {};
window.errlog = function() {};
//<NOLITE>
}
//</NOLITE>
}
// ENDFILE: debug.js
// STARTFILE: images.js
// load image of type Title.
function loadImage(image, navpop) {
if (typeof image.stripNamespace != 'function') { alert('loadImages bad'); }
// API call to retrieve image info.
if ( !getValueOf('popupImages') ) return;
if ( !isValidImageName(image) ) return false;
var art=image.urlString();
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query';
url += '&prop=imageinfo&iiprop=url|mime&iiurlwidth=' + getValueOf('popupImageSizeLarge');
url += '&titles=' + art;
pendingNavpopTask(navpop);
var callback=function(d){
popupsInsertImage(navpop.idNumber, navpop, d);
};
var go = function(){
getPageWithCaching(url, callback, navpop);
return true;
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'after', 'DOWNLOAD_IMAGE_QUERY_DATA'); }
}
function popupsInsertImage(id, navpop, download) {
log( "popupsInsertImage");
var imageinfo;
try {
var jsObj=getJsObj(download.data);
var imagepage=anyChild(jsObj.query.pages);
if (typeof imagepage.imageinfo === 'undefined') return;
imageinfo = imagepage.imageinfo[0];
} catch (someError) {
log( "popupsInsertImage failed :(" );
return;
}
var popupImage = document.getElementById("popupImg"+id);
if (!popupImage) {
log( "could not find insertion point for image");
return;
}
popupImage.width=getValueOf('popupImageSize');
popupImage.style.display='inline';
// Set the source for the image.
if( imageinfo.thumburl )
popupImage.src=imageinfo.thumburl;
else if( imageinfo.mime.indexOf("image") === 0 ){
popupImage.src=imageinfo.url;
log( "a thumb could not be found, using original image" );
} else log( "fullsize imagethumb, but not sure if it's an image");
var a=document.getElementById("popupImageLink"+id);
if (a === null) { return null; }
// Determine the action of the surrouding imagelink.
switch (getValueOf('popupThumbAction')) {
case 'imagepage':
if (pg.current.article.namespaceId()!=pg.nsImageId) {
a.href=imageinfo.descriptionurl;
// FIXME: unreliable pg.idNumber
popTipsSoonFn('popupImage' + id)();
break;
}
/* falls through */
case 'sizetoggle':
a.onclick=toggleSize;
a.title=popupString('Toggle image size');
return;
case 'linkfull':
a.href = imageinfo.url;
a.title=popupString('Open full-size image');
return;
}
}
// Toggles the image between inline small and navpop fullwidth.
// It's the same image, no actual sizechange occurs, only display width.
function toggleSize() {
var imgContainer=this;
if (!imgContainer) {
alert('imgContainer is null :/');
return;
}
var img=imgContainer.firstChild;
if (!img) {
alert('img is null :/');
return;
}
if (!img.style.width || img.style.width==='') {
img.style.width='100%';
} else {
img.style.width='';
}
}
// Returns one title of an image from wikiText.
function getValidImageFromWikiText(wikiText) {
// nb in pg.re.image we're interested in the second bracketed expression
// this may change if the regex changes :-(
//var match=pg.re.image.exec(wikiText);
var matched=null;
var match;
// strip html comments, used by evil bots :-(
var t = removeMatchesUnless(wikiText, RegExp('(<!--[\\s\\S]*?-->)'), 1,
RegExp('^<!--[^[]*popup', 'i'));
while ( ( match = pg.re.image.exec(t) ) ) {
// now find a sane image name - exclude templates by seeking {
var m = match[2] || match[6];
if ( isValidImageName(m) ) {
matched=m;
break;
}
}
pg.re.image.lastIndex=0;
if (!matched) { return null; }
return mw.config.get('wgFormattedNamespaces')[pg.nsImageId]+':'+upcaseFirst(matched);
}
function removeMatchesUnless(str, re1, parencount, re2) {
var split=str.parenSplit(re1);
var c=parencount + 1;
for (var i=0; i<split.length; ++i) {
if ( i%c === 0 || re2.test(split[i]) ) { continue; }
split[i]='';
}
return split.join('');
}
//</NOLITE>
// ENDFILE: images.js
// STARTFILE: namespaces.js
// Set up namespaces and other non-strings.js localization
// (currently that means redirs too)
function setNamespaces() {
pg.nsSpecialId = -1;
pg.nsMainspaceId = 0;
pg.nsImageId = 6;
pg.nsUserId = 2;
pg.nsUsertalkId = 3;
pg.nsCategoryId = 14;
pg.nsTemplateId = 10;
}
function setRedirs() {
var r='redirect';
var R='REDIRECT';
var redirLists={
//<NOLITE>
'ar': [ R, 'تحويل' ],
'be': [ r, 'перанакіраваньне' ],
'bg': [ r, 'пренасочване', 'виж' ],
'bs': [ r, 'Preusmjeri', 'preusmjeri', 'PREUSMJERI' ],
'cs': [ R, 'PŘESMĚRUJ' ],
'cy': [ r, 'ail-cyfeirio' ],
'de': [ R, 'WEITERLEITUNG' ],
'el': [ R, 'ΑΝΑΚΑΤΕΥΘΥΝΣΗ'],
'eo': [ R, 'ALIDIREKTU', 'ALIDIREKTI' ],
'es': [ R, 'REDIRECCIÓN' ],
'et': [ r, 'suuna' ],
'ga': [ r, 'athsheoladh' ],
'gl': [ r, 'REDIRECCIÓN', 'REDIRECIONAMENTO'],
'he': [ R, 'הפניה' ],
'hu': [ R, 'ÁTIRÁNYÍTÁS' ],
'is': [ r, 'tilvísun', 'TILVÍSUN' ],
'it': [ R, 'RINVIA', 'Rinvia'],
'ja': [ R, '転送' ],
'mk': [ r, 'пренасочување', 'види' ],
'nds': [ r, 'wiederleiden' ],
'nl': [ R, 'DOORVERWIJZING' ],
'nn': [ r, 'omdiriger' ],
'pl': [ R, 'PATRZ', 'PRZEKIERUJ', 'TAM' ],
'pt': [ R, 'redir' ],
'ru': [ R, 'ПЕРЕНАПРАВЛЕНИЕ', 'ПЕРЕНАПР' ],
'sk': [ r, 'presmeruj' ],
'sr': [ r, 'Преусмери', 'преусмери', 'ПРЕУСМЕРИ', 'Preusmeri', 'preusmeri', 'PREUSMERI' ],
'tt': [ R, 'yünältü', 'перенаправление', 'перенапр' ],
'uk': [ R, 'ПЕРЕНАПРАВЛЕННЯ', 'ПЕРЕНАПР' ],
'vi': [ r, 'đổi' ],
'zh': [ R, '重定向'] // no comma
//</NOLITE>
};
var redirList=redirLists[ pg.wiki.lang ] || [r, R];
// Mediawiki is very tolerant about what comes after the #redirect at the start
pg.re.redirect=RegExp('^\\s*[#](' + redirList.join('|') + ').*?\\[{2}([^\\|\\]]*)(|[^\\]]*)?\\]{2}\\s*(.*)', 'i');
}
function setInterwiki() {
if (pg.wiki.wikimedia) {
// From https://meta.wikimedia.org/wiki/List_of_Wikipedias
pg.wiki.interwiki='aa|ab|ace|af|ak|als|am|an|ang|ar|arc|arz|as|ast|av|ay|az|ba|bar|bat-smg|bcl|be|be-x-old|bg|bh|bi|bjn|bm|bn|bo|bpy|br|bs|bug|bxr|ca|cbk-zam|cdo|ce|ceb|ch|cho|chr|chy|ckb|co|cr|crh|cs|csb|cu|cv|cy|da|de|diq|dsb|dv|dz|ee|el|eml|en|eo|es|et|eu|ext|fa|ff|fi|fiu-vro|fj|fo|fr|frp|frr|fur|fy|ga|gag|gan|gd|gl|glk|gn|got|gu|gv|ha|hak|haw|he|hi|hif|ho|hr|hsb|ht|hu|hy|hz|ia|id|ie|ig|ii|ik|ilo|io|is|it|iu|ja|jbo|jv|ka|kaa|kab|kbd|kg|ki|kj|kk|kl|km|kn|ko|koi|kr|krc|ks|ksh|ku|kv|kw|ky|la|lad|lb|lbe|lg|li|lij|lmo|ln|lo|lt|ltg|lv|map-bms|mdf|mg|mh|mhr|mi|mk|ml|mn|mo|mr|mrj|ms|mt|mus|mwl|my|myv|mzn|na|nah|nap|nds|nds-nl|ne|new|ng|nl|nn|no|nov|nrm|nv|ny|oc|om|or|os|pa|pag|pam|pap|pcd|pdc|pfl|pi|pih|pl|pms|pnb|pnt|ps|pt|qu|rm|rmy|rn|ro|roa-rup|roa-tara|ru|rue|rw|sa|sah|sc|scn|sco|sd|se|sg|sh|si|simple|sk|sl|sm|sn|so|sq|sr|srn|ss|st|stq|su|sv|sw|szl|ta|te|tet|tg|th|ti|tk|tl|tn|to|tpi|tr|ts|tt|tum|tw|ty|udm|ug|uk|ur|uz|ve|vec|vi|vls|vo|wa|war|wo|wuu|xal|xh|yi|yo|za|zea|zh|zh-classical|zh-min-nan|zh-yue|zu';
pg.re.interwiki=RegExp('^'+pg.wiki.interwiki+':');
} else {
pg.wiki.interwiki=null;
pg.re.interwiki=RegExp('^$');
}
}
// return a regexp pattern matching all variants to write the given namespace
function nsRe(namespaceId) {
var imageNamespaceVariants = [];
jQuery.each(mw.config.get('wgNamespaceIds'), function(_localizedNamespaceLc, _namespaceId) {
if (_namespaceId!=namespaceId) return;
_localizedNamespaceLc = upcaseFirst(_localizedNamespaceLc);
imageNamespaceVariants.push(mw.util.escapeRegExp(_localizedNamespaceLc).split(' ').join('[ _]'));
imageNamespaceVariants.push(mw.util.escapeRegExp(encodeURI(_localizedNamespaceLc)));
});
return '(?:' + imageNamespaceVariants.join('|') + ')';
}
function nsReImage() {
return nsRe(pg.nsImageId);
}
// ENDFILE: namespaces.js
// STARTFILE: selpop.js
//<NOLITE>
function getEditboxSelection() {
// see http://www.webgurusforum.com/8/12/0
var editbox;
try {
editbox=document.editform.wpTextbox1;
} catch (dang) { return; }
// IE, Opera
if (document.selection) { return document.selection.createRange().text; }
// Mozilla
var selStart = editbox.selectionStart;
var selEnd = editbox.selectionEnd;
return (editbox.value).substring(selStart, selEnd);
}
function doSelectionPopup() {
// popup if the selection looks like [[foo|anything afterwards at all
// or [[foo|bar]]text without ']]'
// or [[foo|bar]]
var sel=getEditboxSelection();
var open=sel.indexOf('[[');
var pipe=sel.indexOf('|');
var close=sel.indexOf(']]');
if (open == -1 || ( pipe == -1 && close == -1) ) { return; }
if (pipe != -1 && open > pipe || close != -1 && open > close) { return; }
if (getValueOf('popupOnEditSelection')=='boxpreview') {
return doSeparateSelectionPopup(sel);
}
var article=new Title(sel.substring(open+2, (pipe < 0) ? close : pipe)).urlString();
if (close > 0 && sel.substring(close+2).indexOf('[[') >= 0) {
return;
}
var a=document.createElement('a');
a.href=pg.wiki.titlebase + article;
mouseOverWikiLink2(a);
if (a.navpopup) {
a.navpopup.addHook(function(){runStopPopupTimer(a.navpopup);}, 'unhide', 'after');
}
}
function doSeparateSelectionPopup(str) {
var div=document.getElementById('selectionPreview');
if (!div) {
div = document.createElement('div');
div.id='selectionPreview';
try {
var box=document.editform.wpTextbox1;
box.parentNode.insertBefore(div, box);
} catch (error) {
return;
}
}
div.innerHTML=wiki2html(str);
div.ranSetupTooltipsAlready = false;
popTipsSoonFn('selectionPreview')();
}
//</NOLITE>
// ENDFILE: selpop.js
// STARTFILE: navpopup.js
/**
@fileoverview Defines two classes: {@link Navpopup} and {@link Mousetracker}.
<code>Navpopup</code> describes popups: when they appear, where, what
they look like and so on.
<code>Mousetracker</code> "captures" the mouse using
<code>document.onmousemove</code>.
*/
/**
Creates a new Mousetracker.
@constructor
@class The Mousetracker class. This monitors mouse movements and manages associated hooks.
*/
function Mousetracker() {
/**
Interval to regularly run the hooks anyway, in milliseconds.
@type Integer
*/
this.loopDelay=400;
/**
Timer for the loop.
@type Timer
*/
this.timer=null;
/**
Flag - are we switched on?
@type Boolean
*/
this.active=false;
/**
Flag - are we probably inaccurate, i.e. not reflecting the actual mouse position?
*/
this.dirty=true;
/**
Array of hook functions.
@private
@type Array
*/
this.hooks=[];
}
/**
Adds a hook, to be called when we get events.
@param {Function} f A function which is called as
<code>f(x,y)</code>. It should return <code>true</code> when it
wants to be removed, and <code>false</code> otherwise.
*/
Mousetracker.prototype.addHook = function (f) {
this.hooks.push(f);
};
/**
Runs hooks, passing them the x
and y coords of the mouse. Hook functions that return true are
passed to {@link Mousetracker#removeHooks} for removal.
@private
*/
Mousetracker.prototype.runHooks = function () {
if (!this.hooks || !this.hooks.length) { return; }
//log('Mousetracker.runHooks; we got some hooks to run');
var remove=false;
var removeObj={};
// this method gets called a LOT -
// pre-cache some variables
var x=this.x, y=this.y, len = this.hooks.length;
for (var i=0; i<len; ++i) {
//~ run the hook function, and remove it if it returns true
if (this.hooks[i](x, y)===true) {
remove=true;
removeObj[i]=true;
}
}
if (remove) { this.removeHooks(removeObj); }
};
/**
Removes hooks.
@private
@param {Object} removeObj An object whose keys are the index
numbers of functions for removal, with values that evaluate to true
*/
Mousetracker.prototype.removeHooks = function(removeObj) {
var newHooks=[];
var len = this.hooks.length;
for (var i=0; i<len; ++i) {
if (! removeObj[i]) { newHooks.push(this.hooks[i]); }
}
this.hooks=newHooks;
};
/**
Event handler for mouse wiggles.
We simply grab the event, set x and y and run the hooks.
This makes the cpu all hot and bothered :-(
@private
@param {Event} e Mousemove event
*/
Mousetracker.prototype.track=function (e) {
//~ Apparently this is needed in IE.
e = e || window.event;
var x, y;
if (e) {
if (e.pageX) { x=e.pageX; y=e.pageY; }
else if (typeof e.clientX!='undefined') {
var left, top, docElt = document.documentElement;
if (docElt) { left=docElt.scrollLeft; }
left = left || document.body.scrollLeft || document.scrollLeft || 0;
if (docElt) { top=docElt.scrollTop; }
top = top || document.body.scrollTop || document.scrollTop || 0;
x=e.clientX + left;
y=e.clientY + top;
} else { return; }
this.setPosition(x,y);
}
};
/**
Sets the x and y coordinates stored and takes appropriate action,
running hooks as appropriate.
@param {Integer} x, y Screen coordinates to set
*/
Mousetracker.prototype.setPosition=function(x,y) {
this.x = x;
this.y = y;
if (this.dirty || this.hooks.length === 0) { this.dirty=false; return; }
if (typeof this.lastHook_x != 'number') { this.lastHook_x = -100; this.lastHook_y=-100; }
var diff = (this.lastHook_x - x)*(this.lastHook_y - y);
diff = (diff >= 0) ? diff : -diff;
if ( diff > 1 ) {
this.lastHook_x=x;
this.lastHook_y=y;
if (this.dirty) { this.dirty = false; }
else { this.runHooks(); }
}
};
/**
Sets things in motion, unless they are already that is, registering an event handler on <code>document.onmousemove</code>.
A half-hearted attempt is made to preserve the old event handler if there is one.
*/
Mousetracker.prototype.enable = function () {
if (this.active) { return; }
this.active=true;
//~ Save the current handler for mousemove events. This isn't too
//~ robust, of course.
this.savedHandler=document.onmousemove;
//~ Gotta save @tt{this} again for the closure, and use apply for
//~ the member function.
var savedThis=this;
document.onmousemove=function (e) {savedThis.track.apply(savedThis, [e]);};
if (this.loopDelay) { this.timer = setInterval(function() { //log('loop delay in mousetracker is working');
savedThis.runHooks();}, this.loopDelay); }
};
/**
Disables the tracker, removing the event handler.
*/
Mousetracker.prototype.disable = function () {
if (!this.active) { return; }
if (typeof this.savedHandler === 'function') {
document.onmousemove=this.savedHandler;
} else { delete document.onmousemove; }
if (this.timer) { clearInterval(this.timer); }
this.active=false;
};
/**
Creates a new Navpopup.
Gets a UID for the popup and
@param init Contructor object. If <code>init.draggable</code> is true or absent, the popup becomes draggable.
@constructor
@class The Navpopup class. This generates popup hints, and does some management of them.
*/
function Navpopup(/*init*/) {
//alert('new Navpopup(init)');
/** UID for each Navpopup instance.
Read-only.
@type integer
*/
this.uid=Navpopup.uid++;
/**
Read-only flag for current visibility of the popup.
@type boolean
@private
*/
this.visible=false;
/** Flag to be set when we want to cancel a previous request to
show the popup in a little while.
@private
@type boolean
*/
this.noshow=false;
/** Categorised list of hooks.
@see #runHooks
@see #addHook
@private
@type Object
*/
this.hooks={
'create': [],
'unhide': [],
'hide': []
};
/** list of unique IDs of hook functions, to avoid duplicates
@private
*/
this.hookIds={};
/** List of downloads associated with the popup.
@private
@type Array
*/
this.downloads=[];
/** Number of uncompleted downloads.
@type integer
*/
this.pending=null;
/** Tolerance in pixels when detecting whether the mouse has left the popup.
@type integer
*/
this.fuzz=5;
/** Flag to toggle running {@link #limitHorizontalPosition} to regulate the popup's position.
@type boolean
*/
this.constrained=true;
/** The popup width in pixels.
@private
@type integer
*/
this.width=0;
/** The popup width in pixels.
@private
@type integer
*/
this.height=0;
/** The main content DIV element.
@type HTMLDivElement
*/
this.mainDiv=null;
this.createMainDiv();
// if (!init || typeof init.popups_draggable=='undefined' || init.popups_draggable) {
// this.makeDraggable(true);
// }
}
/**
A UID for each Navpopup. This constructor property is just a counter.
@type integer
@private
*/
Navpopup.uid=0;
/**
Retrieves the {@link #visible} attribute, indicating whether the popup is currently visible.
@type boolean
*/
Navpopup.prototype.isVisible=function() {
return this.visible;
};
/**
Repositions popup using CSS style.
@private
@param {integer} x x-coordinate (px)
@param {integer} y y-coordinate (px)
@param {boolean} noLimitHor Don't call {@link #limitHorizontalPosition}
*/
Navpopup.prototype.reposition= function (x,y, noLimitHor) {
log ('reposition('+x+','+y+','+noLimitHor+')');
if (typeof x != 'undefined' && x !== null) { this.left=x; }
if (typeof y != 'undefined' && y !== null) { this.top=y; }
if (typeof this.left != 'undefined' && typeof this.top != 'undefined') {
this.mainDiv.style.left=this.left + 'px';
this.mainDiv.style.top=this.top + 'px';
}
if (!noLimitHor) { this.limitHorizontalPosition(); }
//console.log('navpop'+this.uid+' - (left,top)=(' + this.left + ',' + this.top + '), css=('
//+ this.mainDiv.style.left + ',' + this.mainDiv.style.top + ')');
};
/**
Prevents popups from being in silly locations. Hopefully.
Should not be run if {@link #constrained} is true.
@private
*/
Navpopup.prototype.limitHorizontalPosition=function() {
if (!this.constrained || this.tooWide) { return; }
this.updateDimensions();
var x=this.left;
var w=this.width;
var cWidth=document.body.clientWidth;
// log('limitHorizontalPosition: x='+x+
// ', this.left=' + this.left +
// ', this.width=' + this.width +
// ', cWidth=' + cWidth);
if ( (x+w) >= cWidth ||
( x > 0 &&
this.maxWidth &&
this.width < this.maxWidth &&
this.height > this.width &&
x > cWidth - this.maxWidth ) ) {
// This is a very nasty hack. There has to be a better way!
// We find the "natural" width of the div by positioning it at the far left
// then reset it so that it should be flush right (well, nearly)
this.mainDiv.style.left='-10000px';
this.mainDiv.style.width = this.maxWidth + 'px';
var naturalWidth=parseInt(this.mainDiv.offsetWidth, 10);
var newLeft=cWidth - naturalWidth - 1;
if (newLeft < 0) { newLeft = 0; this.tooWide=true; } // still unstable for really wide popups?
log ('limitHorizontalPosition: moving to ('+newLeft + ','+ this.top+');' + ' naturalWidth=' + naturalWidth + ', clientWidth=' + cWidth);
this.reposition(newLeft, null, true);
}
};
/**
Counter indicating the z-order of the "highest" popup.
We start the z-index at 1000 so that popups are above everything
else on the screen.
@private
@type integer
*/
Navpopup.highest=1000;
/**
Brings popup to the top of the z-order.
We increment the {@link #highest} property of the contructor here.
@private
*/
Navpopup.prototype.raise = function () {
this.mainDiv.style.zIndex=Navpopup.highest + 1;
++Navpopup.highest;
};
/**
Shows the popup provided {@link #noshow} is not true.
Updates the position, brings the popup to the top of the z-order and unhides it.
*/
Navpopup.prototype.show = function () {
//document.title+='s';
if (this.noshow) { return; }
//document.title+='t';
this.reposition();
this.raise();
this.unhide();
};
/**
Checks to see if the mouse pointer has
stabilised (checking every <code>time</code>/2 milliseconds) and runs the
{@link #show} method if it has.
@param {integer} time The minimum time (ms) before the popup may be shown.
*/
Navpopup.prototype.showSoonIfStable = function (time) {
log ('showSoonIfStable, time='+time);
if (this.visible) { return; }
this.noshow = false;
//~ initialize these variables so that we never run @tt{show} after
//~ just half the time
this.stable_x = -10000; this.stable_y = -10000;
var stableShow = function() {
log('stableShow called');
var new_x = Navpopup.tracker.x, new_y = Navpopup.tracker.y;
var dx = savedThis.stable_x - new_x, dy = savedThis.stable_y - new_y;
var fuzz2 = 0; // savedThis.fuzz * savedThis.fuzz;
//document.title += '[' + [savedThis.stable_x,new_x, savedThis.stable_y,new_y, dx, dy, fuzz2].join(',') + '] ';
if ( dx * dx <= fuzz2 && dy * dy <= fuzz2 ) {
log ('mouse is stable');
clearInterval(savedThis.showSoonStableTimer);
savedThis.reposition.apply(savedThis, [new_x + 2, new_y + 2]);
savedThis.show.apply(savedThis, []);
savedThis.limitHorizontalPosition.apply(savedThis, []);
return;
}
savedThis.stable_x = new_x; savedThis.stable_y = new_y;
};
var savedThis = this;
this.showSoonStableTimer = setInterval(stableShow, time/2);
};
/**
Sets the {@link #noshow} flag and hides the popup. This should be called
when the mouse leaves the link before
(or after) it's actually been displayed.
*/
Navpopup.prototype.banish = function () {
log ('banish called');
// hide and prevent showing with showSoon in the future
this.noshow=true;
if (this.showSoonStableTimer) {
log('clearing showSoonStableTimer');
clearInterval(this.showSoonStableTimer);
}
this.hide();
};
/**
Runs hooks added with {@link #addHook}.
@private
@param {String} key Key name of the {@link #hooks} array - one of 'create', 'unhide', 'hide'
@param {String} when Controls exactly when the hook is run: either 'before' or 'after'
*/
Navpopup.prototype.runHooks = function (key, when) {
if (!this.hooks[key]) { return; }
var keyHooks=this.hooks[key];
var len=keyHooks.length;
for (var i=0; i< len; ++i) {
if (keyHooks[i] && keyHooks[i].when == when) {
if (keyHooks[i].hook.apply(this, [])) {
// remove the hook
if (keyHooks[i].hookId) {
delete this.hookIds[keyHooks[i].hookId];
}
keyHooks[i]=null;
}
}
}
};
/**
Adds a hook to the popup. Hook functions are run with <code>this</code> set to refer to the Navpopup instance, and no arguments.
@param {Function} hook The hook function. Functions that return true are deleted.
@param {String} key Key name of the {@link #hooks} array - one of 'create', 'unhide', 'hide'
@param {String} when Controls exactly when the hook is run: either 'before' or 'after'
@param {String} uid A truthy string identifying the hook function; if it matches another hook in this position, it won't be added again.
*/
Navpopup.prototype.addHook = function ( hook, key, when, uid ) {
when = when || 'after';
if (!this.hooks[key]) { return; }
// if uid is specified, don't add duplicates
var hookId=null;
if (uid) {
hookId=[key,when,uid].join('|');
if (this.hookIds[hookId]) {
return;
}
this.hookIds[hookId]=true;
}
this.hooks[key].push( {hook: hook, when: when, hookId: hookId} );
};
/**
Creates the main DIV element, which contains all the actual popup content.
Runs hooks with key 'create'.
@private
*/
Navpopup.prototype.createMainDiv = function () {
if (this.mainDiv) { return; }
this.runHooks('create', 'before');
var mainDiv=document.createElement('div');
var savedThis=this;
mainDiv.onclick=function(e) {savedThis.onclickHandler(e);};
mainDiv.className=(this.className) ? this.className : 'navpopup_maindiv';
mainDiv.id=mainDiv.className + this.uid;
mainDiv.style.position='absolute';
mainDiv.style.minWidth = '350px';
mainDiv.style.display='none';
mainDiv.className='navpopup';
// easy access to javascript object through DOM functions
mainDiv.navpopup=this;
this.mainDiv=mainDiv;
document.body.appendChild(mainDiv);
this.runHooks('create', 'after');
};
/**
Calls the {@link #raise} method.
@private
*/
Navpopup.prototype.onclickHandler=function(/*e*/) {
this.raise();
};
/**
Makes the popup draggable, using a {@link Drag} object.
@private
*/
Navpopup.prototype.makeDraggable=function(handleName) {
if (!this.mainDiv) { this.createMainDiv(); }
var drag=new Drag();
if (!handleName) {
drag.startCondition=function(e) {
try { if (!e.shiftKey) { return false; } } catch (err) { return false; }
return true;
};
}
var dragHandle;
if (handleName) dragHandle = document.getElementById(handleName);
if (!dragHandle) dragHandle = this.mainDiv;
var np=this;
drag.endHook=function(x,y) {
Navpopup.tracker.dirty=true;
np.reposition(x,y);
};
drag.init(dragHandle,this.mainDiv);
};
/** Hides the popup using CSS. Runs hooks with key 'hide'.
Sets {@link #visible} appropriately. {@link #banish} should be called externally instead of this method.
@private
*/
Navpopup.prototype.hide = function () {
this.runHooks('hide', 'before');
this.abortDownloads();
if (typeof this.visible != 'undefined' && this.visible) {
this.mainDiv.style.display='none';
this.visible=false;
}
this.runHooks('hide', 'after');
};
/** Shows the popup using CSS. Runs hooks with key 'unhide'.
Sets {@link #visible} appropriately. {@link #show} should be called externally instead of this method.
@private
*/
Navpopup.prototype.unhide = function () {
this.runHooks('unhide', 'before');
if (typeof this.visible != 'undefined' && !this.visible) {
this.mainDiv.style.display='inline';
this.visible=true;
}
this.runHooks('unhide', 'after');
};
/**
Sets the <code>innerHTML</code> attribute of the main div containing the popup content.
@param {String} html The HTML to set.
*/
Navpopup.prototype.setInnerHTML = function (html) {
this.mainDiv.innerHTML = html;
};
/**
Updates the {@link #width} and {@link #height} attributes with the CSS properties.
@private
*/
Navpopup.prototype.updateDimensions = function () {
this.width=parseInt(this.mainDiv.offsetWidth, 10);
this.height=parseInt(this.mainDiv.offsetHeight, 10);
};
/**
Checks if the point (x,y) is within {@link #fuzz} of the
{@link #mainDiv}.
@param {integer} x x-coordinate (px)
@param {integer} y y-coordinate (px)
@type boolean
*/
Navpopup.prototype.isWithin = function(x,y) {
//~ If we're not even visible, no point should be considered as
//~ being within the popup.
if (!this.visible) { return false; }
this.updateDimensions();
var fuzz=this.fuzz || 0;
//~ Use a simple box metric here.
return (x+fuzz >= this.left && x-fuzz <= this.left + this.width &&
y+fuzz >= this.top && y-fuzz <= this.top + this.height);
};
/**
Adds a download to {@link #downloads}.
@param {Downloader} download
*/
Navpopup.prototype.addDownload=function(download) {
if (!download) { return; }
this.downloads.push(download);
};
/**
Aborts the downloads listed in {@link #downloads}.
@see Downloader#abort
*/
Navpopup.prototype.abortDownloads=function() {
for(var i=0; i<this.downloads.length; ++i) {
var d=this.downloads[i];
if (d && d.abort) { d.abort(); }
}
this.downloads=[];
};
/**
A {@link Mousetracker} instance which is a property of the constructor (pseudo-global).
*/
Navpopup.tracker=new Mousetracker();
// ENDFILE: navpopup.js
// STARTFILE: diff.js
//<NOLITE>
/*
* Javascript Diff Algorithm
* By John Resig (http://ejohn.org/) and [[:en:User:Lupin]]
*
* More Info:
* http://ejohn.org/projects/javascript-diff-algorithm/
*/
function delFmt(x) {
if (!x.length) { return ''; }
return "<del class='popupDiff'>" + x.join('') +"</del>";
}
function insFmt(x) {
if (!x.length) { return ''; }
return "<ins class='popupDiff'>" + x.join('') +"</ins>";
}
function countCrossings(a, b, i, eject) {
// count the crossings on the edge starting at b[i]
if (!b[i].row && b[i].row !== 0) { return -1; }
var count=0;
for (var j=0; j<a.length; ++j) {
if (!a[j].row && a[j].row !== 0) { continue; }
if ( (j-b[i].row)*(i-a[j].row) > 0) {
if(eject) { return true; }
count++;
}
}
return count;
}
function shortenDiffString(str, context) {
var re=RegExp('(<del[\\s\\S]*?</del>|<ins[\\s\\S]*?</ins>)');
var splitted=str.parenSplit(re);
var ret=[''];
for (var i=0; i<splitted.length; i+=2) {
if (splitted[i].length < 2*context) {
ret[ret.length-1] += splitted[i];
if (i+1<splitted.length) { ret[ret.length-1] += splitted[i+1]; }
continue;
}
else {
if (i > 0) { ret[ret.length-1] += splitted[i].substring(0,context); }
if (i+1 < splitted.length) {
ret.push(splitted[i].substring(splitted[i].length-context) +
splitted[i+1]);
}
}
}
while (ret.length > 0 && !ret[0]) { ret = ret.slice(1); }
return ret;
}
function diffString( o, n, simpleSplit ) {
var splitRe=RegExp('([[]{2}|[\\]]{2}|[{]{2,3}|[}]{2,3}|[|]|=|<|>|[*:]+|\\s|\\b)');
// We need to split the strings o and n first, and entify() the parts
// individually, so that the HTML entities are never cut apart. (AxelBoldt)
var out, i, oSplitted, nSplitted;
if (simpleSplit) {
oSplitted=o.split(/\b/);
nSplitted=n.split(/\b/);
} else {
oSplitted=o.parenSplit(splitRe);
nSplitted=n.parenSplit(splitRe);
}
for (i=0; i<oSplitted.length; ++i) {oSplitted[i]=oSplitted[i].entify();}
for (i=0; i<nSplitted.length; ++i) {nSplitted[i]=nSplitted[i].entify();}
out = diff (oSplitted, nSplitted);
var str = "";
var acc=[]; // accumulator for prettier output
// crossing pairings -- eg 'A B' vs 'B A' -- cause problems, so let's iron them out
// this doesn't always do things optimally but it should be fast enough
var maxOutputPair=0;
for (i=0; i<out.n.length; ++i) {
if ( out.n[i].paired ) {
if( maxOutputPair > out.n[i].row ) {
// tangle - delete pairing
out.o[ out.n[i].row ]=out.o[ out.n[i].row ].text;
out.n[i]=out.n[i].text;
}
if (maxOutputPair < out.n[i].row) { maxOutputPair = out.n[i].row; }
}
}
// output the stuff preceding the first paired old line
for (i=0; i<out.o.length && !out.o[i].paired; ++i) { acc.push( out.o[i] ); }
str += delFmt(acc); acc=[];
// main loop
for ( i = 0; i < out.n.length; ++i ) {
// output unpaired new "lines"
while ( i < out.n.length && !out.n[i].paired ) { acc.push( out.n[i++] ); }
str += insFmt(acc); acc=[];
if ( i < out.n.length ) { // this new "line" is paired with the (out.n[i].row)th old "line"
str += out.n[i].text;
// output unpaired old rows starting after this new line's partner
var m = out.n[i].row + 1;
while ( m < out.o.length && !out.o[m].paired ) { acc.push ( out.o[m++] ); }
str += delFmt(acc); acc=[];
}
}
return str;
}
// see http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Object
// FIXME: use obj.hasOwnProperty instead of this kludge!
var jsReservedProperties=RegExp('^(constructor|prototype|__((define|lookup)[GS]etter)__' +
'|eval|hasOwnProperty|propertyIsEnumerable' +
'|to(Source|String|LocaleString)|(un)?watch|valueOf)$');
function diffBugAlert(word) {
if (!diffBugAlert.list[word]) {
diffBugAlert.list[word]=1;
alert('Bad word: '+word+'\n\nPlease report this bug.');
}
}
diffBugAlert.list={};
function makeDiffHashtable(src) {
var ret={};
for ( var i = 0; i < src.length; i++ ) {
if ( jsReservedProperties.test(src[i]) ) { src[i] += '<!-- -->'; }
if ( !ret[ src[i] ] ) { ret[ src[i] ] = []; }
try { ret[ src[i] ].push( i ); } catch (err) { diffBugAlert(src[i]); }
}
return ret;
}
function diff( o, n ) {
// pass 1: make hashtable ns with new rows as keys
var ns = makeDiffHashtable(n);
// pass 2: make hashtable os with old rows as keys
var os = makeDiffHashtable(o);
// pass 3: pair unique new rows and matching unique old rows
var i;
for ( i in ns ) {
if ( ns[i].length == 1 && os[i] && os[i].length == 1 ) {
n[ ns[i][0] ] = { text: n[ ns[i][0] ], row: os[i][0], paired: true };
o[ os[i][0] ] = { text: o[ os[i][0] ], row: ns[i][0], paired: true };
}
}
// pass 4: pair matching rows immediately following paired rows (not necessarily unique)
for ( i = 0; i < n.length - 1; i++ ) {
if ( n[i].paired && ! n[i+1].paired && n[i].row + 1 < o.length && ! o[ n[i].row + 1 ].paired &&
n[i+1] == o[ n[i].row + 1 ] ) {
n[i+1] = { text: n[i+1], row: n[i].row + 1, paired: true };
o[n[i].row+1] = { text: o[n[i].row+1], row: i + 1, paired: true };
}
}
// pass 5: pair matching rows immediately preceding paired rows (not necessarily unique)
for ( i = n.length - 1; i > 0; i-- ) {
if ( n[i].paired && ! n[i-1].paired && n[i].row > 0 && ! o[ n[i].row - 1 ].paired &&
n[i-1] == o[ n[i].row - 1 ] ) {
n[i-1] = { text: n[i-1], row: n[i].row - 1, paired: true };
o[n[i].row-1] = { text: o[n[i].row-1], row: i - 1, paired: true };
}
}
return { o: o, n: n };
}
//</NOLITE>
// ENDFILE: diff.js
// STARTFILE: init.js
function setSiteInfo() {
if (window.popupLocalDebug) {
pg.wiki.hostname = 'en.wikipedia.org';
} else {
pg.wiki.hostname = location.hostname; // use in preference to location.hostname for flexibility (?)
}
pg.wiki.wikimedia=RegExp('(wiki([pm]edia|source|books|news|quote|versity)|wiktionary|mediawiki)[.]org').test(pg.wiki.hostname);
pg.wiki.wikia=RegExp('[.]wikia[.]com$', 'i').test(pg.wiki.hostname);
pg.wiki.isLocal=RegExp('^localhost').test(pg.wiki.hostname);
pg.wiki.commons=( pg.wiki.wikimedia && pg.wiki.hostname != 'commons.wikimedia.org') ? 'commons.wikimedia.org' : null;
pg.wiki.lang = mw.config.get('wgContentLanguage');
var port = location.port ? ':' + location.port : '';
pg.wiki.sitebase = pg.wiki.hostname + port;
}
function setUserInfo() {
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'query',
list: 'users',
ususers: mw.config.get('wgUserName'),
usprop: 'rights'
};
pg.user.canReview = false;
if (getValueOf('popupReview')) {
api.get(params).done(function(data){
var rights = data.query.users[0].rights;
pg.user.canReview = rights.indexOf('review') !== -1; // TODO: Should it be a getValueOf('ReviewRight') ?
});
}
}
function setTitleBase() {
var protocol = ( window.popupLocalDebug ? 'http:' : location.protocol );
pg.wiki.articlePath = mw.config.get('wgArticlePath').replace(/\/\$1/, ""); // as in http://some.thing.com/wiki/Article
pg.wiki.botInterfacePath = mw.config.get('wgScript');
pg.wiki.APIPath = mw.config.get('wgScriptPath') +"/api.php";
// default mediawiki setting is paths like http://some.thing.com/articlePath/index.php?title=foo
var titletail = pg.wiki.botInterfacePath + '?title=';
//var titletail2 = joinPath([pg.wiki.botInterfacePath, 'wiki.phtml?title=']);
// other sites may need to add code here to set titletail depending on how their urls work
pg.wiki.titlebase = protocol + '//' + pg.wiki.sitebase + titletail;
//pg.wiki.titlebase2 = protocol + '//' + joinPath([pg.wiki.sitebase, titletail2]);
pg.wiki.wikibase = protocol + '//' + pg.wiki.sitebase + pg.wiki.botInterfacePath;
pg.wiki.apiwikibase = protocol + '//' + pg.wiki.sitebase + pg.wiki.APIPath;
pg.wiki.articlebase = protocol + '//' + pg.wiki.sitebase + pg.wiki.articlePath;
pg.wiki.commonsbase = protocol + '//' + pg.wiki.commons + pg.wiki.botInterfacePath;
pg.wiki.apicommonsbase = protocol + '//' + pg.wiki.commons + pg.wiki.APIPath;
pg.re.basenames = RegExp( '^(' +
map( literalizeRegex, [ pg.wiki.titlebase, //pg.wiki.titlebase2,
pg.wiki.articlebase ]).join('|') + ')' );
}
//////////////////////////////////////////////////
// Global regexps
function setMainRegex() {
var reStart='[^:]*://';
var preTitles = literalizeRegex( mw.config.get('wgScriptPath') ) + '/(?:index[.]php|wiki[.]phtml)[?]title=';
preTitles += '|' + literalizeRegex( pg.wiki.articlePath + '/' );
var reEnd='(' + preTitles + ')([^&?#]*)[^#]*(?:#(.+))?';
pg.re.main = RegExp(reStart + literalizeRegex(pg.wiki.sitebase) + reEnd);
}
function setRegexps() {
// TODO: We shoud use an api call to get the aliases for special pages, now it does not work for non-English wikipedias:
// E.g., https://ru.wikipedia.org/w/api.php?action=query&meta=siteinfo&siprop=specialpagealiases&formatversion=2
setMainRegex();
var sp=nsRe(pg.nsSpecialId);
pg.re.urlNoPopup=RegExp('((title=|/)' + sp + '(?:%3A|:)|section=[0-9]|^#$)') ;
pg.re.contribs =RegExp('(title=|/)' + sp + '(?:%3A|:)Contributions' + '(&target=|/|/' + nsRe(pg.nsUserId)+':)(.*)') ;
pg.re.email =RegExp('(title=|/)' + sp + '(?:%3A|:)EmailUser' + '(&target=|/|/(?:' + nsRe(pg.nsUserId)+':)?)(.*)') ;
pg.re.backlinks =RegExp('(title=|/)' + sp + '(?:%3A|:)WhatLinksHere' + '(&target=|/)([^&]*)');
pg.re.specialdiff=RegExp('/' + sp + '(?:%3A|:)Diff/([^?#]*)');
//<NOLITE>
var im=nsReImage();
// note: tries to get images in infobox templates too, e.g. movie pages, album pages etc
// (^|\[\[)image: *([^|\]]*[^|\] ]) *
// (^|\[\[)image: *([^|\]]*[^|\] ])([^0-9\]]*([0-9]+) *px)?
// $4 = 120 as in 120px
pg.re.image = RegExp('(^|\\[\\[)' + im + ': *([^|\\]]*[^|\\] ])' +
'([^0-9\\]]*([0-9]+) *px)?|(?:\\n *[|]?|[|]) *' +
'(' + getValueOf('popupImageVarsRegexp') + ')' +
' *= *(?:\\[\\[ *)?(?:' + im + ':)?' +
'([^|]*?)(?:\\]\\])? *[|]? *\\n', 'img') ;
pg.re.imageBracketCount = 6;
pg.re.category = RegExp('\\[\\[' +nsRe(pg.nsCategoryId) +
': *([^|\\]]*[^|\\] ]) *', 'i');
pg.re.categoryBracketCount = 1;
pg.re.ipUser=RegExp('^' +
// IPv6
'(?::(?::|(?::[0-9A-Fa-f]{1,4}){1,7})|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0,6}::|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){7})' +
// IPv4
'|(((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}' +
'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9]))$');
pg.re.stub= RegExp(getValueOf('popupStubRegexp'), 'im');
pg.re.disambig=RegExp(getValueOf('popupDabRegexp'), 'im');
//</NOLITE>
// FIXME replace with general parameter parsing function, this is daft
pg.re.oldid=RegExp('[?&]oldid=([^&]*)');
pg.re.diff=RegExp('[?&]diff=([^&]*)');
}
//////////////////////////////////////////////////
// miscellany
function setupCache() {
// page caching
pg.cache.pages = [];
}
function setMisc() {
pg.current.link=null;
pg.current.links=[];
pg.current.linksHash={};
setupCache();
pg.timer.checkPopupPosition=null;
pg.counter.loop=0;
// ids change with each popup: popupImage0, popupImage1 etc
pg.idNumber=0;
// for myDecodeURI
pg.misc.decodeExtras = [
{from: '%2C', to: ',' },
{from: '_', to: ' ' },
{from: '%24', to: '$'},
{from: '%26', to: '&' } // no ,
];
pg.misc.userAgent = 'Navigation popups/1.0 (' + mw.config.get( 'wgServerName' ) +')';
}
// We need a callback since this might end up asynchronous because of
// the mw.loader.using() call.
function setupPopups( callback ) {
if ( setupPopups.completed ) {
if ( typeof callback === 'function' ) {
callback();
}
return;
}
// These dependencies are also enforced from the gadget,
// but not everyone loads this as a gadget, so double check
mw.loader.using( ['mediawiki.util', 'mediawiki.user', 'user.options'] ).then( function() {
// NB translatable strings should be set up first (strings.js)
// basics
setupDebugging();
setSiteInfo();
setTitleBase();
setOptions(); // see options.js
setUserInfo();
// namespaces etc
setNamespaces();
setInterwiki();
// regexps
setRegexps();
setRedirs();
// other stuff
setMisc();
setupLivePreview();
// main deal here
setupTooltips();
log('In setupPopups(), just called setupTooltips()');
Navpopup.tracker.enable();
setupPopups.completed = true;
if ( typeof callback === 'function' ) {
callback();
}
});
}
// ENDFILE: init.js
// STARTFILE: navlinks.js
//<NOLITE>
//////////////////////////////////////////////////
// navlinks... let the fun begin
//
function defaultNavlinkSpec() {
var str='';
str += '<b><<mainlink|shortcut= >></b>';
if (getValueOf('popupLastEditLink')) {
str += '*<<lastEdit|shortcut=/>>|<<lastContrib>>|<<sinceMe>>if(oldid){|<<oldEdit>>|<<diffCur>>}';
}
// user links
// contribs - log - count - email - block
// count only if applicable; block only if popupAdminLinks
str += 'if(user){<br><<contribs|shortcut=c>>*<<userlog|shortcut=L|log>>';
str+='if(ipuser){*<<arin>>}if(wikimedia){*<<count|shortcut=#>>}';
str+='if(ipuser){}else{*<<email|shortcut=E>>}if(admin){*<<block|shortcut=b>>|<<blocklog|log>>}}';
// editing links
// talkpage -> edit|new - history - un|watch - article|edit
// other page -> edit - history - un|watch - talk|edit|new
var editstr='<<edit|shortcut=e>>';
var editOldidStr='if(oldid){<<editOld|shortcut=e>>|<<revert|shortcut=v|rv>>|<<edit|cur>>}else{' + editstr + '}';
var historystr='<<history|shortcut=h>>|<<editors|shortcut=E|>>';
var watchstr='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
str+='<br>if(talk){' +
editOldidStr+'|<<new|shortcut=+>>' + '*' + historystr+'*'+watchstr + '*' +
'<b><<article|shortcut=a>></b>|<<editArticle|edit>>' +
'}else{' + // not a talk page
editOldidStr + '*' + historystr + '*' + watchstr + '*' +
'<b><<talk|shortcut=t>></b>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>}';
// misc links
str += '<br><<whatLinksHere|shortcut=l>>*<<relatedChanges|shortcut=r>>*<<move|shortcut=m>>';
// admin links
str += 'if(admin){<br><<unprotect|unprotectShort>>|<<protect|shortcut=p>>|<<protectlog|log>>*' +
'<<undelete|undeleteShort>>|<<delete|shortcut=d>>|<<deletelog|log>>}';
return str;
}
function navLinksHTML (article, hint, params) { //oldid, rcid) {
var str = '<span class="popupNavLinks">' + defaultNavlinkSpec() + '</span>';
// BAM
return navlinkStringToHTML(str, article, params);
}
function expandConditionalNavlinkString(s,article,z,recursionCount) {
var oldid=z.oldid, rcid=z.rcid, diff=z.diff;
// nested conditionals (up to 10 deep) are ok, hopefully! (work from the inside out)
if (typeof recursionCount!=typeof 0) { recursionCount=0; }
var conditionalSplitRegex=RegExp(
//(1 if \\( (2 2) \\) {(3 3)} (4 else {(5 5)} 4)1)
'(;?\\s*if\\s*\\(\\s*([\\w]*)\\s*\\)\\s*\\{([^{}]*)\\}(\\s*else\\s*\\{([^{}]*?)\\}|))', 'i');
var splitted=s.parenSplit(conditionalSplitRegex);
// $1: whole conditional
// $2: test condition
// $3: true expansion
// $4: else clause (possibly empty)
// $5: false expansion (possibly null)
var numParens=5;
var ret = splitted[0];
for (var i=1; i<splitted.length; i=i+numParens+1) {
var testString=splitted[i+2-1];
var trueString=splitted[i+3-1];
var falseString=splitted[i+5-1];
if (typeof falseString=='undefined' || !falseString) { falseString=''; }
var testResult=null;
switch (testString) {
case 'user':
testResult=(article.userName())?true:false;
break;
case 'talk':
testResult=(article.talkPage())?false:true; // talkPage converts _articles_ to talkPages
break;
case 'admin':
testResult=getValueOf('popupAdminLinks')?true:false;
break;
case 'oldid':
testResult=(typeof oldid != 'undefined' && oldid)?true:false;
break;
case 'rcid':
testResult=(typeof rcid != 'undefined' && rcid)?true:false;
break;
case 'ipuser':
testResult=(article.isIpUser())?true:false;
break;
case 'mainspace_en':
testResult=isInMainNamespace(article) &&
pg.wiki.hostname=='en.wikipedia.org';
break;
case 'wikimedia':
testResult=(pg.wiki.wikimedia) ? true : false;
break;
case 'diff':
testResult=(typeof diff != 'undefined' && diff)?true:false;
break;
}
switch(testResult) {
case null: ret+=splitted[i]; break;
case true: ret+=trueString; break;
case false: ret+=falseString; break;
}
// append non-conditional string
ret += splitted[i+numParens];
}
if (conditionalSplitRegex.test(ret) && recursionCount < 10) {
return expandConditionalNavlinkString(ret,article,z,recursionCount+1);
}
return ret;
}
function navlinkStringToArray(s, article, params) {
s=expandConditionalNavlinkString(s,article,params);
var splitted=s.parenSplit(RegExp('<<(.*?)>>'));
var ret=[];
for (var i=0; i<splitted.length; ++i) {
if (i%2) { // i odd, so s is a tag
var t=new navlinkTag();
var ss=splitted[i].split('|');
t.id=ss[0];
for (var j=1; j<ss.length; ++j) {
var sss=ss[j].split('=');
if (sss.length>1) {
t[sss[0]]=sss[1];
}
else { // no assignment (no "="), so treat this as a title (overwriting the last one)
t.text=popupString(sss[0]);
}
}
t.article=article;
var oldid=params.oldid, rcid=params.rcid, diff=params.diff;
if (typeof oldid !== 'undefined' && oldid !== null) { t.oldid=oldid; }
if (typeof rcid !== 'undefined' && rcid !== null) { t.rcid=rcid; }
if (typeof diff !== 'undefined' && diff !== null) { t.diff=diff; }
if (!t.text && t.id !== 'mainlink') { t.text=popupString(t.id); }
ret.push(t);
}
else { // plain HTML
ret.push(splitted[i]);
}
}
return ret;
}
function navlinkSubstituteHTML(s) {
return s.split('*').join(getValueOf('popupNavLinkSeparator'))
.split('<menurow>').join('<li class="popup_menu_row">')
.split('</menurow>').join('</li>')
.split('<menu>').join('<ul class="popup_menu">')
.split('</menu>').join('</ul>');
}
function navlinkDepth(magic,s) {
return s.split('<' + magic + '>').length - s.split('</' + magic + '>').length;
}
// navlinkString: * becomes the separator
// <<foo|bar=baz|fubar>> becomes a foo-link with attribute bar='baz'
// and visible text 'fubar'
// if(test){...} and if(test){...}else{...} work too (nested ok)
function navlinkStringToHTML(s,article,params) {
//limitAlert(navlinkStringToHTML, 5, 'navlinkStringToHTML\n' + article + '\n' + (typeof article));
var p=navlinkStringToArray(s,article,params);
var html='';
var menudepth = 0; // nested menus not currently allowed, but doesn't do any harm to code for it
var menurowdepth = 0;
for (var i=0; i<p.length; ++i) {
if (typeof p[i] == typeof '') {
html+=navlinkSubstituteHTML(p[i]);
menudepth += navlinkDepth('menu', p[i]);
menurowdepth += navlinkDepth('menurow', p[i]);
// if (menudepth === 0) {
// tagType='span';
// } else if (menurowdepth === 0) {
// tagType='li';
// } else {
// tagType = null;
// }
} else if (typeof p[i].type != 'undefined' && p[i].type=='navlinkTag') {
if (menudepth > 0 && menurowdepth === 0) {
html += '<li class="popup_menu_item">' + p[i].html() + '</li>';
} else {
html+=p[i].html();
}
}
}
return html;
}
function navlinkTag() {
this.type='navlinkTag';
}
navlinkTag.prototype.html=function () {
this.getNewWin();
this.getPrintFunction();
var html='';
var opening, closing;
var tagType='span';
if (!tagType) {
opening = ''; closing = '';
} else {
opening = '<' + tagType + ' class="popup_' + this.id + '">';
closing = '</' + tagType + '>';
}
if (typeof this.print!='function') {
errlog ('Oh dear - invalid print function for a navlinkTag, id='+this.id);
} else {
html=this.print(this);
if (typeof html != typeof '') {html='';}
else if (typeof this.shortcut!='undefined') html=addPopupShortcut(html, this.shortcut);
}
return opening + html + closing;
};
navlinkTag.prototype.getNewWin=function() {
getValueOf('popupLinksNewWindow');
if (typeof pg.option.popupLinksNewWindow[this.id] === 'undefined') { this.newWin=null; }
this.newWin=pg.option.popupLinksNewWindow[this.id];
};
navlinkTag.prototype.getPrintFunction=function() { //think about this some more
// this.id and this.article should already be defined
if (typeof this.id!=typeof '' || typeof this.article!=typeof {} ) { return; }
this.noPopup=1;
switch (this.id) {
case 'contribs': case 'history': case 'whatLinksHere':
case 'userPage': case 'monobook': case 'userTalk':
case 'talk': case 'article': case 'lastEdit':
this.noPopup=null;
}
switch (this.id) {
case 'email': case 'contribs': case 'block': case 'unblock':
case 'userlog': case 'userSpace': case 'deletedContribs':
this.article=this.article.userName();
}
switch (this.id) {
case 'userTalk': case 'newUserTalk': case 'editUserTalk':
case 'userPage': case 'monobook': case 'editMonobook': case 'blocklog':
this.article=this.article.userName(true);
/* fall through */
case 'pagelog': case 'deletelog': case 'protectlog':
delete this.oldid;
}
if (this.id=='editMonobook' || this.id=='monobook') { this.article.append('/monobook.js'); }
if (this.id != 'mainlink') {
// FIXME anchor handling should be done differently with Title object
this.article=this.article.removeAnchor();
// if (typeof this.text=='undefined') this.text=popupString(this.id);
}
switch (this.id) {
case 'undelete': this.print=specialLink; this.specialpage='Undelete'; this.sep='/'; break;
case 'whatLinksHere': this.print=specialLink; this.specialpage='Whatlinkshere'; break;
case 'relatedChanges': this.print=specialLink; this.specialpage='Recentchangeslinked'; break;
case 'move': this.print=specialLink; this.specialpage='Movepage'; break;
case 'contribs': this.print=specialLink; this.specialpage='Contributions'; break;
case 'deletedContribs':this.print=specialLink; this.specialpage='Deletedcontributions'; break;
case 'email': this.print=specialLink; this.specialpage='EmailUser'; this.sep='/'; break;
case 'block': this.print=specialLink; this.specialpage='Blockip'; this.sep='&ip='; break;
case 'unblock': this.print=specialLink; this.specialpage='Ipblocklist'; this.sep='&action=unblock&ip='; break;
case 'userlog': this.print=specialLink; this.specialpage='Log'; this.sep='&user='; break;
case 'blocklog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=block&page='; break;
case 'pagelog': this.print=specialLink; this.specialpage='Log'; this.sep='&page='; break;
case 'protectlog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=protect&page='; break;
case 'deletelog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=delete&page='; break;
case 'userSpace': this.print=specialLink; this.specialpage='PrefixIndex'; this.sep='&namespace=2&prefix='; break;
case 'search': this.print=specialLink; this.specialpage='Search'; this.sep='&fulltext=Search&search='; break;
case 'thank': this.print=specialLink; this.specialpage='Thanks'; this.sep='/'; this.article.value = this.diff; break;
case 'unwatch': case 'watch':
this.print=magicWatchLink; this.action=this.id+'&autowatchlist=1&autoimpl=' + popupString('autoedit_version') + '&actoken='+autoClickToken(); break;
case 'history': case 'historyfeed':
case 'unprotect': case 'protect':
this.print=wikiLink; this.action=this.id; break;
case 'delete':
this.print=wikiLink; this.action='delete';
if (this.article.namespaceId()==pg.nsImageId) {
var img=this.article.stripNamespace();
this.action+='&image='+img;
}
break;
case 'markpatrolled':
case 'edit': // editOld should keep the oldid, but edit should not.
delete this.oldid;
/* fall through */
case 'view': case 'purge': case 'render':
this.print=wikiLink;
this.action=this.id; break;
case 'raw':
this.print=wikiLink; this.action='raw'; break;
case 'new':
this.print=wikiLink; this.action='edit§ion=new'; break;
case 'mainlink':
if (typeof this.text=='undefined') { this.text=this.article.toString().entify(); }
if (getValueOf('popupSimplifyMainLink') && isInStrippableNamespace(this.article)) {
// only show the /subpage part of the title text
var s=this.text.split('/'); this.text=s[s.length-1];
if (this.text==='' && s.length > 1) { this.text=s[s.length-2]; }
}
this.print=titledWikiLink;
if (typeof this.title==='undefined' && pg.current.link && typeof pg.current.link.href !== 'undefined') {
this.title=safeDecodeURI((pg.current.link.originalTitle)?pg.current.link.originalTitle:this.article);
if (typeof this.oldid !== 'undefined' && this.oldid) {
this.title=tprintf('Revision %s of %s', [this.oldid, this.title]);
}
}
this.action='view'; break;
case 'userPage':
case 'article':
case 'monobook':
case 'editMonobook':
case 'editArticle':
delete this.oldid;
//alert(this.id+'\n'+this.article + '\n'+ typeof this.article);
this.article=this.article.articleFromTalkOrArticle();
//alert(this.id+'\n'+this.article + '\n'+ typeof this.article);
this.print=wikiLink;
if (this.id.indexOf('edit')===0) {
this.action='edit';
} else { this.action='view';}
break;
case 'userTalk':
case 'talk':
this.article=this.article.talkPage();
delete this.oldid;
this.print=wikiLink;
this.action='view'; break;
case 'arin':
this.print=arinLink; break;
case 'count':
this.print=editCounterLink; break;
case 'google':
this.print=googleLink; break;
case 'editors':
this.print=editorListLink; break;
case 'globalsearch':
this.print=globalSearchLink; break;
case 'lastEdit':
this.print=titledDiffLink;
this.title=popupString('Show the last edit');
this.from='prev'; this.to='cur'; break;
case 'oldEdit':
this.print=titledDiffLink;
this.title=popupString('Show the edit made to get revision') + ' ' + this.oldid;
this.from='prev'; this.to=this.oldid; break;
case 'editOld':
this.print=wikiLink; this.action='edit'; break;
case 'undo':
this.print=wikiLink; this.action='edit&undo='; break;
case 'revert':
this.print=wikiLink; this.action='revert'; break;
case 'nullEdit':
this.print=wikiLink; this.action='nullEdit'; break;
case 'diffCur':
this.print=titledDiffLink;
this.title=tprintf('Show changes since revision %s', [this.oldid]);
this.from=this.oldid; this.to='cur'; break;
case 'editUserTalk':
case 'editTalk':
delete this.oldid;
this.article=this.article.talkPage();
this.action='edit'; this.print=wikiLink; break;
case 'newUserTalk':
case 'newTalk':
this.article=this.article.talkPage();
this.action='edit§ion=new'; this.print=wikiLink; break;
case 'lastContrib':
case 'sinceMe':
this.print=magicHistoryLink;
break;
case 'togglePreviews':
this.text=popupString(pg.option.simplePopups ? 'enable previews' : 'disable previews');
/* fall through */
case 'disablePopups': case 'purgePopups':
this.print=popupMenuLink;
break;
default:
this.print=function () {return 'Unknown navlink type: '+this.id+'';};
}
};
//
// end navlinks
//////////////////////////////////////////////////
//</NOLITE>
// ENDFILE: navlinks.js
// STARTFILE: shortcutkeys.js
//<NOLITE>
function popupHandleKeypress(evt) {
var keyCode = window.event ? window.event.keyCode : ( evt.keyCode ? evt.keyCode : evt.which);
if (!keyCode || !pg.current.link || !pg.current.link.navpopup) { return; }
if (keyCode==27) { // escape
killPopup();
return false; // swallow keypress
}
var letter=String.fromCharCode(keyCode);
var links=pg.current.link.navpopup.mainDiv.getElementsByTagName('A');
var startLink=0;
var i,j;
if (popupHandleKeypress.lastPopupLinkSelected) {
for (i=0; i<links.length; ++i) {
if (links[i]==popupHandleKeypress.lastPopupLinkSelected) { startLink=i; }
}
}
for (j=0; j<links.length; ++j) {
i=(startLink + j + 1) % links.length;
if (links[i].getAttribute('popupkey')==letter) {
if (evt && evt.preventDefault) evt.preventDefault();
links[i].focus();
popupHandleKeypress.lastPopupLinkSelected=links[i];
return false; // swallow keypress
}
}
// pass keypress on
if (document.oldPopupOnkeypress) { return document.oldPopupOnkeypress(evt); }
return true;
}
function addPopupShortcuts() {
if (document.onkeypress!=popupHandleKeypress) {
document.oldPopupOnkeypress=document.onkeypress;
}
document.onkeypress=popupHandleKeypress;
}
function rmPopupShortcuts() {
popupHandleKeypress.lastPopupLinkSelected=null;
try {
if (document.oldPopupOnkeypress && document.oldPopupOnkeypress==popupHandleKeypress) {
// panic
document.onkeypress=null; //function () {};
return;
}
document.onkeypress=document.oldPopupOnkeypress;
} catch (nasties) { /* IE goes here */ }
}
function addLinkProperty(html, property) {
// take "<a href=...>...</a> and add a property
// not sophisticated at all, easily broken
var i=html.indexOf('>');
if (i<0) { return html; }
return html.substring(0,i) + ' ' + property + html.substring(i);
}
function addPopupShortcut(html, key) {
if (!getValueOf('popupShortcutKeys')) { return html; }
var ret= addLinkProperty(html, 'popupkey="'+key+'"');
if (key==' ') { key=popupString('spacebar'); }
return ret.replace(RegExp('^(.*?)(title=")(.*?)(".*)$', 'i'),'$1$2$3 ['+key+']$4');
}
//</NOLITE>
// ENDFILE: shortcutkeys.js
// STARTFILE: diffpreview.js
//<NOLITE>
//lets jump through hoops to find the rev ids we need to retrieve
function loadDiff(article, oldid, diff, navpop) {
navpop.diffData={ oldRev: {}, newRev: {} };
mw.loader.using( 'mediawiki.api' ).then( function() {
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'compare',
prop: 'ids|title'
};
if(article.title){
params.fromtitle = article.title;
}
switch (diff) {
case 'cur':
switch ( oldid ) {
case null:
case '':
case 'prev':
// this can only work if we have the title
// cur -> prev
params.torelative = 'prev';
break;
default:
params.fromrev = oldid;
params.torelative = 'cur';
break;
}
break;
case 'prev':
if( oldid ) {
params.fromrev = oldid;
} else {
params.fromtitle;
}
params.torelative = 'prev';
break;
case 'next':
params.fromrev = oldid || 0;
params.torelative = 'next';
break;
default:
params.fromrev = oldid || 0;
params.torev = diff || 0;
break;
}
api.get( params ).then( function( data ) {
navpop.diffData.oldRev.revid = data.compare.fromrevid;
navpop.diffData.newRev.revid = data.compare.torevid;
addReviewLink(navpop, 'popupMiscTools');
var go = function() {
pendingNavpopTask(navpop);
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&';
url += 'revids=' + navpop.diffData.oldRev.revid + '|' + navpop.diffData.newRev.revid;
url += '&prop=revisions&rvprop=ids|timestamp|content';
getPageWithCaching(url, doneDiff, navpop);
return true; // remove hook once run
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'before', 'DOWNLOAD_DIFFS'); }
} );
} );
}
// Put a "mark patrolled" link to an element target
// TODO: Allow patrol a revision, as well as a diff
function addReviewLink (navpop, target) {
if (! pg.user.canReview) return;
// If 'newRev' is older than 'oldRev' than it could be confusing, so we do not show the review link.
if (navpop.diffData.newRev.revid <= navpop.diffData.oldRev.revid) return;
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'query',
prop: 'info|flagged',
revids: navpop.diffData.oldRev.revid,
formatversion : 2
};
api.get (params).then(function(data){
var stable_revid = data.query.pages[0].flagged && data.query.pages[0].flagged.stable_revid || 0;
// The diff can be reviewed if the old version is the last reviewed version
// TODO: Other possible conditions that we may want to implement instead of this one:
// * old version is patrolled and the new version is not patrolled
// * old version is patrolled and the new version is more recent than the last reviewed version
if (stable_revid == navpop.diffData.oldRev.revid) {
var a = document.createElement('a');
a.innerHTML = popupString('mark patrolled');
a.title=popupString('markpatrolledHint');
a.onclick = function() {
var params = {
action: 'review',
revid: navpop.diffData.newRev.revid,
comment: tprintf('defaultpopupReviewedSummary', [navpop.diffData.oldRev.revid, navpop.diffData.newRev.revid])
};
api.postWithToken('csrf',params).done(function(){
a.style.display = "none";
// TODO: Update current page and other already constructed popups
} ).fail(function(){
alert(popupString('Could not marked this edit as patrolled'));
});
};
setPopupHTML(a, target, navpop.idNumber,null,true);
}
});
}
function doneDiff(download) {
if (!download.owner || !download.owner.diffData) { return; }
var navpop=download.owner;
completedNavpopTask(navpop);
var pages, revisions=[];
try{
// Process the downloads
pages = getJsObj(download.data).query.pages;
for(var i=0; i < pages.length; i++ ) {
revisions = revisions.concat(pages[i].revisions);
}
for(i=0; i< revisions.length; i++){
if(revisions[i].revid == navpop.diffData.oldRev.revid) {
navpop.diffData.oldRev.revision = revisions[i];
} else if (revisions[i].revid == navpop.diffData.newRev.revid) {
navpop.diffData.newRev.revision = revisions[i];
}
}
} catch(someError) {
errlog( 'Could not get diff' );
}
insertDiff(navpop);
}
function rmBoringLines(a,b,context) {
if (typeof context == 'undefined') { context=2; }
// this is fairly slow... i think it's quicker than doing a word-based diff from the off, though
var aa=[], aaa=[];
var bb=[], bbb=[];
var i, j;
// first, gather all disconnected nodes in a and all crossing nodes in a and b
for (i=0; i<a.length; ++i ) {
if(!a[i].paired) { aa[i]=1; }
else if (countCrossings(b,a,i, true)) {
aa[i]=1;
bb[ a[i].row ] = 1;
}
}
// pick up remaining disconnected nodes in b
for (i=0; i<b.length; ++i ) {
if (bb[i]==1) { continue; }
if(!b[i].paired) { bb[i]=1; }
}
// another pass to gather context: we want the neighbours of included nodes which are not yet included
// we have to add in partners of these nodes, but we don't want to add context for *those* nodes in the next pass
for (i=0; i<b.length; ++i) {
if ( bb[i] == 1 ) {
for (j=Math.max(0,i-context); j < Math.min(b.length, i+context); ++j) {
if ( !bb[j] ) { bb[j] = 1; aa[ b[j].row ] = 0.5; }
}
}
}
for (i=0; i<a.length; ++i) {
if ( aa[i] == 1 ) {
for (j=Math.max(0,i-context); j < Math.min(a.length, i+context); ++j) {
if ( !aa[j] ) { aa[j] = 1; bb[ a[j].row ] = 0.5; }
}
}
}
for (i=0; i<bb.length; ++i) {
if (bb[i] > 0) { // it's a row we need
if (b[i].paired) { bbb.push(b[i].text); } // joined; partner should be in aa
else {
bbb.push(b[i]);
}
}
}
for (i=0; i<aa.length; ++i) {
if (aa[i] > 0) { // it's a row we need
if (a[i].paired) { aaa.push(a[i].text); } // joined; partner should be in aa
else {
aaa.push(a[i]);
}
}
}
return { a: aaa, b: bbb};
}
function stripOuterCommonLines(a,b,context) {
var i=0;
while (i<a.length && i < b.length && a[i]==b[i]) { ++i; }
var j=a.length-1; var k=b.length-1;
while ( j>=0 && k>=0 && a[j]==b[k] ) { --j; --k; }
return { a: a.slice(Math.max(0,i - 1 - context), Math.min(a.length+1, j + context+1)),
b: b.slice(Math.max(0,i - 1 - context), Math.min(b.length+1, k + context+1)) };
}
function insertDiff(navpop) {
// for speed reasons, we first do a line-based diff, discard stuff that seems boring, then do a word-based diff
// FIXME: sometimes this gives misleading diffs as distant chunks are squashed together
var oldlines = navpop.diffData.oldRev.revision.content.split('\n');
var newlines = navpop.diffData.newRev.revision.content.split('\n');
var inner=stripOuterCommonLines(oldlines,newlines,getValueOf('popupDiffContextLines'));
oldlines=inner.a; newlines=inner.b;
var truncated=false;
getValueOf('popupDiffMaxLines');
if (oldlines.length > pg.option.popupDiffMaxLines || newlines.length > pg.option.popupDiffMaxLines) {
// truncate
truncated=true;
inner=stripOuterCommonLines(oldlines.slice(0,pg.option.popupDiffMaxLines),
newlines.slice(0,pg.option.popupDiffMaxLines),
pg.option.popupDiffContextLines);
oldlines=inner.a; newlines=inner.b;
}
var lineDiff=diff(oldlines, newlines);
var lines2=rmBoringLines(lineDiff.o, lineDiff.n);
var oldlines2=lines2.a; var newlines2=lines2.b;
var simpleSplit = !String.prototype.parenSplit.isNative;
var html='<hr />';
if (getValueOf('popupDiffDates')) {
html += diffDatesTable(navpop);
html += '<hr />';
}
html += shortenDiffString(
diffString(oldlines2.join('\n'), newlines2.join('\n'), simpleSplit),
getValueOf('popupDiffContextCharacters') ).join('<hr />');
setPopupTipsAndHTML(html.split('\n').join('<br>') +
(truncated ? '<hr /><b>'+popupString('Diff truncated for performance reasons')+'</b>' : '') ,
'popupPreview', navpop.idNumber);
}
function diffDatesTable( navpop ) {
var html='<table class="popup_diff_dates">';
html += diffDatesTableRow( navpop.diffData.newRev.revision, tprintf('New revision'));
html += diffDatesTableRow( navpop.diffData.oldRev.revision, tprintf('Old revision'));
html += '</table>';
return html;
}
function diffDatesTableRow( revision, label ) {
var txt='';
var lastModifiedDate = new Date(revision.timestamp);
var datePrint=getValueOf('popupDiffDatePrinter');
if (typeof lastModifiedDate[datePrint] == 'function') {
var d2 = adjustDate(lastModifiedDate, getTimeOffset());
txt = dayFormat(d2, true) + ' ' + timeFormat(d2, true);
} else {
txt = tprintf('Invalid %s %s', ['popupDiffDatePrinter', datePrint]);
}
var revlink = generalLink({url: mw.config.get('wgScript') + '?oldid='+revision.revid,
text: label, title: label});
return simplePrintf('<tr><td>%s</td><td>%s</td></tr>', [ revlink, txt ]);
}
//</NOLITE>
// ENDFILE: diffpreview.js
// STARTFILE: links.js
//<NOLITE>
/////////////////////
// LINK GENERATION //
/////////////////////
// titledDiffLink --> titledWikiLink --> generalLink
// wikiLink --> titledWikiLink --> generalLink
// editCounterLink --> generalLink
// TODO Make these functions return Element objects, not just raw HTML strings.
function titledDiffLink(l) { // article, text, title, from, to) {
return titledWikiLink({article: l.article, action: l.to + '&oldid=' + l.from,
newWin: l.newWin,
noPopup: l.noPopup,
text: l.text, title: l.title,
/* hack: no oldid here */
actionName: 'diff'});
}
function wikiLink(l) {
//{article:article, action:action, text:text, oldid, newid}) {
if (! (typeof l.article == typeof {} &&
typeof l.action == typeof '' &&
typeof l.text==typeof '')) return null;
if (typeof l.oldid == 'undefined') { l.oldid=null; }
var savedOldid = l.oldid;
if (!/^(edit|view|revert|render)$|^raw/.test(l.action)) { l.oldid=null; }
var hint=popupString(l.action + 'Hint'); // revertHint etc etc etc
var oldidData=[l.oldid, safeDecodeURI(l.article)];
var revisionString = tprintf('revision %s of %s', oldidData);
log('revisionString='+revisionString);
switch (l.action) {
case 'edit§ion=new': hint = popupString('newSectionHint'); break;
case 'edit&undo=':
if (l.diff && l.diff != 'prev' && savedOldid ) {
l.action += l.diff + '&undoafter=' + savedOldid;
} else if (savedOldid) {
l.action += savedOldid;
}
hint = popupString('undoHint');
break;
case 'raw&ctype=text/css': hint=popupString('rawHint'); break;
case 'revert':
var p=parseParams(pg.current.link.href);
l.action='edit&autoclick=wpSave&actoken=' + autoClickToken() + '&autoimpl=' + popupString('autoedit_version') + '&autosummary=' + revertSummary(l.oldid, p.diff);
if (p.diff=='prev') {
l.action += '&direction=prev';
revisionString = tprintf('the revision prior to revision %s of %s', oldidData);
}
if (getValueOf('popupRevertSummaryPrompt')) { l.action += '&autosummaryprompt=true'; }
if (getValueOf('popupMinorReverts')) { l.action += '&autominor=true'; }
log('revisionString is now '+revisionString);
break;
case 'nullEdit':
l.action='edit&autoclick=wpSave&actoken=' + autoClickToken() + '&autoimpl=' + popupString('autoedit_version') + '&autosummary=null';
break;
case 'historyfeed':
l.action='history&feed=rss';
break;
case 'markpatrolled':
l.action='markpatrolled&rcid='+l.rcid;
}
if (hint) {
if (l.oldid) {
hint = simplePrintf(hint, [revisionString]);
}
else {
hint = simplePrintf(hint, [safeDecodeURI(l.article)]);
}
}
else {
hint = safeDecodeURI(l.article + '&action=' + l.action) + (l.oldid) ? '&oldid='+l.oldid : '';
}
return titledWikiLink({article: l.article, action: l.action, text: l.text, newWin:l.newWin,
title: hint, oldid: l.oldid, noPopup: l.noPopup, onclick: l.onclick});
}
function revertSummary(oldid, diff) {
var ret='';
if (diff == 'prev') {
ret=getValueOf('popupQueriedRevertToPreviousSummary');
} else { ret = getValueOf('popupQueriedRevertSummary'); }
return ret + '&autorv=' + oldid;
}
function titledWikiLink(l) {
// possible properties of argument:
// article, action, text, title, oldid, actionName, className, noPopup
// oldid = null is fine here
// article and action are mandatory args
if (typeof l.article == 'undefined' || typeof l.action=='undefined') {
errlog('got undefined article or action in titledWikiLink');
return null;
}
var base = pg.wiki.titlebase + l.article.urlString();
var url=base;
if (typeof l.actionName=='undefined' || !l.actionName) { l.actionName='action'; }
// no need to add &action=view, and this confuses anchors
if (l.action != 'view') { url = base + '&' + l.actionName + '=' + l.action; }
if (typeof l.oldid!='undefined' && l.oldid) { url+='&oldid='+l.oldid; }
var cssClass=pg.misc.defaultNavlinkClassname;
if (typeof l.className!='undefined' && l.className) { cssClass=l.className; }
return generalNavLink({url: url, newWin: l.newWin,
title: (typeof l.title != 'undefined') ? l.title : null,
text: (typeof l.text!='undefined')?l.text:null,
className: cssClass, noPopup:l.noPopup, onclick:l.onclick});
}
pg.fn.getLastContrib = function getLastContrib(wikipage, newWin) {
getHistoryInfo(wikipage, function(x) {
processLastContribInfo(x, {page: wikipage, newWin: newWin});
});
};
function processLastContribInfo(info, stuff) {
if(!info.edits || !info.edits.length) { alert('Popups: an odd thing happened. Please retry.'); return; }
if(!info.firstNewEditor) {
alert(tprintf('Only found one editor: %s made %s edits', [info.edits[0].editor,info.edits.length]));
return;
}
var newUrl=pg.wiki.titlebase + new Title(stuff.page).urlString() + '&diff=cur&oldid='+info.firstNewEditor.oldid;
displayUrl(newUrl, stuff.newWin);
}
pg.fn.getDiffSinceMyEdit = function getDiffSinceMyEdit(wikipage, newWin) {
getHistoryInfo(wikipage, function(x){
processDiffSinceMyEdit(x, {page: wikipage, newWin: newWin});
});
};
function processDiffSinceMyEdit(info, stuff) {
if(!info.edits || !info.edits.length) { alert('Popups: something fishy happened. Please try again.'); return; }
var friendlyName=stuff.page.split('_').join(' ');
if(!info.myLastEdit) {
alert(tprintf('Couldn\'t find an edit by %s\nin the last %s edits to\n%s',
[info.userName, getValueOf('popupHistoryLimit'), friendlyName]));
return;
}
if(info.myLastEdit.index === 0) {
alert(tprintf("%s seems to be the last editor to the page %s", [info.userName, friendlyName]));
return;
}
var newUrl=pg.wiki.titlebase + new Title(stuff.page).urlString() + '&diff=cur&oldid='+ info.myLastEdit.oldid;
displayUrl(newUrl, stuff.newWin);
}
function displayUrl(url, newWin){
if(newWin) { window.open(url); }
else { document.location=url; }
}
pg.fn.purgePopups = function purgePopups() {
processAllPopups(true);
setupCache(); // deletes all cached items (not browser cached, though...)
pg.option={};
abortAllDownloads();
};
function processAllPopups(nullify, banish) {
for (var i=0; pg.current.links && i<pg.current.links.length; ++i) {
if (!pg.current.links[i].navpopup) { continue; }
if (nullify || banish) pg.current.links[i].navpopup.banish();
pg.current.links[i].simpleNoMore=false;
if (nullify) pg.current.links[i].navpopup=null;
}
}
pg.fn.disablePopups = function disablePopups(){
processAllPopups(false, true);
setupTooltips(null, true);
};
pg.fn.togglePreviews = function togglePreviews() {
processAllPopups(true, true);
pg.option.simplePopups=!pg.option.simplePopups;
abortAllDownloads();
};
function magicWatchLink(l) {
//Yuck!! Would require a thorough redesign to add this as a click event though ...
l.onclick = simplePrintf( 'pg.fn.modifyWatchlist(\'%s\',\'%s\');return false;', [l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), this.id] );
return wikiLink(l);
}
pg.fn.modifyWatchlist = function modifyWatchlist(title, action) {
var reqData = {
'action': 'watch',
'formatversion': 2,
'titles': title,
'uselang': mw.config.get('wgUserLanguage')
};
if ( action === 'unwatch' ) reqData.unwatch = true;
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
// Load the Addedwatchtext or Removedwatchtext message and show it
var mwTitle = mw.Title.newFromText( title );
var messageName;
if ( mwTitle && mwTitle.getNamespaceId() > 0 && mwTitle.getNamespaceId() % 2 === 1 ) {
messageName = action === 'watch' ? 'addedwatchtext-talk' : 'removedwatchtext-talk';
} else {
messageName = action === 'watch' ? 'addedwatchtext' : 'removedwatchtext';
}
$.when(
api.postWithToken( 'watch', reqData ),
mw.loader.using( [ 'mediawiki.api', 'mediawiki.jqueryMsg' ] ).then( function () {
return api.loadMessagesIfMissing( [ messageName ] );
} )
).done( function () {
mw.notify( mw.message( messageName, title ).parseDom() );
} );
};
function magicHistoryLink(l) {
// FIXME use onclick change href trick to sort this out instead of window.open
var jsUrl='', title='', onClick='';
switch(l.id) {
case 'lastContrib':
onClick=simplePrintf('pg.fn.getLastContrib(\'%s\',%s)',
[l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), l.newWin]);
title=popupString('lastContribHint');
break;
case 'sinceMe':
onClick=simplePrintf('pg.fn.getDiffSinceMyEdit(\'%s\',%s)',
[l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), l.newWin]);
title=popupString('sinceMeHint');
break;
}
jsUrl = 'javascript:' + onClick; // jshint ignore:line
onClick += ';return false;';
return generalNavLink({url: jsUrl, newWin: false, // can't have new windows with JS links, I think
title: title, text: l.text, noPopup: l.noPopup, onclick: onClick });
}
function popupMenuLink(l) {
var jsUrl=simplePrintf('javascript:pg.fn.%s()', [l.id]); // jshint ignore:line
var title=popupString(simplePrintf('%sHint', [l.id]));
var onClick=simplePrintf('pg.fn.%s();return false;', [l.id]);
return generalNavLink({url: jsUrl, newWin:false, title:title, text:l.text, noPopup:l.noPopup, onclick: onClick});
}
function specialLink(l) {
// properties: article, specialpage, text, sep
if (typeof l.specialpage=='undefined'||!l.specialpage) return null;
var base = pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId]+':'+l.specialpage;
if (typeof l.sep == 'undefined' || l.sep === null) l.sep='&target=';
var article=l.article.urlString({keepSpaces: l.specialpage=='Search'});
var hint=popupString(l.specialpage+'Hint');
switch (l.specialpage) {
case 'Log':
switch (l.sep) {
case '&user=': hint=popupString('userLogHint'); break;
case '&type=block&page=': hint=popupString('blockLogHint'); break;
case '&page=': hint=popupString('pageLogHint'); break;
case '&type=protect&page=': hint=popupString('protectLogHint'); break;
case '&type=delete&page=': hint=popupString('deleteLogHint'); break;
default: log('Unknown log type, sep=' + l.sep); hint='Missing hint (FIXME)';
}
break;
case 'PrefixIndex': article += '/'; break;
}
if (hint) hint = simplePrintf(hint, [safeDecodeURI(l.article)]);
else hint = safeDecodeURI(l.specialpage+':'+l.article) ;
var url = base + l.sep + article;
return generalNavLink({url: url, title: hint, text: l.text, newWin:l.newWin, noPopup:l.noPopup});
}
function generalLink(l) {
// l.url, l.text, l.title, l.newWin, l.className, l.noPopup, l.onclick
if (typeof l.url=='undefined') return null;
// only quotation marks in the url can screw us up now... I think
var url=l.url.split('"').join('%22');
var ret='<a href="' + url + '"';
if (typeof l.title!='undefined' && l.title) { ret += ' title="' + pg.escapeQuotesHTML(l.title) + '"'; }
if (typeof l.onclick!='undefined' && l.onclick) { ret += ' onclick="' + pg.escapeQuotesHTML(l.onclick) + '"'; }
if (l.noPopup) { ret += ' noPopup=1'; }
var newWin;
if (typeof l.newWin=='undefined' || l.newWin === null) { newWin=getValueOf('popupNewWindows'); }
else { newWin=l.newWin; }
if (newWin) { ret += ' target="_blank"'; }
if (typeof l.className!='undefined'&&l.className) { ret+=' class="'+l.className+'"'; }
ret += '>';
if (typeof l.text==typeof '') { ret+= l.text; }
ret +='</a>';
return ret;
}
function appendParamsToLink(linkstr, params) {
var sp=linkstr.parenSplit(RegExp('(href="[^"]+?)"', 'i'));
if (sp.length<2) return null;
var ret=sp.shift() + sp.shift();
ret += '&' + params + '"';
ret += sp.join('');
return ret;
}
function changeLinkTargetLink(x) { // newTarget, text, hint, summary, clickButton, minor, title (optional), alsoChangeLabel {
if (x.newTarget) {
log ('changeLinkTargetLink: newTarget=' + x.newTarget);
}
if (x.oldTarget !== decodeURIComponent( x.oldTarget ) ) {
log ('This might be an input problem: ' + x.oldTarget );
}
// FIXME: first character of page title as well as namespace should be case insensitive
// eg [[category:X1]] and [[Category:X1]] are equivalent
// this'll break if charAt(0) is nasty
var cA = mw.util.escapeRegExp(x.oldTarget);
var chs = cA.charAt(0).toUpperCase();
chs='['+chs + chs.toLowerCase()+']';
var currentArticleRegexBit=chs+cA.substring(1);
currentArticleRegexBit=currentArticleRegexBit
.split(RegExp('(?:[_ ]+|%20)', 'g')).join('(?:[_ ]+|%20)')
.split('\\(').join('(?:%28|\\()')
.split('\\)').join('(?:%29|\\))'); // why does this need to match encoded strings ? links in the document ?
// leading and trailing space should be ignored, and anchor bits optional:
currentArticleRegexBit = '\\s*(' + currentArticleRegexBit + '(?:#[^\\[\\|]*)?)\\s*';
// e.g. Computer (archaic) -> \s*([Cc]omputer[_ ](?:%2528|\()archaic(?:%2528|\)))\s*
// autoedit=s~\[\[([Cc]ad)\]\]~[[Computer-aided%20design|$1]]~g;s~\[\[([Cc]AD)[|]~[[Computer-aided%20design|~g
var title=x.title || mw.config.get('wgPageName').split('_').join(' ');
var lk=titledWikiLink({article: new Title(title), newWin:x.newWin,
action: 'edit',
text: x.text,
title: x.hint,
className: 'popup_change_title_link'
});
var cmd='';
if (x.newTarget) {
// escape '&' and other nasties
var t = x.newTarget;
var s = mw.util.escapeRegExp(x.newTarget);
if (x.alsoChangeLabel) {
cmd += 's~\\[\\[' + currentArticleRegexBit + '\\]\\]~[[' + t + ']]~g;';
cmd += 's~\\[\\[' + currentArticleRegexBit + '[|]~[[' + t + '|~g;';
cmd += 's~\\[\\[' + s + '\\|' + s + '\\]\\]~[[' + t + ']]~g';
} else {
cmd += 's~\\[\\[' + currentArticleRegexBit + '\\]\\]~[[' + t + '|$1]]~g;';
cmd += 's~\\[\\[' + currentArticleRegexBit + '[|]~[[' + t + '|~g;';
cmd += 's~\\[\\[' + s + '\\|' + s + '\\]\\]~[[' + t + ']]~g';
}
} else {
cmd += 's~\\[\\['+currentArticleRegexBit+'\\]\\]~$1~g;';
cmd += 's~\\[\\['+currentArticleRegexBit+'[|](.*?)\\]\\]~$2~g';
}
// Build query
cmd = 'autoedit=' + encodeURIComponent ( cmd );
cmd += '&autoclick='+ encodeURIComponent( x.clickButton ) + '&actoken=' + encodeURIComponent( autoClickToken() );
cmd += ( x.minor === null ) ? '' : '&autominor='+ encodeURIComponent( x.minor );
cmd += ( x.watch === null ) ? '' : '&autowatch='+ encodeURIComponent( x.watch );
cmd += '&autosummary='+encodeURIComponent(x.summary);
cmd += '&autoimpl='+encodeURIComponent( popupString('autoedit_version') );
return appendParamsToLink(lk, cmd);
}
function redirLink(redirMatch, article) {
// NB redirMatch is in wikiText
var ret='';
if (getValueOf('popupAppendRedirNavLinks') && getValueOf('popupNavLinks')) {
ret += '<hr />';
if (getValueOf('popupFixRedirs') && typeof autoEdit != 'undefined' && autoEdit) {
ret += popupString('Redirects to: (Fix ');
log('redirLink: newTarget=' + redirMatch);
ret += addPopupShortcut(changeLinkTargetLink({
newTarget: redirMatch,
text: popupString('target'),
hint: popupString('Fix this redirect, changing just the link target'),
summary: simplePrintf(getValueOf('popupFixRedirsSummary'),[article.toString(), redirMatch]),
oldTarget: article.toString(),
clickButton: getValueOf('popupRedirAutoClick'),
minor: true,
watch: getValueOf('popupWatchRedirredPages')
}), 'R');
ret += popupString(' or ');
ret += addPopupShortcut(changeLinkTargetLink({
newTarget: redirMatch,
text: popupString('target & label'),
hint: popupString('Fix this redirect, changing the link target and label'),
summary: simplePrintf(getValueOf('popupFixRedirsSummary'),[article.toString(), redirMatch]),
oldTarget: article.toString(),
clickButton: getValueOf('popupRedirAutoClick'),
minor: true,
watch: getValueOf('popupWatchRedirredPages'),
alsoChangeLabel: true
}), 'R');
ret += popupString(')');
}
else ret += popupString('Redirects') + popupString(' to ');
return ret;
}
else return '<br> ' + popupString('Redirects') + popupString(' to ') +
titledWikiLink({article: new Title().fromWikiText(redirMatch), action: 'view', /* FIXME: newWin */
text: safeDecodeURI(redirMatch), title: popupString('Bypass redirect')});
}
function arinLink(l) {
if (!saneLinkCheck(l)) { return null; }
if ( ! l.article.isIpUser() || ! pg.wiki.wikimedia) return null;
var uN=l.article.userName();
return generalNavLink({url:'http://ws.arin.net/cgi-bin/whois.pl?queryinput=' + encodeURIComponent(uN), newWin:l.newWin,
title: tprintf('Look up %s in ARIN whois database', [uN]),
text: l.text, noPopup:1});
}
function toolDbName(cookieStyle) {
var ret = mw.config.get('wgDBname');
if (!cookieStyle) { ret+= '_p'; }
return ret;
}
function saneLinkCheck(l) {
if (typeof l.article != typeof {} || typeof l.text != typeof '') { return false; }
return true;
}
function editCounterLink(l) {
if(!saneLinkCheck(l)) return null;
if (! pg.wiki.wikimedia) return null;
var uN=l.article.userName();
var tool=getValueOf('popupEditCounterTool');
var url;
var defaultToolUrl='//tools.wmflabs.org/supercount/index.php?user=$1&project=$2.$3';
switch(tool) {
case 'custom':
url=simplePrintf(getValueOf('popupEditCounterUrl'), [ encodeURIComponent(uN), toolDbName() ]);
break;
case 'soxred': // no longer available
case 'kate': // no longer available
case 'interiot':// no longer available
/* fall through */
case 'supercount':
default:
var theWiki=pg.wiki.hostname.split('.');
url=simplePrintf(defaultToolUrl, [ encodeURIComponent(uN), theWiki[0], theWiki[1] ]);
}
return generalNavLink({url:url, title: tprintf('editCounterLinkHint', [uN]),
newWin:l.newWin, text: l.text, noPopup:1});
}
function globalSearchLink(l) {
if(!saneLinkCheck(l)) return null;
var base='http://vs.aka-online.de/cgi-bin/globalwpsearch.pl?timeout=120&search=';
var article=l.article.urlString({keepSpaces:true});
return generalNavLink({url:base + article, newWin:l.newWin,
title: tprintf('globalSearchHint', [safeDecodeURI(l.article)]),
text: l.text, noPopup:1});
}
function googleLink(l) {
if(!saneLinkCheck(l)) return null;
var base='https://www.google.com/search?q=';
var article=l.article.urlString({keepSpaces:true});
return generalNavLink({url:base + '%22' + article + '%22', newWin:l.newWin,
title: tprintf('googleSearchHint', [safeDecodeURI(l.article)]),
text: l.text, noPopup:1});
}
function editorListLink(l) {
if(!saneLinkCheck(l)) return null;
var article= l.article.articleFromTalkPage() || l.article;
var url='https://xtools.wmflabs.org/articleinfo/' +
encodeURI( pg.wiki.hostname ) + '/' +
article.urlString() +
'?uselang=' + mw.config.get('wgUserLanguage');
return generalNavLink({url:url,
title: tprintf('editorListHint', [article]),
newWin:l.newWin, text: l.text, noPopup:1});
}
function generalNavLink(l) {
l.className = (l.className === null) ? 'popupNavLink' : l.className;
return generalLink(l);
}
//////////////////////////////////////////////////
// magic history links
//
function getHistoryInfo(wikipage, whatNext) {
log('getHistoryInfo');
getHistory(wikipage, whatNext ? function(d){whatNext(processHistory(d));} : processHistory);
}
// FIXME eliminate pg.idNumber ... how? :-(
function getHistory(wikipage, onComplete) {
log('getHistory');
var url = pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&prop=revisions&titles=' +
new Title(wikipage).urlString() + '&rvlimit=' + getValueOf('popupHistoryLimit');
log('getHistory: url='+url);
return startDownload(url, pg.idNumber+'history', onComplete);
}
function processHistory(download) {
var jsobj = getJsObj(download.data);
try {
var revisions = anyChild(jsobj.query.pages).revisions;
var edits=[];
for (var i=0; i<revisions.length; ++i) {
edits.push({ oldid: revisions[i].revid, editor: revisions[i].user });
}
log('processed ' + edits.length + ' edits');
return finishProcessHistory( edits, mw.config.get('wgUserName') );
} catch (someError) {
log('Something went wrong with JSON business');
return finishProcessHistory([]);
}
}
function finishProcessHistory(edits, userName) {
var histInfo={};
histInfo.edits=edits;
histInfo.userName=userName;
for (var i=0; i<edits.length; ++i) {
if (typeof histInfo.myLastEdit === 'undefined' && userName && edits[i].editor==userName) {
histInfo.myLastEdit={index: i, oldid: edits[i].oldid, previd: (i === 0 ? null : edits[i-1].oldid)};
}
if (typeof histInfo.firstNewEditor === 'undefined' && edits[i].editor != edits[0].editor) {
histInfo.firstNewEditor={index:i, oldid:edits[i].oldid, previd: (i === 0 ? null : edits[i-1].oldid)};
}
}
//pg.misc.historyInfo=histInfo;
return histInfo;
}
//</NOLITE>
// ENDFILE: links.js
// STARTFILE: options.js
//////////////////////////////////////////////////
// options
// check for existing value, else use default
function defaultize(x) {
if (pg.option[x]===null || typeof pg.option[x]=='undefined') {
if (typeof window[x] != 'undefined' ) pg.option[x]=window[x];
else pg.option[x]=pg.optionDefault[x];
}
}
function newOption(x, def) {
pg.optionDefault[x]=def;
}
function setDefault(x, def) {
return newOption(x, def);
}
function getValueOf(varName) {
defaultize(varName);
return pg.option[varName];
}
/*eslint-disable */
function useDefaultOptions() { // for testing
for (var p in pg.optionDefault) {
pg.option[p]=pg.optionDefault[p];
if (typeof window[p]!='undefined') { delete window[p]; }
}
}
/*eslint-enable */
function setOptions() {
// user-settable parameters and defaults
var userIsSysop = false;
if ( mw.config.get('wgUserGroups') ) {
for ( var g = 0; g < mw.config.get('wgUserGroups').length; ++g ) {
if ( mw.config.get('wgUserGroups')[g] == "sysop" )
userIsSysop = true;
}
}
// Basic options
newOption('popupDelay', 0.5);
newOption('popupHideDelay', 0.5);
newOption('simplePopups', false);
newOption('popupStructure', 'shortmenus'); // see later - default for popupStructure is 'original' if simplePopups is true
newOption('popupActionsMenu', true);
newOption('popupSetupMenu', true);
newOption('popupAdminLinks', userIsSysop);
newOption('popupShortcutKeys', false);
newOption('popupHistoricalLinks', true);
newOption('popupOnlyArticleLinks', true);
newOption('removeTitles', true);
newOption('popupMaxWidth', 350);
newOption('popupSimplifyMainLink', true);
newOption('popupAppendRedirNavLinks', true);
newOption('popupTocLinks', false);
newOption('popupSubpopups', true);
newOption('popupDragHandle', false /* 'popupTopLinks'*/);
newOption('popupLazyPreviews', true);
newOption('popupLazyDownloads', true);
newOption('popupAllDabsStubs', false);
newOption('popupDebugging', false);
newOption('popupActiveNavlinks', true);
newOption('popupModifier', false); // ctrl, shift, alt or meta
newOption('popupModifierAction', 'enable'); // or 'disable'
newOption('popupDraggable', true);
newOption('popupReview', false);
//<NOLITE>
// images
newOption('popupImages', true);
newOption('imagePopupsForImages', true);
newOption('popupNeverGetThumbs', false);
//newOption('popupImagesToggleSize', true);
newOption('popupThumbAction', 'imagepage'); //'sizetoggle');
newOption('popupImageSize', 60);
newOption('popupImageSizeLarge', 200);
// redirs, dabs, reversion
newOption('popupFixRedirs', false);
newOption('popupRedirAutoClick', 'wpDiff');
newOption('popupFixDabs', false);
newOption('popupDabsAutoClick', 'wpDiff');
newOption('popupRevertSummaryPrompt', false);
newOption('popupMinorReverts', false);
newOption('popupRedlinkRemoval', false);
newOption('popupRedlinkAutoClick', 'wpDiff');
newOption('popupWatchDisambiggedPages', null);
newOption('popupWatchRedirredPages', null);
newOption('popupDabWiktionary', 'last');
// navlinks
newOption('popupNavLinks', true);
newOption('popupNavLinkSeparator', ' ⋅ ');
newOption('popupLastEditLink', true);
newOption('popupEditCounterTool', 'supercount');
newOption('popupEditCounterUrl', '');
//</NOLITE>
// previews etc
newOption('popupPreviews', true);
newOption('popupSummaryData', true);
newOption('popupMaxPreviewSentences', 9);
newOption('popupMaxPreviewCharacters', 900);
newOption('popupLastModified', true);
newOption('popupPreviewKillTemplates', true);
newOption('popupPreviewRawTemplates', true);
newOption('popupPreviewFirstParOnly', false);
newOption('popupPreviewCutHeadings', false);
newOption('popupPreviewButton', false);
newOption('popupPreviewButtonEvent', 'click');
//<NOLITE>
// diffs
newOption('popupPreviewDiffs', true);
newOption('popupDiffMaxLines', 100);
newOption('popupDiffContextLines', 2);
newOption('popupDiffContextCharacters', 40);
newOption('popupDiffDates', true);
newOption('popupDiffDatePrinter', 'toLocaleString');
// edit summaries. God, these are ugly.
newOption('popupReviewedSummary', popupString('defaultpopupReviewedSummary') );
newOption('popupFixDabsSummary', popupString('defaultpopupFixDabsSummary') );
newOption('popupExtendedRevertSummary', popupString('defaultpopupExtendedRevertSummary') );
newOption('popupRevertSummary', popupString('defaultpopupRevertSummary') );
newOption('popupRevertToPreviousSummary', popupString('defaultpopupRevertToPreviousSummary') );
newOption('popupQueriedRevertSummary', popupString('defaultpopupQueriedRevertSummary') );
newOption('popupQueriedRevertToPreviousSummary', popupString('defaultpopupQueriedRevertToPreviousSummary') );
newOption('popupFixRedirsSummary', popupString('defaultpopupFixRedirsSummary') );
newOption('popupRedlinkSummary', popupString('defaultpopupRedlinkSummary') );
newOption('popupRmDabLinkSummary', popupString('defaultpopupRmDabLinkSummary') );
//</NOLITE>
// misc
newOption('popupHistoryLimit', 50);
//<NOLITE>
newOption('popupFilters', [popupFilterStubDetect, popupFilterDisambigDetect,
popupFilterPageSize, popupFilterCountLinks,
popupFilterCountImages, popupFilterCountCategories,
popupFilterLastModified]);
newOption('extraPopupFilters', []);
newOption('popupOnEditSelection', 'cursor');
newOption('popupPreviewHistory', true);
newOption('popupImageLinks', true);
newOption('popupCategoryMembers', true);
newOption('popupUserInfo', true);
newOption('popupHistoryPreviewLimit', 25);
newOption('popupContribsPreviewLimit',25);
newOption('popupRevDelUrl', '//en.wikipedia.org/wiki/Wikipedia:Revision_deletion');
newOption('popupShowGender', true);
//</NOLITE>
// new windows
newOption('popupNewWindows', false);
newOption('popupLinksNewWindow', {'lastContrib': true, 'sinceMe': true});
// regexps
newOption('popupDabRegexp', '(\\{\\{\\s*disambig(?!uation needed)|disambig(uation|)\\s*\\}\\}|disamb\\s*\\}\\}|dab\\s*\\}\\})|\\{\\{\\s*(((geo|hn|road?|school|number)dis)|[234][lc][acw]|(road|ship)index)(\\s*[|][^}]*)?\\s*[}][}]|is a .*disambiguation.*page');
newOption('popupAnchorRegexp', 'anchors?'); //how to identify an anchors template
newOption('popupStubRegexp', '(sect)?stub[}][}]|This .*-related article is a .*stub');
newOption('popupImageVarsRegexp', 'image|image_(?:file|skyline|name|flag|seal)|cover|badge|logo');
}
// ENDFILE: options.js
// STARTFILE: strings.js
//<NOLITE>
//////////////////////////////////////////////////
// Translatable strings
//////////////////////////////////////////////////
//
// See instructions at
// https://en.wikipedia.org/wiki/Wikipedia:Tools/Navigation_popups/Translation
pg.string = {
/////////////////////////////////////
// summary data, searching etc.
/////////////////////////////////////
'article': 'article',
'category': 'category',
'categories': 'categories',
'image': 'image',
'images': 'images',
'stub': 'stub',
'section stub': 'section stub',
'Empty page': 'Empty page',
'kB': 'kB',
'bytes': 'bytes',
'day': 'day',
'days': 'days',
'hour': 'hour',
'hours': 'hours',
'minute': 'minute',
'minutes': 'minutes',
'second': 'second',
'seconds': 'seconds',
'week': 'week',
'weeks': 'weeks',
'search': 'search',
'SearchHint': 'Find English Wikipedia articles containing %s',
'web': 'web',
'global': 'global',
'globalSearchHint': 'Search across Wikipedias in different languages for %s',
'googleSearchHint': 'Google for %s',
/////////////////////////////////////
// article-related actions and info
// (some actions also apply to user pages)
/////////////////////////////////////
'actions': 'actions', ///// view articles and view talk
'popupsMenu': 'popups',
'togglePreviewsHint': 'Toggle preview generation in popups on this page',
'enable previews': 'enable previews',
'disable previews': 'disable previews',
'toggle previews': 'toggle previews',
'show preview': 'show preview',
'reset': 'reset',
'more...': 'more...',
'disable': 'disable popups',
'disablePopupsHint': 'Disable popups on this page. Reload page to re-enable.',
'historyfeedHint': 'RSS feed of recent changes to this page',
'purgePopupsHint': 'Reset popups, clearing all cached popup data.',
'PopupsHint': 'Reset popups, clearing all cached popup data.',
'spacebar': 'space',
'view': 'view',
'view article': 'view article',
'viewHint': 'Go to %s',
'talk': 'talk',
'talk page': 'talk page',
'this revision': 'this revision',
'revision %s of %s': 'revision %s of %s',
'Revision %s of %s': 'Revision %s of %s',
'the revision prior to revision %s of %s': 'the revision prior to revision %s of %s',
'Toggle image size': 'Click to toggle image size',
'del': 'del', ///// delete, protect, move
'delete': 'delete',
'deleteHint': 'Delete %s',
'undeleteShort': 'un',
'UndeleteHint': 'Show the deletion history for %s',
'protect': 'protect',
'protectHint': 'Restrict editing rights to %s',
'unprotectShort': 'un',
'unprotectHint': 'Allow %s to be edited by anyone again',
'send thanks': 'send thanks',
'ThanksHint': 'Send a thank you notification to this user',
'move': 'move',
'move page': 'move page',
'MovepageHint': 'Change the title of %s',
'edit': 'edit', ///// edit articles and talk
'edit article': 'edit article',
'editHint': 'Change the content of %s',
'edit talk': 'edit talk',
'new': 'new',
'new topic': 'new topic',
'newSectionHint': 'Start a new section on %s',
'null edit': 'null edit',
'nullEditHint': 'Submit an edit to %s, making no changes ',
'hist': 'hist', ///// history, diffs, editors, related
'history': 'history',
'historyHint': 'List the changes made to %s',
'last': 'prev', // For labelling the previous revision in history pages; the key is "last" for backwards compatibility
'lastEdit': 'lastEdit',
'mark patrolled': 'mark patrolled',
'markpatrolledHint': 'Mark this edit as patrolled',
'Could not marked this edit as patrolled': 'Could not marked this edit as patrolled',
'show last edit': 'most recent edit',
'Show the last edit': 'Show the effects of the most recent change',
'lastContrib': 'lastContrib',
'last set of edits': 'latest edits',
'lastContribHint': 'Show the net effect of changes made by the last editor',
'cur': 'cur',
'diffCur': 'diffCur',
'Show changes since revision %s': 'Show changes since revision %s',
'%s old': '%s old', // as in 4 weeks old
'oldEdit': 'oldEdit',
'purge': 'purge',
'purgeHint': 'Demand a fresh copy of %s',
'raw': 'source',
'rawHint': 'Download the source of %s',
'render': 'simple',
'renderHint': 'Show a plain HTML version of %s',
'Show the edit made to get revision': 'Show the edit made to get revision',
'sinceMe': 'sinceMe',
'changes since mine': 'diff my edit',
'sinceMeHint': 'Show changes since my last edit',
'Couldn\'t find an edit by %s\nin the last %s edits to\n%s': 'Couldn\'t find an edit by %s\nin the last %s edits to\n%s',
'eds': 'eds',
'editors': 'editors',
'editorListHint': 'List the users who have edited %s',
'related': 'related',
'relatedChanges': 'relatedChanges',
'related changes': 'related changes',
'RecentchangeslinkedHint': 'Show changes in articles related to %s',
'editOld': 'editOld', ///// edit old version, or revert
'rv': 'rv',
'revert': 'revert',
'revertHint': 'Revert to %s',
'defaultpopupReviewedSummary': 'Accepted by reviewing the [[Special:diff/%s/%s|difference]] between this version and previously accepted version using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRedlinkSummary': 'Removing link to empty page [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupFixDabsSummary': 'Disambiguate [[%s]] to [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupFixRedirsSummary': 'Redirect bypass from [[%s]] to [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupExtendedRevertSummary': 'Revert to revision dated %s by %s, oldid %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRevertToPreviousSummary': 'Revert to the revision prior to revision %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRevertSummary': 'Revert to revision %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupQueriedRevertToPreviousSummary': 'Revert to the revision prior to revision $1 dated $2 by $3 using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupQueriedRevertSummary': 'Revert to revision $1 dated $2 by $3 using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRmDabLinkSummary': 'Remove link to dab page [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'Redirects': 'Redirects', // as in Redirects to ...
' to ': ' to ', // as in Redirects to ...
'Bypass redirect': 'Bypass redirect',
'Fix this redirect': 'Fix this redirect',
'disambig': 'disambig', ///// add or remove dab etc.
'disambigHint': 'Disambiguate this link to [[%s]]',
'Click to disambiguate this link to:': 'Click to disambiguate this link to:',
'remove this link': 'remove this link',
'remove all links to this page from this article': 'remove all links to this page from this article',
'remove all links to this disambig page from this article': 'remove all links to this disambig page from this article',
'mainlink': 'mainlink', ///// links, watch, unwatch
'wikiLink': 'wikiLink',
'wikiLinks': 'wikiLinks',
'links here': 'links here',
'whatLinksHere': 'whatLinksHere',
'what links here': 'what links here',
'WhatlinkshereHint': 'List the pages that are hyperlinked to %s',
'unwatchShort': 'un',
'watchThingy': 'watch', // called watchThingy because {}.watch is a function
'watchHint': 'Add %s to my watchlist',
'unwatchHint': 'Remove %s from my watchlist',
'Only found one editor: %s made %s edits': 'Only found one editor: %s made %s edits',
'%s seems to be the last editor to the page %s': '%s seems to be the last editor to the page %s',
'rss': 'rss',
/////////////////////////////////////
// diff previews
/////////////////////////////////////
'Diff truncated for performance reasons': 'Diff truncated for performance reasons',
'Old revision': 'Old revision',
'New revision': 'New revision',
'Something went wrong :-(': 'Something went wrong :-(',
'Empty revision, maybe non-existent': 'Empty revision, maybe non-existent',
'Unknown date': 'Unknown date',
/////////////////////////////////////
// other special previews
/////////////////////////////////////
'Empty category': 'Empty category',
'Category members (%s shown)': 'Category members (%s shown)',
'No image links found': 'No image links found',
'File links': 'File links',
'No image found': 'No image found',
'Image from Commons': 'Image from Commons',
'Description page': 'Description page',
'Alt text:': 'Alt text:',
'revdel':'Hidden revision',
/////////////////////////////////////
// user-related actions and info
/////////////////////////////////////
'user': 'user', ///// user page, talk, email, space
'user page': 'user page',
'user talk': 'user talk',
'edit user talk': 'edit user talk',
'leave comment': 'leave comment',
'email': 'email',
'email user': 'email user',
'EmailuserHint': 'Send an email to %s',
'space': 'space', // short form for userSpace link
'PrefixIndexHint': 'Show pages in the userspace of %s',
'count': 'count', ///// contributions, log
'edit counter': 'edit counter',
'editCounterLinkHint': 'Count the contributions made by %s',
'contribs': 'contribs',
'contributions': 'contributions',
'deletedContribs': 'deleted contributions',
'DeletedcontributionsHint': 'List deleted edits made by %s',
'ContributionsHint': 'List the contributions made by %s',
'log': 'log',
'user log': 'user log',
'userLogHint': 'Show %s\'s user log',
'arin': 'ARIN lookup', ///// ARIN lookup, block user or IP
'Look up %s in ARIN whois database': 'Look up %s in the ARIN whois database',
'unblockShort': 'un',
'block': 'block',
'block user': 'block user',
'IpblocklistHint': 'Unblock %s',
'BlockipHint': 'Prevent %s from editing',
'block log': 'block log',
'blockLogHint': 'Show the block log for %s',
'protectLogHint': 'Show the protection log for %s',
'pageLogHint': 'Show the page log for %s',
'deleteLogHint': 'Show the deletion log for %s',
'Invalid %s %s': 'The option %s is invalid: %s',
'No backlinks found': 'No backlinks found',
' and more': ' and more',
'undo': 'undo',
'undoHint': 'undo this edit',
'Download preview data': 'Download preview data',
'Invalid or IP user': 'Invalid or IP user',
'Not a registered username': 'Not a registered username',
'BLOCKED': 'BLOCKED',
'Has blocks': 'Has blocks',
' edits since: ': ' edits since: ',
'last edit on ': 'last edit on ',
/////////////////////////////////////
// Autoediting
/////////////////////////////////////
'Enter a non-empty edit summary or press cancel to abort': 'Enter a non-empty edit summary or press cancel to abort',
'Failed to get revision information, please edit manually.\n\n': 'Failed to get revision information, please edit manually.\n\n',
'The %s button has been automatically clicked. Please wait for the next page to load.': 'The %s button has been automatically clicked. Please wait for the next page to load.',
'Could not find button %s. Please check the settings in your javascript file.': 'Could not find button %s. Please check the settings in your javascript file.',
/////////////////////////////////////
// Popups setup
/////////////////////////////////////
'Open full-size image': 'Open full-size image',
'zxy': 'zxy',
'autoedit_version': 'np20140416'
};
function popupString(str) {
if (typeof popupStrings != 'undefined' && popupStrings && popupStrings[str]) { return popupStrings[str]; }
if (pg.string[str]) { return pg.string[str]; }
return str;
}
function tprintf(str,subs) {
if (typeof subs != typeof []) { subs = [subs]; }
return simplePrintf(popupString(str), subs);
}
//</NOLITE>
// ENDFILE: strings.js
// STARTFILE: run.js
////////////////////////////////////////////////////////////////////
// Run things
////////////////////////////////////////////////////////////////////
// For some reason popups requires a fully loaded page jQuery.ready(...) causes problems for some.
// The old addOnloadHook did something similar to the below
if (document.readyState=="complete")
autoEdit(); //will setup popups
else
$( window ).on( 'load', autoEdit );
// Support for MediaWiki's live preview, VisualEditor's saves and Echo's flyout.
( function () {
var once = true;
function dynamicContentHandler( $content ) {
// Try to detect the hook fired on initial page load and disregard
// it, we already hook to onload (possibly to different parts of
// page - it's configurable) and running twice might be bad. Ugly…
if ( $content.attr( 'id' ) == 'mw-content-text' ) {
if ( once ) {
once = false;
return;
}
}
function registerHooksForVisibleNavpops () {
for (var i=0; pg.current.links && i<pg.current.links.length; ++i) {
var navpop = pg.current.links[i].navpopup;
if (!navpop || !navpop.isVisible()) { continue; }
Navpopup.tracker.addHook(posCheckerHook(navpop));
}
}
function doIt () {
registerHooksForVisibleNavpops();
$content.each( function () {
this.ranSetupTooltipsAlready = false;
setupTooltips( this );
} );
}
setupPopups( doIt );
}
// This hook is also fired after page load.
mw.hook( 'wikipage.content' ).add( dynamicContentHandler );
mw.hook( 'ext.echo.overlay.beforeShowingOverlay' ).add( function($overlay){
dynamicContentHandler( $overlay.find(".mw-echo-state") );
});
} )();
// Forked from [[User:Connel MacKenzie/mess-with-popups.js]]
Previewmaker.prototype.firstBit = function() {
try{
if (mw.config.get("wgNamespaceNumber") !== 0){
// dont't be givin' me no subsequent paragraphs, you hear me?
/// first we "normalize" section headings, removing whitespace after, adding before
var d = this.data;
if (getValueOf('popupPreviewCutHeadings')) {
this.data = this.data.replace(RegExp('\\s*(==+[^=]*==+)\\s*', 'g'), '\n\n$1 ');
/// then we want to get rid of paragraph breaks whose text ends badly
this.data = this.data.replace(RegExp('([:;]) *\\n{2,}', 'g'), '$1\n');
this.data = this.data.replace(RegExp('^[\\s\\n]*'), '');
stuff = (RegExp('^([^\\n]|\\n[^\\n\\s])*')).exec(this.data);
if (stuff) {
d = stuff[0];
}
if (!getValueOf('popupPreviewFirstParOnly')) {
d = this.data;
}
/// now put \n\n after sections so that bullets and numbered lists work
d = d.replace(RegExp('(==+[^=]*==+)\\s*', 'g'), '$1\n\n');
}
// superfluous sentences are RIGHT OUT.
// note: exactly 1 set of parens here needed to make the slice work
d = d.parenSplit(RegExp('([!?.]+["' + "'" + ']*\\s)', 'g'));
// leading space is bad, mmkay?
d[0] = d[0].replace(RegExp('^\\s*'), '');
var notSentenceEnds = RegExp('([^.][a-z][.][a-z]|etc|sic|Dr|Mr|Mrs|Ms|St|no|\\[[^\\]]*|\\s[A-Zvclm])$', 'i');
d = this.fixSentenceEnds(d, notSentenceEnds);
var maxChars=getValueOf('popupMaxPreviewCharacters');
var n = getValueOf('popupMaxPreviewSentences');
var dd;
do {
dd = this.firstSentences(d, n);
--n;
}
while (dd.length > maxChars && n > 0);
this.data = dd;
} else {
//TODO: Limit this override to urls that start with http://*.wiktionary.org/
//TODO: extend the above section to also preview Wikipedia links
var charsWikt = 0;
var linesWikt = this.data.split('\n');
for (jWikt = 0; jWikt < linesWikt.length; jWikt++) {
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{[in]transitive}}/g, '');
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{[un]countable}}/g, '');
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{cattag\|(.*?)}}/g, "");
linesWikt[jWikt] = linesWikt[jWikt].replace(/[\{\}]/g, "'");
if (linesWikt[jWikt].search(/^[#]/) == -1) {
if (linesWikt[jWikt].search(/^==[A-Z]/) == -1) {
linesWikt[jWikt] = '';
}
}
charsWikt += linesWikt[jWikt].length;
}
if (charsWikt < 2) {
linesWikt[0] = "No properly formatted content";
}
var maxChars = getValueOf('popupMaxPreviewCharacters');
var n = getValueOf('popupMaxPreviewSentences');
var dd = '';
for (jWikt = 0; jWikt < linesWikt.length; jWikt++) {
if (linesWikt[jWikt] !== '') {
dd += linesWikt[jWikt] + '\n';
}
if (dd.length < maxChars) {
this.data = dd;
}
}
}
}
catch (e) {
console.log("error from mess with popups: ", e);
}
};
});
orsd53nf3vg9wb097lspoxfv57tbbij
88927
88926
2026-08-06T23:13:52Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-popups.js]]
88926
javascript
text/javascript
// {{documentation}}
// _________________________________________________________________________________________
// | |
// | === WARNING: GLOBAL GADGET FILE === |
// | Changes to this page affect many users. |
// | Please discuss changes on the [[Wiktionary:Grease Pit]] before editing. |
// |_________________________________________________________________________________________|
mw.loader.load('//en.wikipedia.org/w/index.php?action=raw&title=MediaWiki:Gadget-navpop.css&ctype=text/css', "text/css");
// Forked from [[:w:MediaWiki:Gadget-popups.js]]
$(function () {
//////////////////////////////////////////////////
// Globals
//
// Trying to shove as many of these as possible into the pg (popup globals) object
var pg = {
re: {}, // regexps
ns: {}, // namespaces
string: {}, // translatable strings
wiki: {}, // local site info
user: {}, // current user info
misc: {}, // YUCK PHOOEY
option: {}, // options, see newOption etc
optionDefault: {}, // default option values
flag: {}, // misc flags
cache: {}, // page and image cache
structures: {}, // navlink structures
timer: {}, // all sorts of timers (too damn many)
counter: {}, // .. and all sorts of counters
current: {}, // state info
fn: {}, // functions
endoflist: null
};
/* Bail if the gadget/script is being loaded twice */
if( window.pg ) {
return;
}
/* Export to global context */
window.pg = pg;
/// Local Variables: ///
/// mode:c ///
/// End: ///
// ENDFILE: main.js
// STARTFILE: actions.js
function setupTooltips(container, remove, force, popData) {
log('setupTooltips, container='+container+', remove='+remove);
if (!container) {
//<NOLITE>
// the main initial call
if (getValueOf('popupOnEditSelection') && document && document.editform && document.editform.wpTextbox1) {
document.editform.wpTextbox1.onmouseup=doSelectionPopup;
}
//</NOLITE>
// article/content is a structure-dependent thing
container = defaultPopupsContainer();
}
if (!remove && !force && container.ranSetupTooltipsAlready) { return; }
container.ranSetupTooltipsAlready = !remove;
var anchors;
anchors=container.getElementsByTagName('A');
setupTooltipsLoop(anchors, 0, 250, 100, remove, popData);
}
function defaultPopupsContainer() {
if (getValueOf('popupOnlyArticleLinks')) {
return document.getElementById('mw_content') ||
document.getElementById('content') ||
document.getElementById('article') || document;
}
return document;
}
function setupTooltipsLoop(anchors,begin,howmany,sleep, remove, popData) {
log(simplePrintf('setupTooltipsLoop(%s,%s,%s,%s,%s)', arguments));
var finish=begin+howmany;
var loopend = Math.min(finish, anchors.length);
var j=loopend - begin;
log ('setupTooltips: anchors.length=' + anchors.length + ', begin=' + begin +
', howmany=' + howmany + ', loopend=' + loopend + ', remove=' + remove);
var doTooltip= remove ? removeTooltip : addTooltip;
// try a faster (?) loop construct
if (j > 0) {
do {
var a=anchors[loopend - j];
if (typeof a==='undefined' || !a || !a.href) {
log('got null anchor at index ' + loopend - j);
continue;
}
doTooltip(a, popData);
} while (--j);
}
if (finish < anchors.length) {
setTimeout(function() {
setupTooltipsLoop(anchors,finish,howmany,sleep,remove,popData);},
sleep);
} else {
if ( !remove && ! getValueOf('popupTocLinks')) { rmTocTooltips(); }
pg.flag.finishedLoading=true;
}
}
// eliminate popups from the TOC
// This also kills any onclick stuff that used to be going on in the toc
function rmTocTooltips() {
var toc=document.getElementById('toc');
if (toc) {
var tocLinks=toc.getElementsByTagName('A');
var tocLen = tocLinks.length;
for (var j=0; j<tocLen; ++j) {
removeTooltip(tocLinks[j], true);
}
}
}
function addTooltip(a, popData) {
if ( !isPopupLink(a) ) { return; }
a.onmouseover=mouseOverWikiLink;
a.onmouseout= mouseOutWikiLink;
a.onmousedown = killPopup;
a.hasPopup = true;
a.popData = popData;
}
function removeTooltip(a) {
if ( !a.hasPopup ) { return; }
a.onmouseover = null;
a.onmouseout = null;
if (a.originalTitle) { a.title = a.originalTitle; }
a.hasPopup=false;
}
function removeTitle(a) {
if (!a.originalTitle) {
a.originalTitle=a.title;
}
a.title='';
}
function restoreTitle(a) {
if ( a.title || !a.originalTitle ) { return; }
a.title = a.originalTitle;
}
function registerHooks(np) {
var popupMaxWidth=getValueOf('popupMaxWidth');
if (typeof popupMaxWidth === 'number') {
var setMaxWidth = function () {
np.mainDiv.style.maxWidth = popupMaxWidth + 'px';
np.maxWidth = popupMaxWidth;
};
np.addHook(setMaxWidth, 'unhide', 'before');
}
//<NOLITE>
np.addHook(addPopupShortcuts, 'unhide', 'after');
np.addHook(rmPopupShortcuts, 'hide', 'before');
//</NOLITE>
}
function removeModifierKeyHandler(a) {
//remove listeners for modifier key if any that were added in mouseOverWikiLink
document.removeEventListener('keydown', a.modifierKeyHandler, false);
document.removeEventListener('keyup', a.modifierKeyHandler, false);
}
function mouseOverWikiLink(evt) {
if (!evt && window.event) {evt=window.event;}
// if the modifier is needed, listen for it,
// we will remove the listener when we mouseout of this link or kill popup.
if (getValueOf('popupModifier')) {
// if popupModifierAction = enable, we should popup when the modifier is pressed
// if popupModifierAction = disable, we should popup unless the modifier is pressed
var action = getValueOf('popupModifierAction');
var key = action=='disable' ? 'keyup' : 'keydown';
var a = this;
a.modifierKeyHandler = function(evt) {
mouseOverWikiLink2(a, evt);
};
document.addEventListener(key, a.modifierKeyHandler, false);
}
return mouseOverWikiLink2(this, evt);
}
/**
* Gets the references list item that the provided footnote link targets. This
* is typically a li element within the ol.references element inside the reflist.
* @param {Element} a - A footnote link.
* @returns {Element|boolean} The targeted element, or false if one can't be found.
*/
function footnoteTarget(a) {
var aTitle=Title.fromAnchor(a);
// We want ".3A" rather than "%3A" or "?" here, so use the anchor property directly
var anch = aTitle.anchor;
if ( ! /^(cite_note-|_note-|endnote)/.test(anch) ) { return false; }
var lTitle=Title.fromURL(location.href);
if ( lTitle.toString(true) !== aTitle.toString(true) ) { return false; }
var el=document.getElementById(anch);
while ( el && typeof el.nodeName === 'string') {
var nt = el.nodeName.toLowerCase();
if ( nt === 'li' ) { return el; }
else if ( nt === 'body' ) { return false; }
else if ( el.parentNode ) { el=el.parentNode; }
else { return false; }
}
return false;
}
function footnotePreview(x, navpop) {
setPopupHTML('<hr />' + x.innerHTML, 'popupPreview', navpop.idNumber);
}
function modifierPressed(evt) {
var mod=getValueOf('popupModifier');
if (!mod) { return false; }
if (!evt && window.event) {evt=window.event;}
return ( evt && mod && evt[mod.toLowerCase() + 'Key'] );
}
// Checks if the correct modifier pressed/unpressed if needed
function isCorrectModifier(a,evt) {
if (!getValueOf('popupModifier')) { return true; }
// if popupModifierAction = enable, we should popup when the modifier is pressed
// if popupModifierAction = disable, we should popup unless the modifier is pressed
var action = getValueOf('popupModifierAction');
return ( action == 'enable' && modifierPressed(evt) ||
action == 'disable' && !modifierPressed(evt) );
}
function mouseOverWikiLink2(a, evt) {
if (!isCorrectModifier(a,evt)) { return; }
if ( getValueOf('removeTitles') ) { removeTitle(a); }
if ( a==pg.current.link && a.navpopup && a.navpopup.isVisible() ) { return; }
pg.current.link=a;
if (getValueOf('simplePopups') && !pg.option.popupStructure) {
// reset *default value* of popupStructure
setDefault('popupStructure', 'original');
}
var article=(new Title()).fromAnchor(a);
// set global variable (ugh) to hold article (wikipage)
pg.current.article = article;
if (!a.navpopup) {
a.navpopup=newNavpopup(a, article);
pg.current.linksHash[a.href] = a.navpopup;
pg.current.links.push(a);
}
if (a.navpopup.pending === null || a.navpopup.pending !== 0) {
// either fresh popups or those with unfinshed business are redone from scratch
simplePopupContent(a, article);
}
a.navpopup.showSoonIfStable(a.navpopup.delay);
clearInterval(pg.timer.checkPopupPosition);
pg.timer.checkPopupPosition=setInterval(checkPopupPosition, 600);
if(getValueOf('simplePopups')) {
if (getValueOf('popupPreviewButton') && !a.simpleNoMore) {
var d=document.createElement('div');
d.className='popupPreviewButtonDiv';
var s=document.createElement('span');
d.appendChild(s);
s.className='popupPreviewButton';
s['on' + getValueOf('popupPreviewButtonEvent')] = function() {
a.simpleNoMore=true;
d.style.display = "none";
nonsimplePopupContent(a,article);
};
s.innerHTML=popupString('show preview');
setPopupHTML(d, 'popupPreview', a.navpopup.idNumber);
}
}
if (a.navpopup.pending !== 0 ) {
nonsimplePopupContent(a, article);
}
}
// simplePopupContent: the content that do not require additional download
// (it is shown even when simplePopups is true)
function simplePopupContent(a, article) {
/* FIXME hack */ a.navpopup.hasPopupMenu=false;
a.navpopup.setInnerHTML(popupHTML(a));
fillEmptySpans({navpopup:a.navpopup});
if (getValueOf('popupDraggable'))
{
var dragHandle = getValueOf('popupDragHandle') || null;
if (dragHandle && dragHandle != 'all') {
dragHandle += a.navpopup.idNumber;
}
setTimeout(function(){a.navpopup.makeDraggable(dragHandle);}, 150);
}
//<NOLITE>
if (getValueOf('popupRedlinkRemoval') && a.className=='new') {
setPopupHTML('<br>'+popupRedlinkHTML(article), 'popupRedlink', a.navpopup.idNumber);
}
//</NOLITE>
}
function debugData(navpopup) {
if(getValueOf('popupDebugging') && navpopup.idNumber) {
setPopupHTML('idNumber='+navpopup.idNumber + ', pending=' + navpopup.pending,
'popupError', navpopup.idNumber);
}
}
function newNavpopup(a, article) {
var navpopup = new Navpopup();
navpopup.fuzz=5;
navpopup.delay=getValueOf('popupDelay')*1000;
// increment global counter now
navpopup.idNumber = ++pg.idNumber;
navpopup.parentAnchor = a;
navpopup.parentPopup = (a.popData && a.popData.owner);
navpopup.article = article;
registerHooks(navpopup);
return navpopup;
}
// Should we show nonsimple context?
// If simplePopups is set to true, then we do not show nonsimple context,
// but if a bottom "show preview" was clicked we do show nonsimple context
function shouldShowNonSimple(a) {
return !getValueOf('simplePopups') || a.simpleNoMore;
}
// Should we show nonsimple context govern by the option (e.g. popupUserInfo)?
// If the user explicitly asked for nonsimple context by setting the option to true,
// then we show it even in nonsimple mode.
function shouldShow(a,option) {
if (shouldShowNonSimple(a)) {
return getValueOf(option);
} else {
return (typeof window[option] != 'undefined' ) && window[option];
}
}
function nonsimplePopupContent(a, article) {
var diff=null, history=null;
var params=parseParams(a.href);
var oldid=(typeof params.oldid=='undefined' ? null : params.oldid);
//<NOLITE>
if(shouldShow(a,'popupPreviewDiffs')) {
diff=params.diff;
}
if(shouldShow(a,'popupPreviewHistory')) {
history=(params.action=='history');
}
//</NOLITE>
a.navpopup.pending=0;
var referenceElement = footnoteTarget(a);
if (referenceElement) {
footnotePreview(referenceElement, a.navpopup);
//<NOLITE>
} else if ( diff || diff === 0 ) {
loadDiff(article, oldid, diff, a.navpopup);
} else if ( history ) {
loadAPIPreview('history', article, a.navpopup);
} else if ( shouldShowNonSimple(a) && pg.re.contribs.test(a.href) ) {
loadAPIPreview('contribs', article, a.navpopup);
} else if ( shouldShowNonSimple(a) && pg.re.backlinks.test(a.href) ) {
loadAPIPreview('backlinks', article, a.navpopup);
} else if ( // FIXME should be able to get all preview combinations with options
article.namespaceId()==pg.nsImageId &&
( shouldShow(a,'imagePopupsForImages') || ! anchorContainsImage(a) )
) {
loadAPIPreview('imagepagepreview', article, a.navpopup);
loadImage(article, a.navpopup);
//</NOLITE>
} else {
if (article.namespaceId() == pg.nsCategoryId &&
shouldShow(a,'popupCategoryMembers')) {
loadAPIPreview('category', article, a.navpopup);
} else if ((article.namespaceId() == pg.nsUserId || article.namespaceId() == pg.nsUsertalkId) &&
shouldShow(a,'popupUserInfo')) {
loadAPIPreview('userinfo', article, a.navpopup);
}
if (shouldShowNonSimple(a)) startArticlePreview(article, oldid, a.navpopup);
}
}
function pendingNavpopTask(navpop) {
if (navpop && navpop.pending === null) { navpop.pending=0; }
++navpop.pending;
debugData(navpop);
}
function completedNavpopTask(navpop) {
if (navpop && navpop.pending) { --navpop.pending; }
debugData(navpop);
}
function startArticlePreview(article, oldid, navpop) {
navpop.redir=0;
loadPreview(article, oldid, navpop);
}
function loadPreview(article, oldid, navpop) {
if (!navpop.redir) { navpop.originalArticle=article; }
article.oldid = oldid;
loadAPIPreview('revision', article, navpop);
}
function loadPreviewFromRedir(redirMatch, navpop) {
// redirMatch is a regex match
var target = new Title().fromWikiText(redirMatch[2]);
// overwrite (or add) anchor from original target
// mediawiki does overwrite; eg [[User:Lupin/foo3#Done]]
if ( navpop.article.anchor ) { target.anchor = navpop.article.anchor; }
navpop.redir++;
navpop.redirTarget=target;
//<NOLITE>
var warnRedir = redirLink(target, navpop.article);
setPopupHTML(warnRedir, 'popupWarnRedir', navpop.idNumber);
//</NOLITE>
navpop.article=target;
fillEmptySpans({redir: true, redirTarget: target, navpopup:navpop});
return loadPreview(target, null, navpop);
}
function insertPreview(download) {
if (!download.owner) { return; }
var redirMatch = pg.re.redirect.exec(download.data);
if (download.owner.redir === 0 && redirMatch) {
loadPreviewFromRedir(redirMatch, download.owner);
return;
}
if (download.owner.visible || !getValueOf('popupLazyPreviews')) {
insertPreviewNow(download);
} else {
var id=(download.owner.redir) ? 'PREVIEW_REDIR_HOOK' : 'PREVIEW_HOOK';
download.owner.addHook( function(){insertPreviewNow(download); return true;},
'unhide', 'after', id );
}
}
function insertPreviewNow(download) {
if (!download.owner) { return; }
var wikiText=download.data;
var navpop=download.owner;
var art=navpop.redirTarget || navpop.originalArticle;
//<NOLITE>
makeFixDabs(wikiText, navpop);
if (getValueOf('popupSummaryData')) {
getPageInfo(wikiText, download);
setPopupTrailer(getPageInfo(wikiText, download), navpop.idNumber);
}
var imagePage='';
if (art.namespaceId()==pg.nsImageId) { imagePage=art.toString(); }
else { imagePage=getValidImageFromWikiText(wikiText); }
if(imagePage) { loadImage(Title.fromWikiText(imagePage), navpop); }
//</NOLITE>
if (getValueOf('popupPreviews')) { insertArticlePreview(download, art, navpop); }
}
function insertArticlePreview(download, art, navpop) {
if (download && typeof download.data == typeof ''){
if (art.namespaceId()==pg.nsTemplateId && getValueOf('popupPreviewRawTemplates')) {
// FIXME compare/consolidate with diff escaping code for wikitext
var h='<hr /><span style="font-family: monospace;">' + download.data.entify().split('\\n').join('<br />\\n') + '</span>';
setPopupHTML(h, 'popupPreview', navpop.idNumber);
}
else {
var p=prepPreviewmaker(download.data, art, navpop);
p.showPreview();
}
}
}
function prepPreviewmaker(data, article, navpop) {
// deal with tricksy anchors
var d=anchorize(data, article.anchorString());
var urlBase=joinPath([pg.wiki.articlebase, article.urlString()]);
var p=new Previewmaker(d, urlBase, navpop);
return p;
}
// Try to imitate the way mediawiki generates HTML anchors from section titles
function anchorize(d, anch) {
if (!anch) { return d; }
var anchRe=RegExp('(?:=+\\s*' + literalizeRegex(anch).replace(/[_ ]/g, '[_ ]') + '\\s*=+|\\{\\{\\s*'+getValueOf('popupAnchorRegexp')+'\\s*(?:\\|[^|}]*)*?\\s*'+literalizeRegex(anch)+'\\s*(?:\\|[^}]*)?}})');
var match=d.match(anchRe);
if(match && match.length > 0 && match[0]) { return d.substring(d.indexOf(match[0])); }
// now try to deal with == foo [[bar|baz]] boom == -> #foo_baz_boom
var lines=d.split('\n');
for (var i=0; i<lines.length; ++i) {
lines[i]=lines[i].replace(RegExp('[[]{2}([^|\\]]*?[|])?(.*?)[\\]]{2}', 'g'), '$2')
.replace(/'''([^'])/g, '$1').replace(RegExp("''([^'])", 'g'), '$1');
if (lines[i].match(anchRe)) {
return d.split('\n').slice(i).join('\n').replace(RegExp('^[^=]*'), '');
}
}
return d;
}
function killPopup() {
removeModifierKeyHandler(this);
if (getValueOf('popupShortcutKeys')) { rmPopupShortcuts(); }
if (!pg) { return; }
if (pg.current.link && pg.current.link.navpopup) { pg.current.link.navpopup.banish(); }
pg.current.link=null;
abortAllDownloads();
if (pg.timer.checkPopupPosition) {
clearInterval(pg.timer.checkPopupPosition);
pg.timer.checkPopupPosition=null;
}
return true; // preserve default action
}
// ENDFILE: actions.js
// STARTFILE: domdrag.js
/**
@fileoverview
The {@link Drag} object, which enables objects to be dragged around.
<pre>
*************************************************
dom-drag.js
09.25.2001
www.youngpup.net
**************************************************
10.28.2001 - fixed minor bug where events
sometimes fired off the handle, not the root.
*************************************************
Pared down, some hooks added by [[User:Lupin]]
Copyright Aaron Boodman.
Saying stupid things daily since March 2001.
</pre>
*/
/**
Creates a new Drag object. This is used to make various DOM elements draggable.
@constructor
*/
function Drag () {
/**
Condition to determine whether or not to drag. This function should take one parameter, an Event.
To disable this, set it to <code>null</code>.
@type Function
*/
this.startCondition = null;
/**
Hook to be run when the drag finishes. This is passed the final coordinates of
the dragged object (two integers, x and y). To disables this, set it to <code>null</code>.
@type Function
*/
this.endHook = null;
}
/**
Gets an event in a cross-browser manner.
@param {Event} e
@private
*/
Drag.prototype.fixE = function(e) {
if (typeof e == 'undefined') { e = window.event; }
if (typeof e.layerX == 'undefined') { e.layerX = e.offsetX; }
if (typeof e.layerY == 'undefined') { e.layerY = e.offsetY; }
return e;
};
/**
Initialises the Drag instance by telling it which object you want to be draggable, and what you want to drag it by.
@param {DOMElement} o The "handle" by which <code>oRoot</code> is dragged.
@param {DOMElement} oRoot The object which moves when <code>o</code> is dragged, or <code>o</code> if omitted.
*/
Drag.prototype.init = function(o, oRoot) {
var dragObj = this;
this.obj = o;
o.onmousedown = function(e) { dragObj.start.apply( dragObj, [e]); };
o.dragging = false;
o.popups_draggable = true;
o.hmode = true;
o.vmode = true;
o.root = oRoot ? oRoot : o ;
if (isNaN(parseInt(o.root.style.left, 10))) { o.root.style.left = "0px"; }
if (isNaN(parseInt(o.root.style.top, 10))) { o.root.style.top = "0px"; }
o.root.onthisStart = function(){};
o.root.onthisEnd = function(){};
o.root.onthis = function(){};
};
/**
Starts the drag.
@private
@param {Event} e
*/
Drag.prototype.start = function(e) {
var o = this.obj; // = this;
e = this.fixE(e);
if (this.startCondition && !this.startCondition(e)) { return; }
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom, 10);
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10);
o.root.onthisStart(x, y);
o.lastMouseX = e.clientX;
o.lastMouseY = e.clientY;
var dragObj = this;
o.onmousemoveDefault = document.onmousemove;
o.dragging = true;
document.onmousemove = function(e) { dragObj.drag.apply( dragObj, [e] ); };
document.onmouseup = function(e) { dragObj.end.apply( dragObj, [e] ); };
return false;
};
/**
Does the drag.
@param {Event} e
@private
*/
Drag.prototype.drag = function(e) {
e = this.fixE(e);
var o = this.obj;
var ey = e.clientY;
var ex = e.clientX;
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom, 10);
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10 );
var nx, ny;
nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));
this.obj.root.style[o.hmode ? "left" : "right"] = nx + "px";
this.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px";
this.obj.lastMouseX = ex;
this.obj.lastMouseY = ey;
this.obj.root.onthis(nx, ny);
return false;
};
/**
Ends the drag.
@private
*/
Drag.prototype.end = function() {
document.onmousemove=this.obj.onmousemoveDefault;
document.onmouseup = null;
this.obj.dragging = false;
if (this.endHook) {
this.endHook( parseInt(this.obj.root.style[this.obj.hmode ? "left" : "right"], 10),
parseInt(this.obj.root.style[this.obj.vmode ? "top" : "bottom"], 10));
}
};
// ENDFILE: domdrag.js
// STARTFILE: structures.js
//<NOLITE>
pg.structures.original={};
pg.structures.original.popupLayout=function () {
return ['popupError', 'popupImage', 'popupTopLinks', 'popupTitle',
'popupUserData', 'popupData', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks',
'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
pg.structures.original.popupRedirSpans=function () {
return ['popupRedir', 'popupWarnRedir', 'popupRedirTopLinks',
'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'];
};
pg.structures.original.popupTitle=function (x) {
log ('defaultstructure.popupTitle');
if (!getValueOf('popupNavLinks')) {
return navlinkStringToHTML('<b><<mainlink>></b>',x.article,x.params);
}
return '';
};
pg.structures.original.popupTopLinks=function (x) {
log ('defaultstructure.popupTopLinks');
if (getValueOf('popupNavLinks')) { return navLinksHTML(x.article, x.hint, x.params); }
return '';
};
pg.structures.original.popupImage=function(x) {
log ('original.popupImage, x.article='+x.article+', x.navpop.idNumber='+x.navpop.idNumber);
return imageHTML(x.article, x.navpop.idNumber);
};
pg.structures.original.popupRedirTitle=pg.structures.original.popupTitle;
pg.structures.original.popupRedirTopLinks=pg.structures.original.popupTopLinks;
function copyStructure(oldStructure, newStructure) {
pg.structures[newStructure]={};
for (var prop in pg.structures[oldStructure]) {
pg.structures[newStructure][prop]=pg.structures[oldStructure][prop];
}
}
copyStructure('original', 'nostalgia');
pg.structures.nostalgia.popupTopLinks=function(x) {
var str='';
str += '<b><<mainlink|shortcut= >></b>';
// user links
// contribs - log - count - email - block
// count only if applicable; block only if popupAdminLinks
str += 'if(user){<br><<contribs|shortcut=c>>';
str+='if(wikimedia){*<<count|shortcut=#>>}';
str+='if(ipuser){}else{*<<email|shortcut=E>>}if(admin){*<<block|shortcut=b>>}}';
// editing links
// talkpage -> edit|new - history - un|watch - article|edit
// other page -> edit - history - un|watch - talk|edit|new
var editstr='<<edit|shortcut=e>>';
var editOldidStr='if(oldid){<<editOld|shortcut=e>>|<<revert|shortcut=v|rv>>|<<edit|cur>>}else{' +
editstr + '}';
var historystr='<<history|shortcut=h>>';
var watchstr='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
str += '<br>if(talk){' +
editOldidStr+'|<<new|shortcut=+>>' + '*' + historystr+'*'+watchstr + '*' +
'<b><<article|shortcut=a>></b>|<<editArticle|edit>>' +
'}else{' + // not a talk page
editOldidStr + '*' + historystr + '*' + watchstr + '*' +
'<b><<talk|shortcut=t>></b>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>}';
// misc links
str += '<br><<whatLinksHere|shortcut=l>>*<<relatedChanges|shortcut=r>>';
str += 'if(admin){<br>}else{*}<<move|shortcut=m>>';
// admin links
str += 'if(admin){*<<unprotect|unprotectShort>>|<<protect|shortcut=p>>*' +
'<<undelete|undeleteShort>>|<<delete|shortcut=d>>}';
return navlinkStringToHTML(str, x.article, x.params);
};
pg.structures.nostalgia.popupRedirTopLinks=pg.structures.nostalgia.popupTopLinks;
/** -- fancy -- **/
copyStructure('original', 'fancy');
pg.structures.fancy.popupTitle=function (x) {
return navlinkStringToHTML('<font size=+0><<mainlink>></font>',x.article,x.params);
};
pg.structures.fancy.popupTopLinks=function(x) {
var hist='<<history|shortcut=h|hist>>|<<lastEdit|shortcut=/|last>>|<<editors|shortcut=E|eds>>';
var watch='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
var move='<<move|shortcut=m|move>>';
return navlinkStringToHTML('if(talk){' +
'<<edit|shortcut=e>>|<<new|shortcut=+|+>>*' + hist + '*' +
'<<article|shortcut=a>>|<<editArticle|edit>>' + '*' + watch + '*' + move +
'}else{<<edit|shortcut=e>>*' + hist +
'*<<talk|shortcut=t|>>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>' +
'*' + watch + '*' + move+'}<br>', x.article, x.params);
};
pg.structures.fancy.popupOtherLinks=function(x) {
var admin='<<unprotect|unprotectShort>>|<<protect|shortcut=p>>*<<undelete|undeleteShort>>|<<delete|shortcut=d|del>>';
var user='<<contribs|shortcut=c>>if(wikimedia){|<<count|shortcut=#|#>>}';
user+='if(ipuser){|<<arin>>}else{*<<email|shortcut=E|'+
popupString('email')+'>>}if(admin){*<<block|shortcut=b>>}';
var normal='<<whatLinksHere|shortcut=l|links here>>*<<relatedChanges|shortcut=r|related>>';
return navlinkStringToHTML('<br>if(user){' + user + '*}if(admin){'+admin+'if(user){<br>}else{*}}' + normal,
x.article, x.params);
};
pg.structures.fancy.popupRedirTitle=pg.structures.fancy.popupTitle;
pg.structures.fancy.popupRedirTopLinks=pg.structures.fancy.popupTopLinks;
pg.structures.fancy.popupRedirOtherLinks=pg.structures.fancy.popupOtherLinks;
/** -- fancy2 -- **/
// hack for [[User:MacGyverMagic]]
copyStructure('fancy', 'fancy2');
pg.structures.fancy2.popupTopLinks=function(x) { // hack out the <br> at the end and put one at the beginning
return '<br>'+pg.structures.fancy.popupTopLinks(x).replace(RegExp('<br>$','i'),'');
};
pg.structures.fancy2.popupLayout=function () { // move toplinks to after the title
return ['popupError', 'popupImage', 'popupTitle', 'popupUserData', 'popupData', 'popupTopLinks', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks', 'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
/** -- menus -- **/
copyStructure('original', 'menus');
pg.structures.menus.popupLayout=function () {
return ['popupError', 'popupImage', 'popupTopLinks', 'popupTitle', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks', 'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupUserData', 'popupData', 'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
pg.structures.menus.popupTopLinks = function (x, shorter) {
// FIXME maybe this stuff should be cached
var s=[];
var dropdiv='<div class="popup_drop">';
var enddiv='</div>';
var hist='<<history|shortcut=h>>';
if (!shorter) { hist = '<menurow>' + hist +
'|<<historyfeed|rss>>|<<editors|shortcut=E>></menurow>'; }
var lastedit='<<lastEdit|shortcut=/|show last edit>>';
var thank='if(diff){<<thank|send thanks>>}';
var jsHistory='<<lastContrib|last set of edits>><<sinceMe|changes since mine>>';
var linkshere='<<whatLinksHere|shortcut=l|what links here>>';
var related='<<relatedChanges|shortcut=r|related changes>>';
var search='<menurow><<search|shortcut=s>>if(wikimedia){|<<globalsearch|shortcut=g|global>>}' +
'|<<google|shortcut=G|web>></menurow>';
var watch='<menurow><<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>></menurow>';
var protect='<menurow><<unprotect|unprotectShort>>|' +
'<<protect|shortcut=p>>|<<protectlog|log>></menurow>';
var del='<menurow><<undelete|undeleteShort>>|<<delete|shortcut=d>>|' +
'<<deletelog|log>></menurow>';
var move='<<move|shortcut=m|move page>>';
var nullPurge='<menurow><<nullEdit|shortcut=n|null edit>>|<<purge|shortcut=P>></menurow>';
var viewOptions='<menurow><<view|shortcut=v>>|<<render|shortcut=S>>|<<raw>></menurow>';
var editRow='if(oldid){' +
'<menurow><<edit|shortcut=e>>|<<editOld|shortcut=e|this revision>></menurow>' +
'<menurow><<revert|shortcut=v>>|<<undo>></menurow>' + '}else{<<edit|shortcut=e>>}';
var markPatrolled='if(rcid){<<markpatrolled|mark patrolled>>}';
var newTopic='if(talk){<<new|shortcut=+|new topic>>}';
var protectDelete='if(admin){' + protect + del + '}';
if (getValueOf('popupActionsMenu')) {
s.push( '<<mainlink>>*' + dropdiv + menuTitle('actions'));
} else {
s.push( dropdiv + '<<mainlink>>');
}
s.push( '<menu>');
s.push( editRow + markPatrolled + newTopic + hist + lastedit + thank );
if (!shorter) { s.push(jsHistory); }
s.push( move + linkshere + related);
if (!shorter) { s.push(nullPurge + search); }
if (!shorter) { s.push(viewOptions); }
s.push('<hr />' + watch + protectDelete);
s.push('<hr />' +
'if(talk){<<article|shortcut=a|view article>><<editArticle|edit article>>}' +
'else{<<talk|shortcut=t|talk page>><<editTalk|edit talk>>' +
'<<newTalk|shortcut=+|new topic>>}</menu>' + enddiv);
// user menu starts here
var email='<<email|shortcut=E|email user>>';
var contribs= 'if(wikimedia){<menurow>}<<contribs|shortcut=c|contributions>>if(wikimedia){</menurow>}' +
'if(admin){<menurow><<deletedContribs>></menurow>}';
s.push('if(user){*' + dropdiv + menuTitle('user'));
s.push('<menu>');
s.push('<menurow><<userPage|shortcut=u|user page>>|<<userSpace|space>></menurow>');
s.push('<<userTalk|shortcut=t|user talk>><<editUserTalk|edit user talk>>' +
'<<newUserTalk|shortcut=+|leave comment>>');
if(!shorter) { s.push( 'if(ipuser){<<arin>>}else{' + email + '}' ); }
else { s.push( 'if(ipuser){}else{' + email + '}' ); }
s.push('<hr />' + contribs + '<<userlog|shortcut=L|user log>>');
s.push('if(wikimedia){<<count|shortcut=#|edit counter>>}');
s.push('if(admin){<menurow><<unblock|unblockShort>>|<<block|shortcut=b|block user>></menurow>}');
s.push('<<blocklog|shortcut=B|block log>>');
s.push('</menu>' + enddiv + '}');
// popups menu starts here
if (getValueOf('popupSetupMenu') && !x.navpop.hasPopupMenu /* FIXME: hack */) {
x.navpop.hasPopupMenu=true;
s.push('*' + dropdiv + menuTitle('popupsMenu') + '<menu>');
s.push('<<togglePreviews|toggle previews>>');
s.push('<<purgePopups|reset>>');
s.push('<<disablePopups|disable>>');
s.push('</menu>'+enddiv);
}
return navlinkStringToHTML(s.join(''), x.article, x.params);
};
function menuTitle(s) {
return '<a href="#" noPopup=1>' + popupString(s) + '</a>';
}
pg.structures.menus.popupRedirTitle=pg.structures.menus.popupTitle;
pg.structures.menus.popupRedirTopLinks=pg.structures.menus.popupTopLinks;
copyStructure('menus', 'shortmenus');
pg.structures.shortmenus.popupTopLinks=function(x) {
return pg.structures.menus.popupTopLinks(x,true);
};
pg.structures.shortmenus.popupRedirTopLinks=pg.structures.shortmenus.popupTopLinks;
//</NOLITE>
pg.structures.lite={};
pg.structures.lite.popupLayout=function () {
return ['popupTitle', 'popupPreview' ];
};
pg.structures.lite.popupTitle=function (x) {
log (x.article + ': structures.lite.popupTitle');
//return navlinkStringToHTML('<b><<mainlink>></b>',x.article,x.params);
return '<div><span class="popup_mainlink"><b>' + x.article.toString() + '</b></span></div>';
};
// ENDFILE: structures.js
// STARTFILE: autoedit.js
//<NOLITE>
function substitute(data,cmdBody) {
// alert('sub\nfrom: '+cmdBody.from+'\nto: '+cmdBody.to+'\nflags: '+cmdBody.flags);
var fromRe=RegExp(cmdBody.from, cmdBody.flags);
return data.replace(fromRe, cmdBody.to);
}
function execCmds(data, cmdList) {
for (var i=0; i<cmdList.length; ++i) {
data=cmdList[i].action(data, cmdList[i]);
}
return data;
}
function parseCmd(str) {
// returns a list of commands
if (!str.length) { return []; }
var p=false;
switch (str.charAt(0)) {
case 's':
p=parseSubstitute(str);
break;
default:
return false;
}
if (p) { return [p].concat(parseCmd(p.remainder)); }
return false;
}
function unEscape(str, sep) {
return str.split('\\\\').join('\\').split('\\'+sep).join(sep).split('\\n').join('\n');
}
function parseSubstitute(str) {
// takes a string like s/a/b/flags;othercmds and parses it
var from,to,flags,tmp;
if (str.length<4) { return false; }
var sep=str.charAt(1);
str=str.substring(2);
tmp=skipOver(str,sep);
if (tmp) { from=tmp.segment; str=tmp.remainder; }
else { return false; }
tmp=skipOver(str,sep);
if (tmp) { to=tmp.segment; str=tmp.remainder; }
else { return false; }
flags='';
if (str.length) {
tmp=skipOver(str,';') || skipToEnd(str, ';');
if (tmp) {flags=tmp.segment; str=tmp.remainder; }
}
return {action: substitute, from: from, to: to, flags: flags, remainder: str};
}
function skipOver(str,sep) {
var endSegment=findNext(str,sep);
if (endSegment<0) { return false; }
var segment=unEscape(str.substring(0,endSegment), sep);
return {segment: segment, remainder: str.substring(endSegment+1)};
}
/*eslint-disable*/
function skipToEnd(str,sep) {
return {segment: str, remainder: ''};
}
/*eslint-enable */
function findNext(str, ch) {
for (var i=0; i<str.length; ++i) {
if (str.charAt(i)=='\\') { i+=2; }
if (str.charAt(i)==ch) { return i; }
}
return -1;
}
function setCheckbox(param, box) {
var val=mw.util.getParamValue(param);
if (val) {
switch (val) {
case '1': case 'yes': case 'true':
box.checked=true;
break;
case '0': case 'no': case 'false':
box.checked=false;
}
}
}
function autoEdit() {
setupPopups( function () {
if (mw.util.getParamValue('autoimpl') !== popupString('autoedit_version') ) { return false; }
if (mw.util.getParamValue('autowatchlist') && mw.util.getParamValue('actoken')===autoClickToken()) {
pg.fn.modifyWatchlist(mw.util.getParamValue('title'), mw.util.getParamValue('action'));
}
if (!document.editform) { return false; }
if (autoEdit.alreadyRan) { return false; }
autoEdit.alreadyRan=true;
var cmdString=mw.util.getParamValue('autoedit');
if (cmdString) {
try {
var editbox=document.editform.wpTextbox1;
var cmdList=parseCmd(cmdString);
var input=editbox.value;
var output=execCmds(input, cmdList);
editbox.value=output;
} catch (dang) { return; }
// wikEd user script compatibility
if (typeof(wikEdUseWikEd) != 'undefined') {
if (wikEdUseWikEd === true) {
WikEdUpdateFrame();
}
}
}
setCheckbox('autominor', document.editform.wpMinoredit);
setCheckbox('autowatch', document.editform.wpWatchthis);
var rvid = mw.util.getParamValue('autorv');
if (rvid) {
var url=pg.wiki.apiwikibase + '?action=query&format=json&formatversion=2&prop=revisions&revids='+rvid;
startDownload(url, null, autoEdit2);
} else { autoEdit2(); }
} );
}
function autoEdit2(d) {
var summary=mw.util.getParamValue('autosummary');
var summaryprompt=mw.util.getParamValue('autosummaryprompt');
var summarynotice='';
if (d && d.data && mw.util.getParamValue('autorv')) {
var s = getRvSummary(summary, d.data);
if (s === false) {
summaryprompt=true;
summarynotice=popupString('Failed to get revision information, please edit manually.\n\n');
summary = simplePrintf(summary, [mw.util.getParamValue('autorv'), '(unknown)', '(unknown)']);
} else { summary = s; }
}
if (summaryprompt) {
var txt= summarynotice +
popupString('Enter a non-empty edit summary or press cancel to abort');
var response=prompt(txt, summary);
if (response) { summary=response; }
else { return; }
}
if (summary) { document.editform.wpSummary.value=summary; }
// Attempt to avoid possible premature clicking of the save button
// (maybe delays in updates to the DOM are to blame?? or a red herring)
setTimeout(autoEdit3, 100);
}
function autoClickToken() {
return mw.user.sessionId();
}
function autoEdit3() {
if( mw.util.getParamValue('actoken') != autoClickToken()) { return; }
var btn=mw.util.getParamValue('autoclick');
if (btn) {
if (document.editform && document.editform[btn]) {
var button=document.editform[btn];
var msg=tprintf('The %s button has been automatically clicked. Please wait for the next page to load.',
[ button.value ]);
bannerMessage(msg);
document.title='('+document.title+')';
button.click();
} else {
alert(tprintf('Could not find button %s. Please check the settings in your javascript file.',
[ btn ]));
}
}
}
function bannerMessage(s) {
var headings=document.getElementsByTagName('h1');
if (headings) {
var div=document.createElement('div');
div.innerHTML='<font size=+1><b>' + s + '</b></font>';
headings[0].parentNode.insertBefore(div, headings[0]);
}
}
function getRvSummary(template, json) {
try {
var o=getJsObj(json);
var edit = anyChild(o.query.pages).revisions[0];
var timestamp = edit.timestamp.split(/[A-Z]/g).join(' ').replace(/^ *| *$/g, '');
return simplePrintf(template, [edit.revid, timestamp, edit.userhidden ? '(hidden)' : edit.user ]);
} catch (badness) {
return false;
}
}
//</NOLITE>
// ENDFILE: autoedit.js
// STARTFILE: downloader.js
/**
@fileoverview
{@link Downloader}, a xmlhttprequest wrapper, and helper functions.
*/
/**
Creates a new Downloader
@constructor
@class The Downloader class. Create a new instance of this class to download stuff.
@param {String} url The url to download. This can be omitted and supplied later.
*/
function Downloader(url) {
if (typeof XMLHttpRequest!='undefined') { this.http = new XMLHttpRequest(); }
/**
The url to download
@type String
*/
this.url = url;
/**
A universally unique ID number
@type integer
*/
this.id=null;
/**
Modification date, to be culled from the incoming headers
@type Date
@private
*/
this.lastModified = null;
/**
What to do when the download completes successfully
@type Function
@private
*/
this.callbackFunction = null;
/**
What to do on failure
@type Function
@private
*/
this.onFailure = null;
/**
Flag set on <code>abort</code>
@type boolean
*/
this.aborted = false;
/**
HTTP method. See https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html for possibilities.
@type String
*/
this.method='GET';
/**
Async flag.
@type boolean
*/
this.async=true;
}
new Downloader();
/** Submits the http request. */
Downloader.prototype.send = function (x) {
if (!this.http) { return null; }
return this.http.send(x);
};
/** Aborts the download, setting the <code>aborted</code> field to true. */
Downloader.prototype.abort = function () {
if (!this.http) { return null; }
this.aborted=true;
return this.http.abort();
};
/** Returns the downloaded data. */
Downloader.prototype.getData = function () {if (!this.http) { return null; } return this.http.responseText;};
/** Prepares the download. */
Downloader.prototype.setTarget = function () {
if (!this.http) { return null; }
this.http.open(this.method, this.url, this.async);
this.http.setRequestHeader( 'Api-User-Agent', pg.misc.userAgent );
};
/** Gets the state of the download. */
Downloader.prototype.getReadyState=function () {if (!this.http) { return null; } return this.http.readyState;};
pg.misc.downloadsInProgress = { };
/** Starts the download.
Note that setTarget {@link Downloader#setTarget} must be run first
*/
Downloader.prototype.start=function () {
if (!this.http) { return; }
pg.misc.downloadsInProgress[this.id] = this;
this.http.send(null);
};
/** Gets the 'Last-Modified' date from the download headers.
Should be run after the download completes.
Returns <code>null</code> on failure.
@return {Date}
*/
Downloader.prototype.getLastModifiedDate=function () {
if(!this.http) { return null; }
var lastmod=null;
try {
lastmod=this.http.getResponseHeader('Last-Modified');
} catch (err) {}
if (lastmod) { return new Date(lastmod); }
return null;
};
/** Sets the callback function.
@param {Function} f callback function, called as <code>f(this)</code> on success
*/
Downloader.prototype.setCallback = function (f) {
if(!this.http) { return; }
this.http.onreadystatechange = f;
};
Downloader.prototype.getStatus = function() { if (!this.http) { return null; } return this.http.status; };
//////////////////////////////////////////////////
// helper functions
/** Creates a new {@link Downloader} and prepares it for action.
@param {String} url The url to download
@param {integer} id The ID of the {@link Downloader} object
@param {Function} callback The callback function invoked on success
@return {String/Downloader} the {@link Downloader} object created, or 'ohdear' if an unsupported browser
*/
function newDownload(url, id, callback, onfailure) {
var d=new Downloader(url);
if (!d.http) { return 'ohdear'; }
d.id=id;
d.setTarget();
if (!onfailure) {
onfailure=2;
}
var f = function () {
if (d.getReadyState() == 4) {
delete pg.misc.downloadsInProgress[this.id];
try {
if ( d.getStatus() == 200 ) {
d.data=d.getData();
d.lastModified=d.getLastModifiedDate();
callback(d);
} else if (typeof onfailure == typeof 1) {
if (onfailure > 0) {
// retry
newDownload(url, id, callback, onfailure - 1);
}
} else if (typeof onfailure === 'function') {
onfailure(d,url,id,callback);
}
} catch (somerr) { /* ignore it */ }
}
};
d.setCallback(f);
return d;
}
/** Simulates a download from cached data.
The supplied data is put into a {@link Downloader} as if it had downloaded it.
@param {String} url The url.
@param {integer} id The ID.
@param {Function} callback The callback, which is invoked immediately as <code>callback(d)</code>,
where <code>d</code> is the new {@link Downloader}.
@param {String} data The (cached) data.
@param {Date} lastModified The (cached) last modified date.
*/
function fakeDownload(url, id, callback, data, lastModified, owner) {
var d=newDownload(url,callback);
d.owner=owner;
d.id=id; d.data=data;
d.lastModified=lastModified;
return callback(d);
}
/**
Starts a download.
@param {String} url The url to download
@param {integer} id The ID of the {@link Downloader} object
@param {Function} callback The callback function invoked on success
@return {String/Downloader} the {@link Downloader} object created, or 'ohdear' if an unsupported browser
*/
function startDownload(url, id, callback) {
var d=newDownload(url, id, callback);
if (typeof d == typeof '' ) { return d; }
d.start();
return d;
}
/**
Aborts all downloads which have been started.
*/
function abortAllDownloads() {
for ( var x in pg.misc.downloadsInProgress ) {
try {
pg.misc.downloadsInProgress[x].aborted=true;
pg.misc.downloadsInProgress[x].abort();
delete pg.misc.downloadsInProgress[x];
} catch (e) {}
}
}
// ENDFILE: downloader.js
// STARTFILE: livepreview.js
// TODO: location is often not correct (eg relative links in previews)
// NOTE: removed md5 and image and math parsing. was broken, lots of bytes.
/**
* InstaView - a Mediawiki to HTML converter in JavaScript
* Version 0.6.1
* Copyright (C) Pedro Fayolle 2005-2006
* https://en.wikipedia.org/wiki/User:Pilaf
* Distributed under the BSD license
*
* Changelog:
*
* 0.6.1
* - Fixed problem caused by \r characters
* - Improved inline formatting parser
*
* 0.6
* - Changed name to InstaView
* - Some major code reorganizations and factored out some common functions
* - Handled conversion of relative links (i.e. [[/foo]])
* - Fixed misrendering of adjacent definition list items
* - Fixed bug in table headings handling
* - Changed date format in signatures to reflect Mediawiki's
* - Fixed handling of [[:Image:...]]
* - Updated MD5 function (hopefully it will work with UTF-8)
* - Fixed bug in handling of links inside images
*
* To do:
* - Better support for math tags
* - Full support for <nowiki>
* - Parser-based (as opposed to RegExp-based) inline wikicode handling (make it one-pass and bullet-proof)
* - Support for templates (through AJAX)
* - Support for coloured links (AJAX)
*/
var Insta = {};
function setupLivePreview() {
// options
Insta.conf =
{
baseUrl: '',
user: {},
wiki: {
lang: pg.wiki.lang,
interwiki: pg.wiki.interwiki,
default_thumb_width: 180
},
paths: {
articles: pg.wiki.articlePath + '/',
// Only used for Insta previews with images. (not in popups)
math: '/math/',
images: '//upload.wikimedia.org/wikipedia/en/', // FIXME getImageUrlStart(pg.wiki.hostname),
images_fallback: '//upload.wikimedia.org/wikipedia/commons/',
},
locale: {
user: mw.config.get('wgFormattedNamespaces')[pg.nsUserId],
image: mw.config.get('wgFormattedNamespaces')[pg.nsImageId],
category: mw.config.get('wgFormattedNamespaces')[pg.nsCategoryId],
// shouldn't be used in popup previews, i think
months: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
}
};
// options with default values or backreferences
Insta.conf.user.name = Insta.conf.user.name || 'Wikipedian';
Insta.conf.user.signature = '[['+Insta.conf.locale.user+':'+Insta.conf.user.name+'|'+Insta.conf.user.name+']]';
//Insta.conf.paths.images = '//upload.wikimedia.org/wikipedia/' + Insta.conf.wiki.lang + '/';
// define constants
Insta.BLOCK_IMAGE = new RegExp('^\\[\\[(?:File|Image|'+Insta.conf.locale.image+
'):.*?\\|.*?(?:frame|thumbnail|thumb|none|right|left|center)', 'i');
}
Insta.dump = function(from, to)
{
if (typeof from == 'string') { from = document.getElementById(from); }
if (typeof to == 'string') { to = document.getElementById(to); }
to.innerHTML = this.convert(from.value);
};
Insta.convert = function(wiki)
{
var ll = (typeof wiki == 'string')? wiki.replace(/\r/g,'').split(/\n/): wiki, // lines of wikicode
o = '', // output
p = 0, // para flag
$r; // result of passing a regexp to $()
// some shorthands
function remain() { return ll.length; }
function sh() { return ll.shift(); } // shift
function ps(s) { o += s; } // push
// similar to C's printf, uses ? as placeholders, ?? to escape question marks
function f()
{
var i=1, a=arguments, f=a[0], o='', c, p;
for (; i<a.length; i++) {
if ((p=f.indexOf('?'))+1) {
// allow character escaping
i -= c = f.charAt(p+1)=='?' ? 1 : 0;
o += f.substring(0,p) + (c ? '?' : a[i]);
f = f.substr(p+1+c);
} else { break; }
}
return o+f;
}
function html_entities(s) {
return s.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
}
// Wiki text parsing to html is a nightmare.
// The below functions deliberately don't escape the ampersand since this would make it more difficult,
// and we don't absolutely need to for how we need it.
// This means that any unescaped ampersands in wikitext will remain unescaped and can cause invalid HTML.
// Browsers should all be able to handle it though.
// We also escape significant wikimarkup characters to prevent further matching on the processed text
function htmlescape_text(s) {
return s.replace(/</g,"<").replace(/>/g,">").replace(/:/g,":").replace(/\[/g,"[").replace(/]/g,"]");
}
function htmlescape_attr(s) {
return htmlescape_text(s).replace(/'/g,"'").replace(/"/g,""");
}
// return the first non matching character position between two strings
function str_imatch(a, b)
{
for (var i=0, l=Math.min(a.length, b.length); i<l; i++) {
if (a.charAt(i)!=b.charAt(i)) { break; }
}
return i;
}
// compare current line against a string or regexp
// if passed a string it will compare only the first string.length characters
// if passed a regexp the result is stored in $r
function $(c) { return (typeof c == 'string') ? (ll[0].substr(0,c.length)==c) : ($r = ll[0].match(c)); }
function $$(c) { return ll[0]==c; } // compare current line against a string
function _(p) { return ll[0].charAt(p); } // return char at pos p
function endl(s) { ps(s); sh(); }
function parse_list()
{
var prev='';
while (remain() && $(/^([*#:;]+)(.*)$/)) {
var l_match = $r;
sh();
var ipos = str_imatch(prev, l_match[1]);
// close uncontinued lists
for (var prevPos=prev.length-1; prevPos >= ipos; prevPos--) {
var pi = prev.charAt(prevPos);
if (pi=='*') { ps('</ul>'); }
else if (pi=='#') { ps('</ol>'); }
// close a dl only if the new item is not a dl item (:, ; or empty)
else if($.inArray(l_match[1].charAt(prevPos), ['','*','#'])) { ps('</dl>'); }
}
// open new lists
for (var matchPos=ipos; matchPos<l_match[1].length; matchPos++) {
var li = l_match[1].charAt(matchPos);
if (li=='*') { ps('<ul>'); }
else if (li=='#') { ps('<ol>'); }
// open a new dl only if the prev item is not a dl item (:, ; or empty)
else if ($.inArray(prev.charAt(matchPos), ['','*','#'])) { ps('<dl>'); }
}
switch (l_match[1].charAt(l_match[1].length-1)) {
case '*': case '#':
ps('<li>' + parse_inline_nowiki(l_match[2]));
break;
case ';':
ps('<dt>');
var dt_match = l_match[2].match(/(.*?)(:.*?)$/);
// handle ;dt :dd format
if (dt_match) {
ps(parse_inline_nowiki(dt_match[1]));
ll.unshift(dt_match[2]);
} else ps(parse_inline_nowiki(l_match[2]));
break;
case ':':
ps('<dd>' + parse_inline_nowiki(l_match[2]));
}
prev=l_match[1];
}
// close remaining lists
for (var i=prev.length-1; i>=0; i--) {
ps(f('</?>', (prev.charAt(i)=='*')? 'ul': ((prev.charAt(i)=='#')? 'ol': 'dl')));
}
}
function parse_table()
{
endl(f('<table>', $(/^\{\|( .*)$/)? $r[1]: ''));
for (;remain();) if ($('|')) switch (_(1)) {
case '}':
endl('</table>');
return;
case '-':
endl(f('<tr>', $(/\|-*(.*)/)[1]));
break;
default:
parse_table_data();
}
else if ($('!')) { parse_table_data(); }
else { sh(); }
}
function parse_table_data()
{
var td_line, match_i;
// 1: "|+", '|' or '+'
// 2: ??
// 3: attributes ??
// TODO: finish commenting this regexp
var td_match = sh().match(/^(\|\+|\||!)((?:([^[|]*?)\|(?!\|))?(.*))$/);
if (td_match[1] == '|+') ps('<caption');
else ps('<t' + ((td_match[1]=='|')?'d':'h'));
if (typeof td_match[3] != 'undefined') {
//ps(' ' + td_match[3])
match_i = 4;
} else match_i = 2;
ps('>');
if (td_match[1] != '|+') {
// use || or !! as a cell separator depending on context
// NOTE: when split() is passed a regexp make sure to use non-capturing brackets
td_line = td_match[match_i].split((td_match[1] == '|')? '||': /(?:\|\||!!)/);
ps(parse_inline_nowiki(td_line.shift()));
while (td_line.length) ll.unshift(td_match[1] + td_line.pop());
} else ps(td_match[match_i]);
var tc = 0, td = [];
while (remain()) {
td.push(sh());
if ($('|')) {
if (!tc) break; // we're at the outer-most level (no nested tables), skip to td parse
else if (_(1)=='}') tc--;
}
else if (!tc && $('!')) break;
else if ($('{|')) tc++;
}
if (td.length) ps(Insta.convert(td));
}
function parse_pre()
{
ps('<pre>');
do {
endl(parse_inline_nowiki(ll[0].substring(1)) + "\n");
} while (remain() && $(' '));
ps('</pre>');
}
function parse_block_image()
{
ps(parse_image(sh()));
}
function parse_image(str)
{
//<NOLITE>
// get what's in between "[[Image:" and "]]"
var tag = str.substring(str.indexOf(':') + 1, str.length - 2);
/* eslint-disable no-unused-vars */
var width;
var attr = [], filename, caption = '';
var thumb=0, frame=0, center=0;
var align='';
/* eslint-enable no-unused-vars */
if (tag.match(/\|/)) {
// manage nested links
var nesting = 0;
var last_attr;
for (var i = tag.length-1; i > 0; i--) {
if (tag.charAt(i) == '|' && !nesting) {
last_attr = tag.substr(i+1);
tag = tag.substring(0, i);
break;
} else switch (tag.substr(i-1, 2)) {
case ']]':
nesting++;
i--;
break;
case '[[':
nesting--;
i--;
}
}
attr = tag.split(/\s*\|\s*/);
attr.push(last_attr);
filename = attr.shift();
var w_match;
for (;attr.length; attr.shift()) {
w_match = attr[0].match(/^(\d*)(?:[px]*\d*)?px$/);
if (w_match) width = w_match[1];
else switch(attr[0]) {
case 'thumb':
case 'thumbnail':
thumb=true;
frame=true;
break;
case 'frame':
frame=true;
break;
case 'none':
case 'right':
case 'left':
center=false;
align=attr[0];
break;
case 'center':
center=true;
align='none';
break;
default:
if (attr.length == 1) caption = attr[0];
}
}
} else filename = tag;
return '';
//</NOLITE>
}
function parse_inline_nowiki(str)
{
var start, lastend=0;
var substart=0, nestlev=0, open, close, subloop;
var html='';
while (-1 != (start = str.indexOf('<nowiki>', substart))) {
html += parse_inline_wiki(str.substring(lastend, start));
start += 8;
substart = start;
subloop = true;
do {
open = str.indexOf('<nowiki>', substart);
close = str.indexOf('</nowiki>', substart);
if (close<=open || open==-1) {
if (close==-1) {
return html + html_entities(str.substr(start));
}
substart = close+9;
if (nestlev) {
nestlev--;
} else {
lastend = substart;
html += html_entities(str.substring(start, lastend-9));
subloop = false;
}
} else {
substart = open+8;
nestlev++;
}
} while (subloop);
}
return html + parse_inline_wiki(str.substr(lastend));
}
function parse_inline_images(str)
{
//<NOLITE>
var start, substart=0, nestlev=0;
var loop, close, open, wiki, html;
while (-1 != (start=str.indexOf('[[', substart))) {
if(str.substr(start+2).match(RegExp('^(Image|File|' + Insta.conf.locale.image + '):','i'))) {
loop=true;
substart=start;
do {
substart+=2;
close=str.indexOf(']]',substart);
open=str.indexOf('[[',substart);
if (close<=open||open==-1) {
if (close==-1) return str;
substart=close;
if (nestlev) {
nestlev--;
} else {
wiki=str.substring(start,close+2);
html=parse_image(wiki);
str=str.replace(wiki,html);
substart=start+html.length;
loop=false;
}
} else {
substart=open;
nestlev++;
}
} while (loop);
} else break;
}
//</NOLITE>
return str;
}
// the output of this function doesn't respect the FILO structure of HTML
// but since most browsers can handle it I'll save myself the hassle
function parse_inline_formatting(str)
{
var em,st,i,li,o='';
while ((i=str.indexOf("''",li))+1) {
o += str.substring(li,i);
li=i+2;
if (str.charAt(i+2)=="'") {
li++;
st=!st;
o+=st?'<strong>':'</strong>';
} else {
em=!em;
o+=em?'<em>':'</em>';
}
}
return o+str.substr(li);
}
function parse_inline_wiki(str)
{
str = parse_inline_images(str);
str = parse_inline_formatting(str);
// math
str = str.replace(/<(?:)math>(.*?)<\/math>/ig, '');
// Build a Mediawiki-formatted date string
var date = new Date();
var minutes = date.getUTCMinutes();
if (minutes < 10) minutes = '0' + minutes;
date = f("?:?, ? ? ? (UTC)", date.getUTCHours(), minutes, date.getUTCDate(), Insta.conf.locale.months[date.getUTCMonth()], date.getUTCFullYear());
// text formatting
return str.
// signatures
replace(/~{5}(?!~)/g, date).
replace(/~{4}(?!~)/g, Insta.conf.user.name+' '+date).
replace(/~{3}(?!~)/g, Insta.conf.user.name).
// [[:Category:...]], [[:Image:...]], etc...
replace(RegExp('\\[\\[:((?:'+Insta.conf.locale.category+'|Image|File|'+Insta.conf.locale.image+'|'+Insta.conf.wiki.interwiki+'):[^|]*?)\\]\\](\\w*)','gi'), function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($1) + htmlescape_text($2));}).
// remove straight category and interwiki tags
replace(RegExp('\\[\\[(?:'+Insta.conf.locale.category+'|'+Insta.conf.wiki.interwiki+'):.*?\\]\\]','gi'),'').
// [[:Category:...|Links]], [[:Image:...|Links]], etc...
replace(RegExp('\\[\\[:((?:'+Insta.conf.locale.category+'|Image|File|'+Insta.conf.locale.image+'|'+Insta.conf.wiki.interwiki+'):.*?)\\|([^\\]]+?)\\]\\](\\w*)','gi'), function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($2) + htmlescape_text($3));}).
// [[/Relative links]]
replace(/\[\[(\/[^|]*?)\]\]/g, function($0,$1){return f("<a href='?'>?</a>", Insta.conf.baseUrl + htmlescape_attr($1), htmlescape_text($1)); }).
// [[/Replaced|Relative links]]
replace(/\[\[(\/.*?)\|(.+?)\]\]/g, function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.baseUrl + htmlescape_attr($1), htmlescape_text($2)); }).
// [[Common links]]
replace(/\[\[([^[|]*?)\]\](\w*)/g, function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($1) + htmlescape_text($2)); }).
// [[Replaced|Links]]
replace(/\[\[([^[]*?)\|([^\]]+?)\]\](\w*)/g, function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($2) + htmlescape_text($3)); }).
// [[Stripped:Namespace|Namespace]]
replace(/\[\[([^\]]*?:)?(.*?)( *\(.*?\))?\|\]\]/g, function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1) + htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($2)); }).
// External links
replace(/\[(https?|news|ftp|mailto|gopher|irc):(\/*)([^\]]*?) (.*?)\]/g, function($0,$1,$2,$3,$4){return f("<a class='external' href='?:?'>?</a>", htmlescape_attr($1), htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($4)); }).
replace(/\[http:\/\/(.*?)\]/g, function($0,$1){return f("<a class='external' href='http://?'>[#]</a>", htmlescape_attr($1)); }).
replace(/\[(news|ftp|mailto|gopher|irc):(\/*)(.*?)\]/g, function($0,$1,$2,$3){return f("<a class='external' href='?:?'>?:?</a>", htmlescape_attr($1), htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($1), htmlescape_text($2) + htmlescape_text($3)); }).
replace(/(^| )(https?|news|ftp|mailto|gopher|irc):(\/*)([^ $]*[^.,!?;: $])/g, function($0,$1,$2,$3,$4){return f("?<a class='external' href='?:?'>?:?</a>", htmlescape_text($1), htmlescape_attr($2), htmlescape_attr($3) + htmlescape_attr($4), htmlescape_text($2), htmlescape_text($3) + htmlescape_text($4)); }).
replace('__NOTOC__','').
replace('__NOEDITSECTION__','');
}
// begin parsing
for (;remain();) if ($(/^(={1,6})(.*)\1(.*)$/)) {
p=0;
endl(f('<h?>?</h?>?', $r[1].length, parse_inline_nowiki($r[2]), $r[1].length, $r[3]));
} else if ($(/^[*#:;]/)) {
p=0;
parse_list();
} else if ($(' ')) {
p=0;
parse_pre();
} else if ($('{|')) {
p=0;
parse_table();
} else if ($(/^----+$/)) {
p=0;
endl('<hr />');
} else if ($(Insta.BLOCK_IMAGE)) {
p=0;
parse_block_image();
} else {
// handle paragraphs
if ($$('')) {
p = (remain()>1 && ll[1]===(''));
if (p) endl('<p><br>');
} else {
if(!p) {
ps('<p>');
p=1;
}
ps(parse_inline_nowiki(ll[0]) + ' ');
}
sh();
}
return o;
};
function wiki2html(txt,baseurl) {
Insta.conf.baseUrl=baseurl;
return Insta.convert(txt);
}
// ENDFILE: livepreview.js
// STARTFILE: pageinfo.js
//<NOLITE>
function popupFilterPageSize(data) {
return formatBytes(data.length);
}
function popupFilterCountLinks(data) {
var num=countLinks(data);
return String(num) + ' ' + ((num!=1)?popupString('wikiLinks'):popupString('wikiLink'));
}
function popupFilterCountImages(data) {
var num=countImages(data);
return String(num) + ' ' + ((num!=1)?popupString('images'):popupString('image'));
}
function popupFilterCountCategories(data) {
var num=countCategories(data);
return String(num) + ' ' + ((num!=1)?popupString('categories'):popupString('category'));
}
function popupFilterLastModified(data,download) {
var lastmod=download.lastModified;
var now=new Date();
var age=now-lastmod;
if (lastmod && getValueOf('popupLastModified')) {
return (tprintf('%s old', [formatAge(age)])).replace(RegExp(' ','g'), ' ');
}
return '';
}
function formatAge(age) {
// coerce into a number
var a=0+age, aa=a;
var seclen = 1000;
var minlen = 60*seclen;
var hourlen = 60*minlen;
var daylen = 24*hourlen;
var weeklen = 7*daylen;
var numweeks = (a-a%weeklen)/weeklen; a = a-numweeks*weeklen; var sweeks = addunit(numweeks, 'week');
var numdays = (a-a%daylen)/daylen; a = a-numdays*daylen; var sdays = addunit(numdays, 'day');
var numhours = (a-a%hourlen)/hourlen; a = a-numhours*hourlen; var shours = addunit(numhours,'hour');
var nummins = (a-a%minlen)/minlen; a = a-nummins*minlen; var smins = addunit(nummins, 'minute');
var numsecs = (a-a%seclen)/seclen; a = a-numsecs*seclen; var ssecs = addunit(numsecs, 'second');
if (aa > 4*weeklen) { return sweeks; }
if (aa > weeklen) { return sweeks + ' ' + sdays; }
if (aa > daylen) { return sdays + ' ' + shours; }
if (aa > 6*hourlen) { return shours; }
if (aa > hourlen) { return shours + ' ' + smins; }
if (aa > 10*minlen) { return smins; }
if (aa > minlen) { return smins + ' ' + ssecs; }
return ssecs;
}
function addunit(num,str) { return '' + num + ' ' + ((num!=1) ? popupString(str+'s') : popupString(str)) ;}
function runPopupFilters(list, data, download) {
var ret=[];
for (var i=0; i<list.length; ++i) {
if (list[i] && typeof list[i] == 'function') {
var s=list[i](data, download, download.owner.article);
if (s) { ret.push(s); }
}
}
return ret;
}
function getPageInfo(data, download) {
if (!data || data.length === 0) { return popupString('Empty page'); }
var popupFilters=getValueOf('popupFilters') || [];
var extraPopupFilters = getValueOf('extraPopupFilters') || [];
var pageInfoArray = runPopupFilters(popupFilters.concat(extraPopupFilters), data, download);
var pageInfo=pageInfoArray.join(', ');
if (pageInfo !== '' ) { pageInfo = upcaseFirst(pageInfo); }
return pageInfo;
}
// this could be improved!
function countLinks(wikiText) { return wikiText.split('[[').length - 1; }
// if N = # matches, n = # brackets, then
// String.parenSplit(regex) intersperses the N+1 split elements
// with Nn other elements. So total length is
// L= N+1 + Nn = N(n+1)+1. So N=(L-1)/(n+1).
function countImages(wikiText) {
return (wikiText.parenSplit(pg.re.image).length - 1) / (pg.re.imageBracketCount + 1);
}
function countCategories(wikiText) {
return (wikiText.parenSplit(pg.re.category).length - 1) / (pg.re.categoryBracketCount + 1);
}
function popupFilterStubDetect(data, download, article) {
var counts=stubCount(data, article);
if (counts.real) { return popupString('stub'); }
if (counts.sect) { return popupString('section stub'); }
return '';
}
function popupFilterDisambigDetect(data, download, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return ''; }
return (isDisambig(data, article)) ? popupString('disambig') : '';
}
function formatBytes(num) {
return (num > 949) ? (Math.round(num/100)/10+popupString('kB')) : (num +' ' + popupString('bytes')) ;
}
//</NOLITE>
// ENDFILE: pageinfo.js
// STARTFILE: titles.js
/**
@fileoverview Defines the {@link Title} class, and associated crufty functions.
<code>Title</code> deals with article titles and their various
forms. {@link Stringwrapper} is the parent class of
<code>Title</code>, which exists simply to make things a little
neater.
*/
/**
Creates a new Stringwrapper.
@constructor
@class the Stringwrapper class. This base class is not really
useful on its own; it just wraps various common string operations.
*/
function Stringwrapper() {
/**
Wrapper for this.toString().indexOf()
@param {String} x
@type integer
*/
this.indexOf=function(x){return this.toString().indexOf(x);};
/**
Returns this.value.
@type String
*/
this.toString=function(){return this.value;};
/**
Wrapper for {@link String#parenSplit} applied to this.toString()
@param {RegExp} x
@type Array
*/
this.parenSplit=function(x){return this.toString().parenSplit(x);};
/**
Wrapper for this.toString().substring()
@param {String} x
@param {String} y (optional)
@type String
*/
this.substring=function(x,y){
if (typeof y=='undefined') { return this.toString().substring(x); }
return this.toString().substring(x,y);
};
/**
Wrapper for this.toString().split()
@param {String} x
@type Array
*/
this.split=function(x){return this.toString().split(x);};
/**
Wrapper for this.toString().replace()
@param {String} x
@param {String} y
@type String
*/
this.replace=function(x,y){ return this.toString().replace(x,y); };
}
/**
Creates a new <code>Title</code>.
@constructor
@class The Title class. Holds article titles and converts them into
various forms. Also deals with anchors, by which we mean the bits
of the article URL after a # character, representing locations
within an article.
@param {String} value The initial value to assign to the
article. This must be the canonical title (see {@link
Title#value}. Omit this in the constructor and use another function
to set the title if this is unavailable.
*/
function Title(val) {
/**
The canonical article title. This must be in UTF-8 with no
entities, escaping or nasties. Also, underscores should be
replaced with spaces.
@type String
@private
*/
this.value=null;
/**
The canonical form of the anchor. This should be exactly as
it appears in the URL, i.e. with the .C3.0A bits in.
@type String
*/
this.anchor='';
this.setUtf(val);
}
Title.prototype=new Stringwrapper();
/**
Returns the canonical representation of the article title, optionally without anchor.
@param {boolean} omitAnchor
@fixme Decide specs for anchor
@return String The article title and the anchor.
*/
Title.prototype.toString=function(omitAnchor) {
return this.value + ( (!omitAnchor && this.anchor) ? '#' + this.anchorString() : '' );
};
Title.prototype.anchorString=function() {
if (!this.anchor) { return ''; }
var split=this.anchor.parenSplit(/((?:[.][0-9A-F]{2})+)/);
var len=split.length;
for (var j=1; j<len; j+=2) {
// FIXME s/decodeURI/decodeURIComponent/g ?
split[j]=decodeURIComponent(split[j].split('.').join('%')).split('_').join(' ');
}
return split.join('');
};
Title.prototype.urlAnchor=function() {
var split=this.anchor.parenSplit('/((?:[%][0-9A-F]{2})+)/');
var len=split.length;
for (var j=1; j<len; j+=2) {
split[j]=split[j].split('%').join('.');
}
return split.join('');
};
Title.prototype.anchorFromUtf=function(str) {
this.anchor=encodeURIComponent(str.split(' ').join('_'))
.split('%3A').join(':').split("'").join('%27').split('%').join('.');
};
Title.fromURL=function(h) {
return new Title().fromURL(h);
};
Title.prototype.fromURL=function(h) {
if (typeof h != 'string') {
this.value=null;
return this;
}
// NOTE : playing with decodeURI, encodeURI, escape, unescape,
// we seem to be able to replicate the IE borked encoding
// IE doesn't do this new-fangled utf-8 thing.
// and it's worse than that.
// IE seems to treat the query string differently to the rest of the url
// the query is treated as bona-fide utf8, but the first bit of the url is pissed around with
// we fix up & for all browsers, just in case.
var splitted=h.split('?');
splitted[0]=splitted[0].split('&').join('%26');
h=splitted.join('?');
var contribs=pg.re.contribs.exec(h);
if (contribs) {
if (contribs[1]=='title=') { contribs[3]=contribs[3].split('+').join(' '); }
var u=new Title(contribs[3]);
this.setUtf(this.decodeNasties(mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + u.stripNamespace()));
return this;
}
var email=pg.re.email.exec(h);
if (email) {
this.setUtf(this.decodeNasties(mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + new Title(email[3]).stripNamespace()));
return this;
}
var backlinks=pg.re.backlinks.exec(h);
if (backlinks) {
this.setUtf(this.decodeNasties(new Title(backlinks[3])));
return this;
}
//A dummy title object for a Special:Diff link.
var specialdiff=pg.re.specialdiff.exec(h);
if (specialdiff) {
this.setUtf(this.decodeNasties(new Title(mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId] + ':Diff')));
return this;
}
// no more special cases to check --
// hopefully it's not a disguised user-related or specially treated special page
var m=pg.re.main.exec(h);
if(m === null) { this.value=null; }
else {
var fromBotInterface = /[?](.+[&])?title=/.test(h);
if (fromBotInterface) {
m[2]=m[2].split('+').join('_');
}
var extracted = m[2] + (m[3] ? '#' + m[3] : '');
if (pg.flag.isSafari && /%25[0-9A-Fa-f]{2}/.test(extracted)) {
// Fix Safari issue
// Safari sometimes encodes % as %25 in UTF-8 encoded strings like %E5%A3 -> %25E5%25A3.
this.setUtf(decodeURIComponent(unescape(extracted)));
} else {
this.setUtf(this.decodeNasties(extracted));
}
}
return this;
};
Title.prototype.decodeNasties=function(txt) {
var ret= this.decodeEscapes(decodeURI(txt));
ret = ret.replace(/[_ ]*$/, '');
return ret;
};
Title.prototype.decodeEscapes=function(txt) {
var split=txt.parenSplit(/((?:[%][0-9A-Fa-f]{2})+)/);
var len=split.length;
for (var i=1; i<len; i=i+2) {
// FIXME is decodeURIComponent better?
split[i]=unescape(split[i]);
}
return split.join('');
};
Title.fromAnchor=function(a) {
return new Title().fromAnchor(a);
};
Title.prototype.fromAnchor=function(a) {
if (!a) { this.value=null; return this; }
return this.fromURL(a.href);
};
Title.fromWikiText=function(txt) {
return new Title().fromWikiText(txt);
};
Title.prototype.fromWikiText=function(txt) {
// FIXME - testing needed
txt=myDecodeURI(txt);
this.setUtf(txt);
return this;
};
Title.prototype.hintValue=function(){
if(!this.value) { return ''; }
return safeDecodeURI(this.value);
};
//<NOLITE>
Title.prototype.toUserName=function(withNs) {
if (this.namespaceId() != pg.nsUserId && this.namespaceId() != pg.nsUsertalkId) {
this.value=null;
return;
}
this.value = (withNs ? mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' : '') + this.stripNamespace().split('/')[0];
};
Title.prototype.userName=function(withNs) {
var t=(new Title(this.value));
t.toUserName(withNs);
if (t.value) { return t; }
return null;
};
Title.prototype.toTalkPage=function() {
// convert article to a talk page, or if we can't, return null
// In other words: return null if this ALREADY IS a talk page
// and return the corresponding talk page otherwise
//
// Per https://www.mediawiki.org/wiki/Manual:Namespace#Subject_and_talk_namespaces
// * All discussion namespaces have odd-integer indices
// * The discussion namespace index for a specific namespace with index n is n + 1
if (this.value === null) { return null; }
var namespaceId = this.namespaceId();
if (namespaceId>=0 && namespaceId % 2 === 0) //non-special and subject namespace
{
var localizedNamespace = mw.config.get('wgFormattedNamespaces')[namespaceId+1];
if (typeof localizedNamespace!=='undefined')
{
if (localizedNamespace === '') {
this.value = this.stripNamespace();
} else {
this.value = localizedNamespace.split(' ').join('_') + ':' + this.stripNamespace();
}
return this.value;
}
}
this.value=null;
return null;
};
//</NOLITE>
// Return canonical, localized namespace
Title.prototype.namespace=function() {
return mw.config.get('wgFormattedNamespaces')[this.namespaceId()];
};
Title.prototype.namespaceId=function() {
var n=this.value.indexOf(':');
if (n<0) { return 0; } //mainspace
var namespaceId = mw.config.get('wgNamespaceIds')[this.value.substring(0,n).split(' ').join('_').toLowerCase()];
if (typeof namespaceId=='undefined') return 0; //mainspace
return namespaceId;
};
//<NOLITE>
Title.prototype.talkPage=function() {
var t=new Title(this.value);
t.toTalkPage();
if (t.value) { return t; }
return null;
};
Title.prototype.isTalkPage=function() {
if (this.talkPage()===null) { return true; }
return false;
};
Title.prototype.toArticleFromTalkPage=function() {
//largely copy/paste from toTalkPage above.
if (this.value === null) { return null; }
var namespaceId = this.namespaceId();
if (namespaceId >= 0 && namespaceId % 2 == 1) //non-special and talk namespace
{
var localizedNamespace = mw.config.get('wgFormattedNamespaces')[namespaceId-1];
if (typeof localizedNamespace!=='undefined')
{
if (localizedNamespace === '') {
this.value = this.stripNamespace();
} else {
this.value = localizedNamespace.split(' ').join('_') + ':' + this.stripNamespace();
}
return this.value;
}
}
this.value=null;
return null;
};
Title.prototype.articleFromTalkPage=function() {
var t=new Title(this.value);
t.toArticleFromTalkPage();
if (t.value) { return t; }
return null;
};
Title.prototype.articleFromTalkOrArticle=function() {
var t=new Title(this.value);
if ( t.toArticleFromTalkPage() ) { return t; }
return this;
};
Title.prototype.isIpUser=function() {
return pg.re.ipUser.test(this.userName());
};
//</NOLITE>
Title.prototype.stripNamespace=function(){ // returns a string, not a Title
var n=this.value.indexOf(':');
if (n<0) { return this.value; }
var namespaceId = this.namespaceId();
if (namespaceId === pg.nsMainspaceId) return this.value;
return this.value.substring(n+1);
};
Title.prototype.setUtf=function(value){
if (!value) { this.value=''; return; }
var anch=value.indexOf('#');
if(anch < 0) { this.value=value.split('_').join(' '); this.anchor=''; return; }
this.value=value.substring(0,anch).split('_').join(' ');
this.anchor=value.substring(anch+1);
this.ns=null; // wait until namespace() is called
};
Title.prototype.setUrl=function(urlfrag) {
var anch=urlfrag.indexOf('#');
this.value=safeDecodeURI(urlfrag.substring(0,anch));
this.anchor=this.value.substring(anch+1);
};
Title.prototype.append=function(x){
this.setUtf(this.value + x);
};
Title.prototype.urlString=function(x) {
if(!x) { x={}; }
var v=this.toString(true);
if (!x.omitAnchor && this.anchor) { v+= '#' + this.urlAnchor(); }
if (!x.keepSpaces) { v=v.split(' ').join('_'); }
return encodeURI(v).split('&').join('%26').split('?').join('%3F').split('+').join('%2B');
};
Title.prototype.removeAnchor=function() {
return new Title(this.toString(true));
};
Title.prototype.toUrl=function() {
return pg.wiki.titlebase + this.urlString();
};
function parseParams(url) {
var specialDiff = pg.re.specialdiff.exec(url);
if (specialDiff)
{
var split= specialDiff[1].split('/');
if (split.length==1) return {oldid:split[0], diff: 'prev'};
else if (split.length==2) return {oldid: split[0], diff: split[1]};
}
var ret={};
if (url.indexOf('?')==-1) { return ret; }
url = url.split('#')[0];
var s=url.split('?').slice(1).join();
var t=s.split('&');
for (var i=0; i<t.length; ++i) {
var z=t[i].split('=');
z.push(null);
ret[z[0]]=z[1];
}
//Diff revision with no oldid is interpreted as a diff to the previous revision by MediaWiki
if (ret.diff && typeof(ret.oldid)==='undefined')
{
ret.oldid = "prev";
}
//Documentation seems to say something different, but oldid can also accept prev/next, and Echo is emitting such URLs. Simple fixup during parameter decoding:
if (ret.oldid && (ret.oldid==='prev' || ret.oldid==='next' || ret.oldid==='cur'))
{
var helper = ret.diff;
ret.diff = ret.oldid;
ret.oldid = helper;
}
return ret;
}
// (a) myDecodeURI (first standard decodeURI, then pg.re.urlNoPopup)
// (b) change spaces to underscores
// (c) encodeURI (just the straight one, no pg.re.urlNoPopup)
function myDecodeURI (str) {
var ret;
// FIXME decodeURIComponent??
try { ret=decodeURI(str.toString()); }
catch (summat) { return str; }
for (var i=0; i<pg.misc.decodeExtras.length; ++i) {
var from=pg.misc.decodeExtras[i].from;
var to=pg.misc.decodeExtras[i].to;
ret=ret.split(from).join(to);
}
return ret;
}
function safeDecodeURI(str) { var ret=myDecodeURI(str); return ret || str; }
///////////
// TESTS //
///////////
//<NOLITE>
function isDisambig(data, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return false; }
return ! article.isTalkPage() && pg.re.disambig.test(data);
}
function stubCount(data, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return false; }
var sectStub=0;
var realStub=0;
if (pg.re.stub.test(data)) {
var s=data.parenSplit(pg.re.stub);
for (var i=1; i<s.length; i=i+2) {
if (s[i]) { ++sectStub; }
else { ++realStub; }
}
}
return { real: realStub, sect: sectStub };
}
function isValidImageName(str){ // extend as needed...
return ( str.indexOf('{') == -1 );
}
function isInStrippableNamespace(article) {
// Does the namespace allow subpages
// Note, would be better if we had access to wgNamespacesWithSubpages
return ( article.namespaceId() !== 0 );
}
function isInMainNamespace(article) { return article.namespaceId() === 0; }
function anchorContainsImage(a) {
// iterate over children of anchor a
// see if any are images
if (a === null) { return false; }
var kids=a.childNodes;
for (var i=0; i<kids.length; ++i) { if (kids[i].nodeName=='IMG') { return true; } }
return false;
}
//</NOLITE>
function isPopupLink(a) {
// NB for performance reasons, TOC links generally return true
// they should be stripped out later
if (!markNopopupSpanLinks.done) { markNopopupSpanLinks(); }
if (a.inNopopupSpan) { return false; }
// FIXME is this faster inline?
if (a.onmousedown || a.getAttribute('nopopup')) { return false; }
var h=a.href;
if (h === document.location.href+'#') { return false; }
if (!pg.re.basenames.test(h)) { return false; }
if (!pg.re.urlNoPopup.test(h)) { return true; }
return (
(pg.re.email.test(h) || pg.re.contribs.test(h) || pg.re.backlinks.test(h) || pg.re.specialdiff.test(h)) &&
h.indexOf('&limit=') == -1 );
}
function markNopopupSpanLinks() {
if( !getValueOf('popupOnlyArticleLinks'))
fixVectorMenuPopups();
var s = $('.nopopups').toArray();
for (var i=0; i<s.length; ++i) {
var as=s[i].getElementsByTagName('a');
for (var j=0; j<as.length; ++j) {
as[j].inNopopupSpan=true;
}
}
markNopopupSpanLinks.done=true;
}
function fixVectorMenuPopups() {
$('div.vectorMenu h3:first a:first').prop('inNopopupSpan', true);
}
// ENDFILE: titles.js
// STARTFILE: getpage.js
//////////////////////////////////////////////////
// Wiki-specific downloading
//
// Schematic for a getWiki call
//
// getPageWithCaching
// |
// false | true
// getPage<-[findPictureInCache]->-onComplete(a fake download)
// \.
// (async)->addPageToCache(download)->-onComplete(download)
// check cache to see if page exists
function getPageWithCaching(url, onComplete, owner) {
log('getPageWithCaching, url='+url);
var i=findInPageCache(url);
var d;
if (i > -1) {
d=fakeDownload(url, owner.idNumber, onComplete,
pg.cache.pages[i].data, pg.cache.pages[i].lastModified,
owner);
} else {
d=getPage(url, onComplete, owner);
if (d && owner && owner.addDownload) {
owner.addDownload(d);
d.owner=owner;
}
}
}
function getPage(url, onComplete, owner) {
log('getPage');
var callback= function (d) { if (!d.aborted) {addPageToCache(d); onComplete(d);} };
return startDownload(url, owner.idNumber, callback);
}
function findInPageCache(url) {
for (var i=0; i<pg.cache.pages.length; ++i) {
if (url==pg.cache.pages[i].url) { return i; }
}
return -1;
}
function addPageToCache(download) {
log('addPageToCache '+download.url);
var page = {url: download.url, data: download.data, lastModified: download.lastModified};
return pg.cache.pages.push(page);
}
// ENDFILE: getpage.js
// STARTFILE: parensplit.js
//////////////////////////////////////////////////
// parenSplit
// String.prototype.parenSplit should do what ECMAscript says String.prototype.split does,
// interspersing paren matches (regex capturing groups) between the split elements.
// i.e. 'abc'.split(/(b)/)) should return ['a','b','c'], not ['a','c']
if (String('abc'.split(/(b)/))!='a,b,c') {
// broken String.split, e.g. konq, IE < 10
String.prototype.parenSplit=function (re) {
re=nonGlobalRegex(re);
var s=this;
var m=re.exec(s);
var ret=[];
while (m && s) {
// without the following loop, we have
// 'ab'.parenSplit(/a|(b)/) != 'ab'.split(/a|(b)/)
for(var i=0; i<m.length; ++i) {
if (typeof m[i]=='undefined') m[i]='';
}
ret.push(s.substring(0,m.index));
ret = ret.concat(m.slice(1));
s=s.substring(m.index + m[0].length);
m=re.exec(s);
}
ret.push(s);
return ret;
};
} else {
String.prototype.parenSplit=function (re) { return this.split(re); };
String.prototype.parenSplit.isNative=true;
}
function nonGlobalRegex(re) {
var s=re.toString();
var flags='';
for (var j=s.length; s.charAt(j) != '/'; --j) {
if (s.charAt(j) != 'g') { flags += s.charAt(j); }
}
var t=s.substring(1,j);
return RegExp(t,flags);
}
// ENDFILE: parensplit.js
// STARTFILE: tools.js
// IE madness with encoding
// ========================
//
// suppose throughout that the page is in utf8, like wikipedia
//
// if a is an anchor DOM element and a.href should consist of
//
// http://host.name.here/wiki/foo?bar=baz
//
// then IE gives foo as "latin1-encoded" utf8; we have foo = decode_utf8(decodeURI(foo_ie))
// but IE gives bar=baz correctly as plain utf8
//
// ---------------------------------
//
// IE's xmlhttp doesn't understand utf8 urls. Have to use encodeURI here.
//
// ---------------------------------
//
// summat else
// Source: http://aktuell.de.selfhtml.org/artikel/javascript/utf8b64/utf8.htm
//<NOLITE>
function getJsObj(json) {
try {
var json_ret = JSON.parse(json);
if( json_ret.warnings ) {
for( var w=0; w < json_ret.warnings.length; w++ ) {
if( json_ret.warnings[w]['*'] ) {
log( json_ret.warnings[w]['*'] );
} else {
log( json_ret.warnings[w]['warnings'] );
}
}
} else if ( json_ret.error ) {
errlog( json_ret.error.code + ': ' + json_ret.error.info );
}
return json_ret;
} catch (someError) {
errlog('Something went wrong with getJsObj, json='+json);
return 1;
}
}
function anyChild(obj) {
for (var p in obj) {
return obj[p];
}
return null;
}
//</NOLITE>
function upcaseFirst(str) {
if (typeof str != typeof '' || str === '') return '';
return str.charAt(0).toUpperCase() + str.substring(1);
}
function findInArray(arr, foo) {
if (!arr || !arr.length) { return -1; }
var len=arr.length;
for (var i=0; i<len; ++i) { if (arr[i]==foo) { return i; } }
return -1;
}
/* eslint-disable no-unused-vars */
function nextOne (array, value) {
// NB if the array has two consecutive entries equal
// then this will loop on successive calls
var i=findInArray(array, value);
if (i<0) { return null; }
return array[i+1];
}
/* eslint-enable no-unused-vars */
function literalizeRegex(str){
return mw.util.escapeRegExp(str);
}
String.prototype.entify=function() {
//var shy='­';
return this.split('&').join('&').split('<').join('<').split('>').join('>'/*+shy*/).split('"').join('"');
};
// Array filter function
function removeNulls(val) { return val !== null; }
function joinPath(list) {
return list.filter(removeNulls).join('/');
}
function simplePrintf(str, subs) {
if (!str || !subs) { return str; }
var ret=[];
var s=str.parenSplit(/(%s|\$[0-9]+)/);
var i=0;
do {
ret.push(s.shift());
if ( !s.length ) { break; }
var cmd=s.shift();
if (cmd == '%s') {
if ( i < subs.length ) { ret.push(subs[i]); } else { ret.push(cmd); }
++i;
} else {
var j=parseInt( cmd.replace('$', ''), 10 ) - 1;
if ( j > -1 && j < subs.length ) { ret.push(subs[j]); } else { ret.push(cmd); }
}
} while (s.length > 0);
return ret.join('');
}
/* eslint-disable no-unused-vars */
function isString(x) { return (typeof x === 'string' || x instanceof String); }
function isNumber(x) { return (typeof x === 'number' || x instanceof Number); }
function isRegExp(x) { return x instanceof RegExp; }
function isArray (x) { return x instanceof Array; }
function isObject(x) { return x instanceof Object; }
function isFunction(x) {
return !isRegExp(x) && (typeof x === 'function' || x instanceof Function);
}
/* eslint-enable no-unused-vars */
function repeatString(s,mult) {
var ret='';
for (var i=0; i<mult; ++i) { ret += s; }
return ret;
}
function zeroFill(s, min) {
min = min || 2;
var t=s.toString();
return repeatString('0', min - t.length) + t;
}
function map(f, o) {
if (isArray(o)) { return map_array(f,o); }
return map_object(f,o);
}
function map_array(f,o) {
var ret=[];
for (var i=0; i<o.length; ++i) {
ret.push(f(o[i]));
}
return ret;
}
function map_object(f,o) {
var ret={};
for (var i in o) { ret[o]=f(o[i]); }
return ret;
}
pg.escapeQuotesHTML = function ( text ) {
return text
.replace(/&/g, "&")
.replace(/"/g, """)
.replace(/</g, "<")
.replace(/>/g, ">");
};
// ENDFILE: tools.js
// STARTFILE: dab.js
//<NOLITE>
//////////////////////////////////////////////////
// Dab-fixing code
//
function retargetDab(newTarget, oldTarget, friendlyCurrentArticleName, titleToEdit) {
log('retargetDab: newTarget='+newTarget + ' oldTarget=' + oldTarget);
return changeLinkTargetLink(
{newTarget: newTarget,
text: newTarget.split(' ').join(' '),
hint: tprintf('disambigHint', [newTarget]),
summary: simplePrintf(
getValueOf('popupFixDabsSummary'), [friendlyCurrentArticleName, newTarget ]),
clickButton: getValueOf('popupDabsAutoClick'), minor: true, oldTarget: oldTarget,
watch: getValueOf('popupWatchDisambiggedPages'),
title: titleToEdit});
}
function listLinks(wikitext, oldTarget, titleToEdit) {
// mediawiki strips trailing spaces, so we do the same
// testcase: https://en.wikipedia.org/w/index.php?title=Radial&oldid=97365633
var reg=RegExp('\\[\\[([^|]*?) *(\\||\\]\\])', 'gi');
var ret=[];
var splitted=wikitext.parenSplit(reg);
// ^[a-z]+ should match interwiki links, hopefully (case-insensitive)
// and ^[a-z]* should match those and [[:Category...]] style links too
var omitRegex=RegExp('^[a-z]*:|^[Ss]pecial:|^[Ii]mage|^[Cc]ategory');
var friendlyCurrentArticleName= oldTarget.toString();
var wikPos = getValueOf('popupDabWiktionary');
for (var i=1; i<splitted.length; i=i+3) {
if (typeof splitted[i] == typeof 'string' && splitted[i].length>0 && !omitRegex.test(splitted[i])) {
ret.push( retargetDab(splitted[i], oldTarget, friendlyCurrentArticleName, titleToEdit) );
} /* if */
} /* for loop */
ret = rmDupesFromSortedList(ret.sort());
if (wikPos) {
var wikTarget='wiktionary:' +
friendlyCurrentArticleName.replace( RegExp('^(.+)\\s+[(][^)]+[)]\\s*$'), '$1' );
var meth;
if (wikPos.toLowerCase() == 'first') { meth = 'unshift'; }
else { meth = 'push'; }
ret[meth]( retargetDab(wikTarget, oldTarget, friendlyCurrentArticleName, titleToEdit) );
}
ret.push(changeLinkTargetLink(
{ newTarget: null,
text: popupString('remove this link').split(' ').join(' '),
hint: popupString("remove all links to this disambig page from this article"),
clickButton: getValueOf('popupDabsAutoClick'), oldTarget: oldTarget,
summary: simplePrintf(getValueOf('popupRmDabLinkSummary'), [friendlyCurrentArticleName]),
watch: getValueOf('popupWatchDisambiggedPages'),
title: titleToEdit
}));
return ret;
}
function rmDupesFromSortedList(list) {
var ret=[];
for (var i=0; i<list.length; ++i) {
if (ret.length === 0 || list[i]!=ret[ret.length-1]) { ret.push(list[i]); }
}
return ret;
}
function makeFixDab(data, navpop) {
// grab title from parent popup if there is one; default exists in changeLinkTargetLink
var titleToEdit=(navpop.parentPopup && navpop.parentPopup.article.toString());
var list=listLinks(data, navpop.originalArticle, titleToEdit);
if (list.length === 0) { log('listLinks returned empty list'); return null; }
var html='<hr />' + popupString('Click to disambiguate this link to:') + '<br>';
html+=list.join(', ');
return html;
}
function makeFixDabs(wikiText, navpop) {
if (getValueOf('popupFixDabs') && isDisambig(wikiText, navpop.article) &&
Title.fromURL(location.href).namespaceId() != pg.nsSpecialId &&
navpop.article.talkPage() ) {
setPopupHTML(makeFixDab(wikiText, navpop), 'popupFixDab', navpop.idNumber);
}
}
function popupRedlinkHTML(article) {
return changeLinkTargetLink(
{ newTarget: null, text: popupString('remove this link').split(' ').join(' '),
hint: popupString("remove all links to this page from this article"),
clickButton: getValueOf('popupRedlinkAutoClick'),
oldTarget: article.toString(),
summary: simplePrintf(getValueOf('popupRedlinkSummary'), [article.toString()])});
}
//</NOLITE>
// ENDFILE: dab.js
// STARTFILE: htmloutput.js
// this has to use a timer loop as we don't know if the DOM element exists when we want to set the text
function setPopupHTML (str, elementId, popupId, onSuccess, append) {
if (typeof popupId === 'undefined') {
//console.error('popupId is not defined in setPopupHTML, html='+str.substring(0,100));
popupId = pg.idNumber;
}
var popupElement=document.getElementById(elementId+popupId);
if (popupElement) {
if (!append) { popupElement.innerHTML=''; }
if (isString(str)) {
popupElement.innerHTML+=str;
} else {
popupElement.appendChild(str);
}
if (onSuccess) { onSuccess(); }
setTimeout(checkPopupPosition, 100);
return true;
} else {
// call this function again in a little while...
setTimeout(function(){
setPopupHTML(str,elementId,popupId,onSuccess);
}, 600);
}
return null;
}
//<NOLITE>
function setPopupTrailer(str,id) {return setPopupHTML(str, 'popupData', id);}
//</NOLITE>
// args.navpopup is mandatory
// optional: args.redir, args.redirTarget
// FIXME: ye gods, this is ugly stuff
function fillEmptySpans(args) {
// if redir is present and true then redirTarget is mandatory
var redir=true;
var rcid;
if (typeof args != 'object' || typeof args.redir == 'undefined' || !args.redir) { redir=false; }
var a=args.navpopup.parentAnchor;
var article, hint=null, oldid=null, params={};
if (redir && typeof args.redirTarget == typeof {}) {
article=args.redirTarget;
//hint=article.hintValue();
} else {
article=(new Title()).fromAnchor(a);
hint=a.originalTitle || article.hintValue();
params=parseParams(a.href);
oldid=(getValueOf('popupHistoricalLinks')) ? params.oldid : null;
rcid=params.rcid;
}
var x={ article:article, hint: hint, oldid: oldid, rcid: rcid, navpop:args.navpopup, params:params };
var structure=pg.structures[getValueOf('popupStructure')];
if (typeof structure != 'object') {
setPopupHTML('popupError', 'Unknown structure (this should never happen): '+
pg.option.popupStructure, args.navpopup.idNumber);
return;
}
var spans=flatten(pg.misc.layout);
var numspans = spans.length;
var redirs=pg.misc.redirSpans;
for (var i=0; i<numspans; ++i) {
var found = redirs && (redirs.indexOf( spans[i] ) !== -1);
//log('redir='+redir+', found='+found+', spans[i]='+spans[i]);
if ( (found && !redir) || (!found && redir) ) {
//log('skipping this set of the loop');
continue;
}
var structurefn=structure[spans[i]];
var setfn = setPopupHTML;
if (getValueOf('popupActiveNavlinks') &&
(spans[i].indexOf('popupTopLinks')===0 || spans[i].indexOf('popupRedirTopLinks')===0)
) {
setfn = setPopupTipsAndHTML;
}
switch (typeof structurefn) {
case 'function':
log('running '+spans[i]+'({article:'+x.article+', hint:'+x.hint+', oldid: '+x.oldid+'})');
setfn(structurefn(x), spans[i], args.navpopup.idNumber);
break;
case 'string':
setfn(structurefn, spans[i], args.navpopup.idNumber);
break;
default:
errlog('unknown thing with label '+spans[i] + ' (span index was ' + i + ')');
break;
}
}
}
// flatten an array
function flatten(list, start) {
var ret=[];
if (typeof start == 'undefined') { start=0; }
for (var i=start; i<list.length; ++i) {
if (typeof list[i] == typeof []) {
return ret.concat(flatten(list[i])).concat(flatten(list, i+1));
}
else { ret.push(list[i]); }
}
return ret;
}
// Generate html for whole popup
function popupHTML (a) {
getValueOf('popupStructure');
var structure=pg.structures[pg.option.popupStructure];
if (typeof structure != 'object') {
//return 'Unknown structure: '+pg.option.popupStructure;
// override user choice
pg.option.popupStructure=pg.optionDefault.popupStructure;
return popupHTML(a);
}
if (typeof structure.popupLayout != 'function') { return 'Bad layout'; }
pg.misc.layout=structure.popupLayout();
if (typeof structure.popupRedirSpans === 'function') { pg.misc.redirSpans=structure.popupRedirSpans(); }
else { pg.misc.redirSpans=[]; }
return makeEmptySpans(pg.misc.layout, a.navpopup);
}
function makeEmptySpans (list, navpop) {
var ret='';
for (var i=0; i<list.length; ++i) {
if (typeof list[i] == typeof '') {
ret += emptySpanHTML(list[i], navpop.idNumber, 'div');
} else if (typeof list[i] == typeof [] && list[i].length > 0 ) {
ret = ret.parenSplit(RegExp('(</[^>]*?>$)')).join(makeEmptySpans(list[i], navpop));
} else if (typeof list[i] == typeof {} && list[i].nodeType ) {
ret += emptySpanHTML(list[i].name, navpop.idNumber, list[i].nodeType);
}
}
return ret;
}
function emptySpanHTML(name, id, tag, classname) {
tag = tag || 'span';
if (!classname) { classname = emptySpanHTML.classAliases[name]; }
classname = classname || name;
if (name == getValueOf('popupDragHandle')) { classname += ' popupDragHandle'; }
return simplePrintf('<%s id="%s" class="%s"></%s>', [tag, name + id, classname, tag]);
}
emptySpanHTML.classAliases={ 'popupSecondPreview': 'popupPreview' };
// generate html for popup image
// <a id="popupImageLinkn"><img id="popupImagen">
// where n=idNumber
function imageHTML(article, idNumber) {
return simplePrintf('<a id="popupImageLink$1">' +
'<img align="right" valign="top" id="popupImg$1" style="display: none;"></img>' +
'</a>', [ idNumber ]);
}
function popTipsSoonFn(id, when, popData) {
if (!when) { when=250; }
var popTips=function(){ setupTooltips(document.getElementById(id), false, true, popData); };
return function() { setTimeout( popTips, when, popData ); };
}
function setPopupTipsAndHTML(html, divname, idnumber, popData) {
setPopupHTML(html, divname, idnumber,
getValueOf('popupSubpopups') ?
popTipsSoonFn(divname + idnumber, null, popData) :
null);
}
// ENDFILE: htmloutput.js
// STARTFILE: mouseout.js
//////////////////////////////////////////////////
// fuzzy checks
function fuzzyCursorOffMenus(x,y, fuzz, parent) {
if (!parent) { return null; }
var uls=parent.getElementsByTagName('ul');
for (var i=0; i<uls.length; ++i) {
if (uls[i].className=='popup_menu') {
if (uls[i].offsetWidth > 0) return false;
} // else {document.title+='.';}
}
return true;
}
function checkPopupPosition () { // stop the popup running off the right of the screen
// FIXME avoid pg.current.link
if (pg.current.link && pg.current.link.navpopup)
pg.current.link.navpopup.limitHorizontalPosition();
}
function mouseOutWikiLink () {
//console ('mouseOutWikiLink');
var a=this;
removeModifierKeyHandler(a);
if (a.navpopup === null || typeof a.navpopup === 'undefined') return;
if ( ! a.navpopup.isVisible() ) {
a.navpopup.banish();
return;
}
restoreTitle(a);
Navpopup.tracker.addHook(posCheckerHook(a.navpopup));
}
function posCheckerHook(navpop) {
return function() {
if (!navpop.isVisible()) { return true; /* remove this hook */ }
if (Navpopup.tracker.dirty) {
return false;
}
var x=Navpopup.tracker.x, y=Navpopup.tracker.y;
var mouseOverNavpop = navpop.isWithin(x,y,navpop.fuzz, navpop.mainDiv) ||
!fuzzyCursorOffMenus(x,y,navpop.fuzz, navpop.mainDiv);
// FIXME it'd be prettier to do this internal to the Navpopup objects
var t=getValueOf('popupHideDelay');
if (t) { t = t * 1000; }
if (!t) {
if(!mouseOverNavpop) {
if(navpop.parentAnchor) {
restoreTitle( navpop.parentAnchor );
}
navpop.banish();
return true; /* remove this hook */
}
return false;
}
// we have a hide delay set
var d=+(new Date());
if ( !navpop.mouseLeavingTime ) {
navpop.mouseLeavingTime = d;
return false;
}
if ( mouseOverNavpop ) {
navpop.mouseLeavingTime=null;
return false;
}
if (d - navpop.mouseLeavingTime > t) {
navpop.mouseLeavingTime=null;
navpop.banish(); return true; /* remove this hook */
}
return false;
};
}
function runStopPopupTimer(navpop) {
// at this point, we should have left the link but remain within the popup
// so we call this function again until we leave the popup.
if (!navpop.stopPopupTimer) {
navpop.stopPopupTimer=setInterval(posCheckerHook(navpop), 500);
navpop.addHook(function(){clearInterval(navpop.stopPopupTimer);},
'hide', 'before');
}
}
// ENDFILE: mouseout.js
// STARTFILE: previewmaker.js
/**
@fileoverview
Defines the {@link Previewmaker} object, which generates short previews from wiki markup.
*/
/**
Creates a new Previewmaker
@constructor
@class The Previewmaker class. Use an instance of this to generate short previews from Wikitext.
@param {String} wikiText The Wikitext source of the page we wish to preview.
@param {String} baseUrl The url we should prepend when creating relative urls.
@param {Navpopup} owner The navpop associated to this preview generator
*/
function Previewmaker(wikiText, baseUrl, owner) {
/** The wikitext which is manipulated to generate the preview. */
this.originalData=wikiText;
this.baseUrl=baseUrl;
this.owner=owner;
this.maxCharacters=getValueOf('popupMaxPreviewCharacters');
this.maxSentences=getValueOf('popupMaxPreviewSentences');
this.setData();
}
Previewmaker.prototype.setData=function() {
var maxSize=Math.max(10000, 2*this.maxCharacters);
this.data=this.originalData.substring(0,maxSize);
};
/** Remove HTML comments
@private
*/
Previewmaker.prototype.killComments = function () {
// this also kills one trailing newline, eg [[diamyo]]
this.data=this.data.replace(RegExp('^<!--[^$]*?-->\\n|\\n<!--[^$]*?-->(?=\\n)|<!--[^$]*?-->', 'g'), '');
};
/**
@private
*/
Previewmaker.prototype.killDivs = function () {
// say goodbye, divs (can be nested, so use * not *?)
this.data=this.data.replace(RegExp('< *div[^>]* *>[\\s\\S]*?< */ *div *>',
'gi'), '');
};
/**
@private
*/
Previewmaker.prototype.killGalleries = function () {
this.data=this.data.replace(RegExp('< *gallery[^>]* *>[\\s\\S]*?< */ *gallery *>',
'gi'), '');
};
/**
@private
*/
Previewmaker.prototype.kill = function(opening, closing, subopening, subclosing, repl) {
var oldk=this.data;
var k=this.killStuff(this.data, opening, closing, subopening, subclosing, repl);
while (k.length < oldk.length) {
oldk=k;
k=this.killStuff(k, opening, closing, subopening, subclosing, repl);
}
this.data=k;
};
/**
@private
*/
Previewmaker.prototype.killStuff = function (txt, opening, closing, subopening, subclosing, repl) {
var op=this.makeRegexp(opening);
var cl=this.makeRegexp(closing, '^');
var sb=subopening ? this.makeRegexp(subopening, '^') : null;
var sc=subclosing ? this.makeRegexp(subclosing, '^') : cl;
if (!op || !cl) {
alert('Navigation Popups error: op or cl is null! something is wrong.');
return;
}
if (!op.test(txt)) { return txt; }
var ret='';
var opResult = op.exec(txt);
ret = txt.substring(0,opResult.index);
txt=txt.substring(opResult.index+opResult[0].length);
var depth = 1;
while (txt.length > 0) {
var removal=0;
if (depth==1 && cl.test(txt)) {
depth--;
removal=cl.exec(txt)[0].length;
} else if (depth > 1 && sc.test(txt)) {
depth--;
removal=sc.exec(txt)[0].length;
}else if (sb && sb.test(txt)) {
depth++;
removal=sb.exec(txt)[0].length;
}
if ( !removal ) { removal = 1; }
txt=txt.substring(removal);
if (depth === 0) { break; }
}
return ret + (repl || '') + txt;
};
/**
@private
*/
Previewmaker.prototype.makeRegexp = function (x, prefix, suffix) {
prefix = prefix || '';
suffix = suffix || '';
var reStr='';
var flags='';
if (isString(x)) {
reStr=prefix + literalizeRegex(x) + suffix;
} else if (isRegExp(x)) {
var s=x.toString().substring(1);
var sp=s.split('/');
flags=sp[sp.length-1];
sp[sp.length-1]='';
s=sp.join('/');
s=s.substring(0,s.length-1);
reStr= prefix + s + suffix;
} else {
log ('makeRegexp failed');
}
log ('makeRegexp: got reStr=' + reStr + ', flags=' + flags);
return RegExp(reStr, flags);
};
/**
@private
*/
Previewmaker.prototype.killBoxTemplates = function () {
// taxobox removal... in fact, there's a saudiprincebox_begin, so let's be more general
// also, have float_begin, ... float_end
this.kill(RegExp('[{][{][^{}\\s|]*?(float|box)[_ ](begin|start)', 'i'), /[}][}]\s*/, '{{');
// infoboxes etc
// from [[User:Zyxw/popups.js]]: kill frames too
this.kill(RegExp('[{][{][^{}\\s|]*?(infobox|elementbox|frame)[_ ]', 'i'), /[}][}]\s*/, '{{');
};
/**
@private
*/
Previewmaker.prototype.killTemplates = function () {
// the plus sign is to avoid transcluding [[Template:', ']]
this.kill('{'+'{', '}'+'}', '{', '}', ' ');
};
/**
@private
*/
Previewmaker.prototype.killTables = function () {
// tables are bad, too
// this can be slow, but it's an inprovement over a browser hang
// torture test: [[Comparison_of_Intel_Central_Processing_Units]]
this.kill('{|', /[|]}\s*/, '{|');
this.kill(/<table.*?>/i, /<\/table.*?>/i, /<table.*?>/i);
// remove lines starting with a pipe for the hell of it (?)
this.data=this.data.replace(RegExp('^[|].*$', 'mg'), '');
};
/**
@private
*/
Previewmaker.prototype.killImages = function () {
var forbiddenNamespaceAliases = [];
jQuery.each(mw.config.get('wgNamespaceIds'), function(_localizedNamespaceLc, _namespaceId) {
if (_namespaceId!=pg.nsImageId && _namespaceId!=pg.nsCategoryId) return;
forbiddenNamespaceAliases.push(_localizedNamespaceLc.split(' ').join('[ _]')); //todo: escape regexp fragments!
});
// images and categories are a nono
this.kill(RegExp('[[][[]\\s*(' + forbiddenNamespaceAliases.join('|') + ')\\s*:', 'i'),
/\]\]\s*/, '[', ']');
};
/**
@private
*/
Previewmaker.prototype.killHTML = function () {
// kill <ref ...>...</ref>
this.kill(/<ref\b[^/>]*?>/i, /<\/ref>/i);
// let's also delete entire lines starting with <. it's worth a try.
this.data=this.data.replace(RegExp('(^|\\n) *<.*', 'g'), '\n');
// and those pesky html tags, but not <nowiki> or <blockquote>
var splitted=this.data.parenSplit(/(<[\w\W]*?(?:>|$|(?=<)))/);
var len=splitted.length;
for (var i=1; i<len; i=i+2) {
switch (splitted[i]) {
case '<nowiki>':
case '</nowiki>':
case '<blockquote>':
case '</blockquote>':
break;
default:
splitted[i]='';
}
}
this.data=splitted.join('');
};
/**
@private
*/
Previewmaker.prototype.killChunks = function() { // heuristics alert
// chunks of italic text? you crazy, man?
var italicChunkRegex=new RegExp
("((^|\\n)\\s*:*\\s*''[^']([^']|'''|'[^']){20}(.|\\n[^\\n])*''[.!?\\s]*\\n)+", 'g');
// keep stuff separated, though, so stick in \n (fixes [[Union Jack]]?
this.data=this.data.replace(italicChunkRegex, '\n');
};
/**
@private
*/
Previewmaker.prototype.mopup = function () {
// we simply *can't* be doing with horizontal rules right now
this.data=this.data.replace(RegExp('^-{4,}','mg'),'');
// no indented lines
this.data=this.data.replace(RegExp('(^|\\n) *:[^\\n]*','g'), '');
// replace __TOC__, __NOTOC__ and whatever else there is
// this'll probably do
this.data=this.data.replace(RegExp('^__[A-Z_]*__ *$', 'gmi'),'');
};
/**
@private
*/
Previewmaker.prototype.firstBit = function () {
// dont't be givin' me no subsequent paragraphs, you hear me?
/// first we "normalize" section headings, removing whitespace after, adding before
var d=this.data;
if (getValueOf('popupPreviewCutHeadings')) {
this.data=this.data.replace(RegExp('\\s*(==+[^=]*==+)\\s*', 'g'), '\n\n$1 ');
/// then we want to get rid of paragraph breaks whose text ends badly
this.data=this.data.replace(RegExp('([:;]) *\\n{2,}', 'g'), '$1\n');
this.data=this.data.replace(RegExp('^[\\s\\n]*'), '');
var stuff=(RegExp('^([^\\n]|\\n[^\\n\\s])*')).exec(this.data);
if (stuff) { d = stuff[0]; }
if (!getValueOf('popupPreviewFirstParOnly')) { d = this.data; }
/// now put \n\n after sections so that bullets and numbered lists work
d=d.replace(RegExp('(==+[^=]*==+)\\s*', 'g'), '$1\n\n');
}
// Split sentences. Superfluous sentences are RIGHT OUT.
// note: exactly 1 set of parens here needed to make the slice work
d = d.parenSplit(RegExp('([!?.]+["'+"'"+']*\\s)','g'));
// leading space is bad, mmkay?
d[0]=d[0].replace(RegExp('^\\s*'), '');
var notSentenceEnds=RegExp('([^.][a-z][.] *[a-z]|etc|sic|Dr|Mr|Mrs|Ms|St|no|op|cit|\\[[^\\]]*|\\s[A-Zvclm])$', 'i');
d = this.fixSentenceEnds(d, notSentenceEnds);
this.fullLength=d.join('').length;
var n=this.maxSentences;
var dd=this.firstSentences(d,n);
do {
dd=this.firstSentences(d,n); --n;
} while ( dd.length > this.maxCharacters && n !== 0 );
this.data = dd;
};
/**
@private
*/
Previewmaker.prototype.fixSentenceEnds = function(strs, reg) {
// take an array of strings, strs
// join strs[i] to strs[i+1] & strs[i+2] if strs[i] matches regex reg
for (var i=0; i<strs.length-2; ++i) {
if (reg.test(strs[i])) {
var a=[];
for (var j=0; j<strs.length; ++j) {
if (j<i) a[j]=strs[j];
if (j==i) a[i]=strs[i]+strs[i+1]+strs[i+2];
if (j>i+2) a[j-2]=strs[j];
}
return this.fixSentenceEnds(a,reg);
}
}
return strs;
};
/**
@private
*/
Previewmaker.prototype.firstSentences = function(strs, howmany) {
var t=strs.slice(0, 2*howmany);
return t.join('');
};
/**
@private
*/
Previewmaker.prototype.killBadWhitespace = function() {
// also cleans up isolated '''', eg [[Suntory Sungoliath]]
this.data=this.data.replace(RegExp('^ *\'+ *$', 'gm'), '');
};
/**
Runs the various methods to generate the preview.
The preview is stored in the <code>html</html> field.
@private
*/
Previewmaker.prototype.makePreview = function() {
if (this.owner.article.namespaceId()!=pg.nsTemplateId &&
this.owner.article.namespaceId()!=pg.nsImageId ) {
this.killComments();
this.killDivs();
this.killGalleries();
this.killBoxTemplates();
if (getValueOf('popupPreviewKillTemplates')) {
this.killTemplates();
} else {
this.killMultilineTemplates();
}
this.killTables();
this.killImages();
this.killHTML();
this.killChunks();
this.mopup();
this.firstBit();
this.killBadWhitespace();
}
else
{
this.killHTML();
}
this.html=wiki2html(this.data, this.baseUrl); // needs livepreview
this.fixHTML();
this.stripLongTemplates();
};
/**
@private
*/
Previewmaker.prototype.esWiki2HtmlPart = function(data) {
var reLinks = /(?:\[\[([^|\]]*)(?:\|([^|\]]*))*]]([a-z]*))/gi; //match a wikilink
reLinks.lastIndex = 0; //reset regex
var match;
var result = "";
var postfixIndex = 0;
while ((match = reLinks.exec(data))) //match all wikilinks
{
//FIXME: the way that link is built here isn't perfect. It is clickable, but popups preview won't recognize it in some cases.
result += pg.escapeQuotesHTML(data.substring(postfixIndex, match.index)) +
'<a href="'+Insta.conf.paths.articles+pg.escapeQuotesHTML(match[1])+'">'+pg.escapeQuotesHTML((match[2]?match[2]:match[1])+match[3])+"</a>";
postfixIndex = reLinks.lastIndex;
}
//append the rest
result += pg.escapeQuotesHTML(data.substring(postfixIndex));
return result;
};
Previewmaker.prototype.editSummaryPreview=function() {
var reAes = /\/\* *(.*?) *\*\//g; //match the first section marker
reAes.lastIndex = 0; //reset regex
var match;
match = reAes.exec(this.data);
if (match)
{
//we have a section link. Split it, process it, combine it.
var prefix = this.data.substring(0,match.index-1);
var section = match[1];
var postfix = this.data.substring(reAes.lastIndex);
var start = "<span class='autocomment'>";
var end = "</span>";
if (prefix.length>0) start = this.esWiki2HtmlPart(prefix) + " " + start + "- ";
if (postfix.length>0) end = ": " + end + this.esWiki2HtmlPart(postfix);
var t=new Title().fromURL(this.baseUrl);
t.anchorFromUtf(section);
var sectionLink = Insta.conf.paths.articles + pg.escapeQuotesHTML(t.toString(true)) + '#' + pg.escapeQuotesHTML(t.anchor);
return start + '<a href="'+sectionLink+'">→</a> '+pg.escapeQuotesHTML(section) + end;
}
//else there's no section link, htmlify the whole thing.
return this.esWiki2HtmlPart(this.data);
};
//<NOLITE>
/** Test function for debugging preview problems one step at a time.
*/
/*eslint-disable */
function previewSteps(txt) {
try {
txt=txt || document.editform.wpTextbox1.value;
} catch (err) {
if (pg.cache.pages.length > 0) {
txt=pg.cache.pages[pg.cache.pages.length-1].data;
} else {
alert('provide text or use an edit page');
}
}
txt=txt.substring(0,10000);
var base=pg.wiki.articlebase + Title.fromURL(document.location.href).urlString();
var p=new Previewmaker(txt, base, pg.current.link.navpopup);
if (this.owner.article.namespaceId() != pg.nsTemplateId) {
p.killComments(); if (!confirm('done killComments(). Continue?\n---\n' + p.data)) { return; }
p.killDivs(); if (!confirm('done killDivs(). Continue?\n---\n' + p.data)) { return; }
p.killGalleries(); if (!confirm('done killGalleries(). Continue?\n---\n' + p.data)) { return; }
p.killBoxTemplates(); if (!confirm('done killBoxTemplates(). Continue?\n---\n' + p.data)) { return; }
if (getValueOf('popupPreviewKillTemplates')) {
p.killTemplates(); if (!confirm('done killTemplates(). Continue?\n---\n' + p.data)) { return; }
} else {
p.killMultilineTemplates(); if (!confirm('done killMultilineTemplates(). Continue?\n---\n' + p.data)) { return; }
}
p.killTables(); if (!confirm('done killTables(). Continue?\n---\n' + p.data)) { return; }
p.killImages(); if (!confirm('done killImages(). Continue?\n---\n' + p.data)) { return; }
p.killHTML(); if (!confirm('done killHTML(). Continue?\n---\n' + p.data)) { return; }
p.killChunks(); if (!confirm('done killChunks(). Continue?\n---\n' + p.data)) { return; }
p.mopup(); if (!confirm('done mopup(). Continue?\n---\n' + p.data)) { return; }
p.firstBit(); if (!confirm('done firstBit(). Continue?\n---\n' + p.data)) { return; }
p.killBadWhitespace(); if (!confirm('done killBadWhitespace(). Continue?\n---\n' + p.data)) { return; }
}
p.html=wiki2html(p.data, base); // needs livepreview
p.fixHTML(); if (!confirm('done fixHTML(). Continue?\n---\n' + p.html)) { return; }
p.stripLongTemplates(); if (!confirm('done stripLongTemplates(). Continue?\n---\n' + p.html)) { return; }
alert('finished preview - end result follows.\n---\n' + p.html);
}
/*eslint-enable */
//</NOLITE>
/**
Works around livepreview bugs.
@private
*/
Previewmaker.prototype.fixHTML = function() {
if(!this.html) return;
var ret = this.html;
// fix question marks in wiki links
// maybe this'll break some stuff :-(
ret=ret.replace(RegExp('(<a href="' + pg.wiki.articlePath + '/[^"]*)[?](.*?")', 'g'), '$1%3F$2');
ret=ret.replace(RegExp('(<a href=\'' + pg.wiki.articlePath + '/[^\']*)[?](.*?\')', 'g'), '$1%3F$2');
// FIXME fix up % too
this.html=ret;
};
/**
Generates the preview and displays it in the current popup.
Does nothing if the generated preview is invalid or consists of whitespace only.
Also activates wikilinks in the preview for subpopups if the popupSubpopups option is true.
*/
Previewmaker.prototype.showPreview = function () {
this.makePreview();
if (typeof this.html != typeof '') return;
if (RegExp('^\\s*$').test(this.html)) return;
setPopupHTML('<hr />', 'popupPrePreviewSep', this.owner.idNumber);
setPopupTipsAndHTML(this.html, 'popupPreview', this.owner.idNumber, { owner: this.owner });
var more = (this.fullLength > this.data.length) ? this.moreLink() : '';
setPopupHTML(more, 'popupPreviewMore', this.owner.idNumber);
};
/**
@private
*/
Previewmaker.prototype.moreLink=function() {
var a=document.createElement('a');
a.className='popupMoreLink';
a.innerHTML=popupString('more...');
var savedThis=this;
a.onclick=function() {
savedThis.maxCharacters+=2000;
savedThis.maxSentences+=20;
savedThis.setData();
savedThis.showPreview();
};
return a;
};
/**
@private
*/
Previewmaker.prototype.stripLongTemplates = function() {
// operates on the HTML!
this.html=this.html.replace(RegExp('^.{0,1000}[{][{][^}]*?(<(p|br)( /)?>\\s*){2,}([^{}]*?[}][}])?', 'gi'), '');
this.html=this.html.split('\n').join(' '); // workaround for <pre> templates
this.html=this.html.replace(RegExp('[{][{][^}]*<pre>[^}]*[}][}]','gi'), '');
};
/**
@private
*/
Previewmaker.prototype.killMultilineTemplates = function() {
this.kill('{{{', '}}}');
this.kill(RegExp('\\s*[{][{][^{}]*\\n'), '}}', '{{');
};
// ENDFILE: previewmaker.js
// STARTFILE: querypreview.js
function loadAPIPreview(queryType, article, navpop) {
var art=new Title(article).urlString();
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&';
var htmlGenerator=function(/*a, d*/){alert('invalid html generator');};
var usernameart = '';
switch (queryType) {
case 'history':
url += 'titles=' + art + '&prop=revisions&rvlimit=' +
getValueOf('popupHistoryPreviewLimit');
htmlGenerator=APIhistoryPreviewHTML;
break;
case 'category':
url += 'list=categorymembers&cmtitle=' + art;
htmlGenerator=APIcategoryPreviewHTML;
break;
case 'userinfo':
var username = new Title( article ).userName();
usernameart = encodeURIComponent( username );
if (pg.re.ipUser.test(username)) {
url += 'list=blocks&bkprop=range|restrictions&bkip=' + usernameart;
} else {
url += 'list=users|usercontribs&usprop=blockinfo|groups|editcount|registration|gender&ususers=' + usernameart + "&meta=globaluserinfo&guiprop=groups|unattached&guiuser="+ usernameart + "&uclimit=1&ucprop=timestamp&ucuser=" + usernameart;
}
htmlGenerator=APIuserInfoPreviewHTML;
break;
case 'contribs':
usernameart = encodeURIComponent( new Title( article ).userName() );
url += 'list=usercontribs&ucuser=' + usernameart +
'&uclimit=' + getValueOf('popupContribsPreviewLimit');
htmlGenerator=APIcontribsPreviewHTML;
break;
case 'imagepagepreview':
var trail='';
if (getValueOf('popupImageLinks')) { trail = '&list=imageusage&iutitle=' + art; }
url += 'titles=' + art + '&prop=revisions|imageinfo&rvprop=content' + trail;
htmlGenerator=APIimagepagePreviewHTML;
break;
case 'backlinks':
url += 'list=backlinks&bltitle=' + art;
htmlGenerator=APIbacklinksPreviewHTML;
break;
case 'revision':
if (article.oldid) {
url += 'revids=' + article.oldid;
} else {
url += 'titles=' + article.removeAnchor().urlString();
}
url += '&prop=revisions|pageprops|info|images|categories&rvprop=ids|timestamp|flags|comment|user|content&cllimit=max&imlimit=max';
htmlGenerator=APIrevisionPreviewHTML;
break;
}
pendingNavpopTask(navpop);
var callback=function(d){
log( "callback of API functions was hit" );
showAPIPreview(queryType, htmlGenerator(article,d,navpop), navpop.idNumber, navpop, d);
};
var go = function(){
getPageWithCaching(url, callback, navpop);
return true;
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'before', 'DOWNLOAD_'+queryType+'_QUERY_DATA'); }
}
function linkList(list) {
list.sort(function(x,y) { return (x==y ? 0 : (x<y ? -1 : 1)); });
var buf=[];
for (var i=0; i<list.length; ++i) {
buf.push(wikiLink({article: new Title(list[i]),
text: list[i].split(' ').join(' '),
action: 'view'}));
}
return buf.join(', ');
}
function getTimeOffset() {
var tz = mw.user.options.get('timecorrection');
if(tz) {
if( tz.indexOf('|') > -1 ) {
// New format
return parseInt(tz.split('|')[1],10);
} else if ( tz.indexOf(':') > -1 ) {
// Old format
return( parseInt(tz,10)*60 + parseInt(tz.split(':')[1],10) );
}
}
return 0;
}
/*
* Creates a HTML table that's shown in the history and user-contribs popups.
* @param {Object[]} h - a list of revisions, returned from the API
* @param {boolean} reallyContribs - true only if we're displaying user contributions
*/
function editPreviewTable(article, h, reallyContribs, timeOffset) {
var html=['<table>'];
var day=null;
var curart=article;
var page=null;
var makeFirstColumnLinks;
if(reallyContribs) {
// We're showing user contributions, so make (diff | hist) links
makeFirstColumnLinks = function(currentRevision) {
var result = '(';
result += '<a href="' + pg.wiki.titlebase +
new Title(currentRevision.title).urlString() + '&diff=prev' +
'&oldid=' + currentRevision.revid + '">' + popupString('diff') + '</a>';
result += ' | ';
result += '<a href="' + pg.wiki.titlebase +
new Title(currentRevision.title).urlString() + '&action=history">' +
popupString('hist') + '</a>';
result += ')';
return result;
};
} else {
// It's a regular history page, so make (cur | last) links
var firstRevid = h[0].revid;
makeFirstColumnLinks = function(currentRevision) {
var result = '(';
result += '<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&diff=' + firstRevid + '&oldid=' + currentRevision.revid + '">' + popupString('cur') + '</a>';
result += ' | ';
result += '<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&diff=prev&oldid=' + currentRevision.revid + '">' + popupString('last') + '</a>';
result += ')';
return result;
};
}
for (var i=0; i<h.length; ++i) {
if (reallyContribs) {
page = h[i].title;
curart = new Title(page);
}
var minor = h[i].minor ? '<b>m </b>' : '';
var editDate = adjustDate(getDateFromTimestamp(h[i].timestamp), timeOffset);
var thisDay = dayFormat(editDate);
var thisTime = timeFormat(editDate);
if (thisDay == day) {
thisDay = '';
} else {
day = thisDay;
}
if (thisDay) {
html.push( '<tr><td colspan=3><span class="popup_history_date">' +
thisDay+'</span></td></tr>' );
}
html.push('<tr class="popup_history_row_' + ( (i%2) ? 'odd' : 'even') + '">');
html.push('<td>' + makeFirstColumnLinks(h[i]) + '</td>');
html.push('<td>' +
'<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&oldid=' + h[i].revid + '">' + thisTime + '</a></td>');
var col3url='', col3txt='';
if (!reallyContribs) {
var user=h[i].user;
if( !h[i].userhidden ) {
if( pg.re.ipUser.test(user) ) {
col3url=pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId] + ':Contributions&target=' + new Title(user).urlString();
} else {
col3url=pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + new Title(user).urlString();
}
col3txt=pg.escapeQuotesHTML(user);
} else {
col3url=getValueOf('popupRevDelUrl');
col3txt=pg.escapeQuotesHTML( popupString('revdel'));
}
} else {
col3url=pg.wiki.titlebase + curart.urlString();
col3txt=pg.escapeQuotesHTML(page);
}
html.push('<td>' + (reallyContribs ? minor : '') +
'<a href="' + col3url + '">' + col3txt + '</a></td>');
var comment='';
var c=h[i].comment || h[i].content;
if (c) {
comment=new Previewmaker(c, new Title(curart).toUrl()).editSummaryPreview();
} else if ( h[i].commenthidden ) {
comment=popupString('revdel');
}
html.push('<td>' + (!reallyContribs ? minor : '') + comment + '</td>');
html.push('</tr>');
html=[html.join('')];
}
html.push('</table>');
return html.join('');
}
function getDateFromTimestamp(t) {
var s=t.split(/[^0-9]/);
switch(s.length) {
case 0: return null;
case 1: return new Date(s[0]);
case 2: return new Date(s[0], s[1]-1);
case 3: return new Date(s[0], s[1]-1, s[2]);
case 4: return new Date(s[0], s[1]-1, s[2], s[3]);
case 5: return new Date(s[0], s[1]-1, s[2], s[3], s[4]);
case 6: return new Date(s[0], s[1]-1, s[2], s[3], s[4], s[5]);
default: return new Date(s[0], s[1]-1, s[2], s[3], s[4], s[5], s[6]);
}
}
function adjustDate(d, offset) {
// offset is in minutes
var o=offset * 60 * 1000;
return new Date( +d + o);
}
function dayFormat(editDate, utc) {
if (utc) { return map(zeroFill, [editDate.getUTCFullYear(), editDate.getUTCMonth()+1, editDate.getUTCDate()]).join('-'); }
return map(zeroFill, [editDate.getFullYear(), editDate.getMonth()+1, editDate.getDate()]).join('-');
}
function timeFormat(editDate, utc) {
if (utc) { return map(zeroFill, [editDate.getUTCHours(), editDate.getUTCMinutes(), editDate.getUTCSeconds()]).join(':'); }
return map(zeroFill, [editDate.getHours(), editDate.getMinutes(), editDate.getSeconds()]).join(':');
}
function showAPIPreview(queryType, html, id, navpop, download) {
// DJ: done
var target='popupPreview';
completedNavpopTask(navpop);
switch (queryType) {
case 'imagelinks':
case 'category':
target='popupPostPreview'; break;
case 'userinfo':
target='popupUserData'; break;
case 'revision':
insertPreview(download);
return;
}
setPopupTipsAndHTML(html, target, id);
}
function APIrevisionPreviewHTML(article, download) {
try{
var jsObj=getJsObj(download.data);
var page=anyChild(jsObj.query.pages);
if( page.missing ) {
// TODO we need to fix this proper later on
download.owner = null;
return;
}
var content = (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
if( typeof content === 'string' )
{
download.data = content;
download.lastModified = new Date(page.revisions[0].timestamp);
}
} catch(someError) {
return 'Revision preview failed :(';
}
}
function APIbacklinksPreviewHTML(article, download/*, navpop*/ ) {
try {
var jsObj=getJsObj(download.data);
var list=jsObj.query.backlinks;
var html=[];
if (!list) { return popupString('No backlinks found'); }
for ( var i=0; i < list.length; i++ ) {
var t=new Title(list[i].title);
html.push('<a href="' + pg.wiki.titlebase + t.urlString() + '">' + t + '</a>');
}
html=html.join(', ');
if (jsObj['continue'] && jsObj['continue'].blcontinue) {
html += popupString(' and more');
}
return html;
} catch (someError) {
return 'backlinksPreviewHTML went wonky';
}
}
pg.fn.APIsharedImagePagePreviewHTML = function APIsharedImagePagePreviewHTML(obj) {
log( "APIsharedImagePagePreviewHTML" );
var popupid = obj.requestid;
if( obj.query && obj.query.pages )
{
var page=anyChild(obj.query.pages );
var content = (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
if( typeof content === 'string' )
{
/* Not entirely safe, but the best we can do */
var p=new Previewmaker(content, pg.current.link.navpopup.article, pg.current.link.navpopup);
p.makePreview();
setPopupHTML( p.html, "popupSecondPreview", popupid );
}
}
};
function APIimagepagePreviewHTML(article, download, navpop) {
try {
var jsObj=getJsObj(download.data);
var page=anyChild(jsObj.query.pages);
var content= (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
var ret='';
var alt='';
try{alt=navpop.parentAnchor.childNodes[0].alt;} catch(e){}
if (alt) {
ret = ret + '<hr /><b>' + popupString('Alt text:') + '</b> ' + pg.escapeQuotesHTML(alt);
}
if (typeof content === 'string') {
var p=prepPreviewmaker(content, article, navpop);
p.makePreview();
if (p.html) { ret += '<hr />' + p.html; }
if (getValueOf('popupSummaryData')) {
var info=getPageInfo(content, download);
log(info);
setPopupTrailer(info, navpop.idNumber);
}
}
if (page && page.imagerepository == "shared" ) {
var art=new Title(article);
var encart = encodeURIComponent( "File:" + art.stripNamespace() );
var shared_url = pg.wiki.apicommonsbase + '?format=json&formatversion=2' +
'&callback=pg.fn.APIsharedImagePagePreviewHTML' +
'&requestid=' + navpop.idNumber +
'&action=query&prop=revisions&rvprop=content&titles=' + encart;
ret = ret +'<hr />' + popupString( 'Image from Commons') +
': <a href="' + pg.wiki.commonsbase + '?title=' + encart + '">' +
popupString( 'Description page') + '</a>';
mw.loader.load( shared_url );
}
showAPIPreview('imagelinks', APIimagelinksPreviewHTML(article,download), navpop.idNumber, download);
return ret;
} catch (someError) {
return 'API imagepage preview failed :(';
}
}
function APIimagelinksPreviewHTML(article, download) {
try {
var jsobj=getJsObj(download.data);
var list=jsobj.query.imageusage;
if (list) {
var ret=[];
for (var i=0; i < list.length; i++) {
ret.push(list[i].title);
}
if (ret.length === 0) { return popupString('No image links found'); }
return '<h2>' + popupString('File links') + '</h2>' + linkList(ret);
} else {
return popupString('No image links found');
}
} catch(someError) {
return 'Image links preview generation failed :(';
}
}
function APIcategoryPreviewHTML(article, download) {
try{
var jsobj=getJsObj(download.data);
var list=jsobj.query.categorymembers;
var ret=[];
for (var p=0; p < list.length; p++) {
ret.push(list[p].title);
}
if (ret.length === 0) { return popupString('Empty category'); }
ret = '<h2>' + tprintf('Category members (%s shown)', [ret.length]) + '</h2>' +linkList(ret);
if (jsobj['continue'] && jsobj['continue'].cmcontinue) {
ret += popupString(' and more');
}
return ret;
} catch(someError) {
return 'Category preview failed :(';
}
}
function APIuserInfoPreviewHTML(article, download) {
var ret=[];
var queryobj = {};
try{
queryobj=getJsObj(download.data).query;
} catch(someError) { return 'Userinfo preview failed :('; }
var user=anyChild(queryobj.users);
if (user) {
var globaluserinfo=queryobj.globaluserinfo;
if (user.invalid === '') {
ret.push( popupString( 'Invalid user') );
} else if (user.missing === '') {
ret.push( popupString( 'Not a registered username') );
}
if( user.blockedby ) {
if( user.blockpartial ) {
ret.push('<b>' + popupString('Has blocks') + '</b>');
} else {
ret.push('<b>' + popupString('BLOCKED') + '</b>');
}
}
if( globaluserinfo && ( 'locked' in globaluserinfo || 'hidden' in globaluserinfo ) ) {
var lockedSulAccountIsAttachedToThis = true;
for( var i=0; globaluserinfo.unattached && i < globaluserinfo.unattached.length; i++) {
if ( globaluserinfo.unattached[i].wiki === mw.config.get('wgDBname') ) {
lockedSulAccountIsAttachedToThis=false;
break;
}
}
if (lockedSulAccountIsAttachedToThis) {
if ( 'locked' in globaluserinfo ) ret.push('<b><i>' + popupString('LOCKED') + '</i></b>');
if ( 'hidden' in globaluserinfo ) ret.push('<b><i>' + popupString('HIDDEN') + '</i></b>');
}
}
if( getValueOf('popupShowGender') && user.gender ) {
switch( user.gender ) {
case "male": ret.push( popupString( "\u2642" ) ); break;
case "female": ret.push( popupString( "\u2640" ) ); break;
}
}
if( user.groups ) {
for( var j=0; j < user.groups.length; j++) {
var currentGroup = user.groups[j];
if( ["*", "user", "autoconfirmed", "extendedconfirmed"].indexOf( currentGroup ) === -1 ) {
ret.push( pg.escapeQuotesHTML(user.groups[j]) );
}
}
}
if( globaluserinfo && globaluserinfo.groups ) {
for( var k=0; k < globaluserinfo.groups.length; k++) {
ret.push( '<i>'+pg.escapeQuotesHTML(globaluserinfo.groups[k])+'</i>' );
}
}
if( user.registration )
ret.push( pg.escapeQuotesHTML((user.editcount?user.editcount:'0') + popupString(' edits since: ') + (user.registration?dayFormat(getDateFromTimestamp(user.registration)):'')) );
}
if (queryobj.usercontribs && queryobj.usercontribs.length) {
ret.push( popupString('last edit on ') + dayFormat(getDateFromTimestamp(queryobj.usercontribs[0].timestamp)) );
}
if (queryobj.blocks) {
ret.push( popupString( 'IP user') ); //we only request list=blocks for IPs
for (var l=0; l<queryobj.blocks.length; l++) {
console.log(queryobj);
var rbstr = queryobj.blocks[l].rangestart === queryobj.blocks[l].rangeend ? 'BLOCK' : 'RANGEBLOCK';
rbstr = (!Array.isArray(queryobj.blocks[l].restrictions) ? 'Has ' + rbstr.toLowerCase() + 's' : rbstr + 'ED');
ret.push('<b>' + popupString(rbstr) + '</b>' );
}
}
ret = '<hr />' + ret.join( ', ' );
return ret;
}
function APIcontribsPreviewHTML(article, download, navpop) {
return APIhistoryPreviewHTML(article, download, navpop, true);
}
function APIhistoryPreviewHTML(article, download, navpop, reallyContribs) {
try {
var jsobj=getJsObj(download.data);
var edits = [];
if( reallyContribs ) {
edits=jsobj.query.usercontribs;
} else {
edits=anyChild(jsobj.query.pages).revisions;
}
var ret=editPreviewTable(article, edits, reallyContribs, getTimeOffset());
return ret;
} catch (someError) {
return 'History preview failed :-(';
}
}
//</NOLITE>
// ENDFILE: querypreview.js
// STARTFILE: debug.js
////////////////////////////////////////////////////////////////////
// Debugging functions
////////////////////////////////////////////////////////////////////
function setupDebugging() {
//<NOLITE>
if (window.popupDebug) { // popupDebug is set from .version
window.log=function(x) { //if(gMsg!='')gMsg += '\n'; gMsg+=time() + ' ' + x; };
window.console.log(x);
};
window.errlog=function(x) {
window.console.error(x);
};
log('Initializing logger');
} else {
//</NOLITE>
window.log = function() {};
window.errlog = function() {};
//<NOLITE>
}
//</NOLITE>
}
// ENDFILE: debug.js
// STARTFILE: images.js
// load image of type Title.
function loadImage(image, navpop) {
if (typeof image.stripNamespace != 'function') { alert('loadImages bad'); }
// API call to retrieve image info.
if ( !getValueOf('popupImages') ) return;
if ( !isValidImageName(image) ) return false;
var art=image.urlString();
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query';
url += '&prop=imageinfo&iiprop=url|mime&iiurlwidth=' + getValueOf('popupImageSizeLarge');
url += '&titles=' + art;
pendingNavpopTask(navpop);
var callback=function(d){
popupsInsertImage(navpop.idNumber, navpop, d);
};
var go = function(){
getPageWithCaching(url, callback, navpop);
return true;
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'after', 'DOWNLOAD_IMAGE_QUERY_DATA'); }
}
function popupsInsertImage(id, navpop, download) {
log( "popupsInsertImage");
var imageinfo;
try {
var jsObj=getJsObj(download.data);
var imagepage=anyChild(jsObj.query.pages);
if (typeof imagepage.imageinfo === 'undefined') return;
imageinfo = imagepage.imageinfo[0];
} catch (someError) {
log( "popupsInsertImage failed :(" );
return;
}
var popupImage = document.getElementById("popupImg"+id);
if (!popupImage) {
log( "could not find insertion point for image");
return;
}
popupImage.width=getValueOf('popupImageSize');
popupImage.style.display='inline';
// Set the source for the image.
if( imageinfo.thumburl )
popupImage.src=imageinfo.thumburl;
else if( imageinfo.mime.indexOf("image") === 0 ){
popupImage.src=imageinfo.url;
log( "a thumb could not be found, using original image" );
} else log( "fullsize imagethumb, but not sure if it's an image");
var a=document.getElementById("popupImageLink"+id);
if (a === null) { return null; }
// Determine the action of the surrouding imagelink.
switch (getValueOf('popupThumbAction')) {
case 'imagepage':
if (pg.current.article.namespaceId()!=pg.nsImageId) {
a.href=imageinfo.descriptionurl;
// FIXME: unreliable pg.idNumber
popTipsSoonFn('popupImage' + id)();
break;
}
/* falls through */
case 'sizetoggle':
a.onclick=toggleSize;
a.title=popupString('Toggle image size');
return;
case 'linkfull':
a.href = imageinfo.url;
a.title=popupString('Open full-size image');
return;
}
}
// Toggles the image between inline small and navpop fullwidth.
// It's the same image, no actual sizechange occurs, only display width.
function toggleSize() {
var imgContainer=this;
if (!imgContainer) {
alert('imgContainer is null :/');
return;
}
var img=imgContainer.firstChild;
if (!img) {
alert('img is null :/');
return;
}
if (!img.style.width || img.style.width==='') {
img.style.width='100%';
} else {
img.style.width='';
}
}
// Returns one title of an image from wikiText.
function getValidImageFromWikiText(wikiText) {
// nb in pg.re.image we're interested in the second bracketed expression
// this may change if the regex changes :-(
//var match=pg.re.image.exec(wikiText);
var matched=null;
var match;
// strip html comments, used by evil bots :-(
var t = removeMatchesUnless(wikiText, RegExp('(<!--[\\s\\S]*?-->)'), 1,
RegExp('^<!--[^[]*popup', 'i'));
while ( ( match = pg.re.image.exec(t) ) ) {
// now find a sane image name - exclude templates by seeking {
var m = match[2] || match[6];
if ( isValidImageName(m) ) {
matched=m;
break;
}
}
pg.re.image.lastIndex=0;
if (!matched) { return null; }
return mw.config.get('wgFormattedNamespaces')[pg.nsImageId]+':'+upcaseFirst(matched);
}
function removeMatchesUnless(str, re1, parencount, re2) {
var split=str.parenSplit(re1);
var c=parencount + 1;
for (var i=0; i<split.length; ++i) {
if ( i%c === 0 || re2.test(split[i]) ) { continue; }
split[i]='';
}
return split.join('');
}
//</NOLITE>
// ENDFILE: images.js
// STARTFILE: namespaces.js
// Set up namespaces and other non-strings.js localization
// (currently that means redirs too)
function setNamespaces() {
pg.nsSpecialId = -1;
pg.nsMainspaceId = 0;
pg.nsImageId = 6;
pg.nsUserId = 2;
pg.nsUsertalkId = 3;
pg.nsCategoryId = 14;
pg.nsTemplateId = 10;
}
function setRedirs() {
var r='redirect';
var R='REDIRECT';
var redirLists={
//<NOLITE>
'ar': [ R, 'تحويل' ],
'be': [ r, 'перанакіраваньне' ],
'bg': [ r, 'пренасочване', 'виж' ],
'bs': [ r, 'Preusmjeri', 'preusmjeri', 'PREUSMJERI' ],
'cs': [ R, 'PŘESMĚRUJ' ],
'cy': [ r, 'ail-cyfeirio' ],
'de': [ R, 'WEITERLEITUNG' ],
'el': [ R, 'ΑΝΑΚΑΤΕΥΘΥΝΣΗ'],
'eo': [ R, 'ALIDIREKTU', 'ALIDIREKTI' ],
'es': [ R, 'REDIRECCIÓN' ],
'et': [ r, 'suuna' ],
'ga': [ r, 'athsheoladh' ],
'gl': [ r, 'REDIRECCIÓN', 'REDIRECIONAMENTO'],
'he': [ R, 'הפניה' ],
'hu': [ R, 'ÁTIRÁNYÍTÁS' ],
'is': [ r, 'tilvísun', 'TILVÍSUN' ],
'it': [ R, 'RINVIA', 'Rinvia'],
'ja': [ R, '転送' ],
'mk': [ r, 'пренасочување', 'види' ],
'nds': [ r, 'wiederleiden' ],
'nl': [ R, 'DOORVERWIJZING' ],
'nn': [ r, 'omdiriger' ],
'pl': [ R, 'PATRZ', 'PRZEKIERUJ', 'TAM' ],
'pt': [ R, 'redir' ],
'ru': [ R, 'ПЕРЕНАПРАВЛЕНИЕ', 'ПЕРЕНАПР' ],
'sk': [ r, 'presmeruj' ],
'sr': [ r, 'Преусмери', 'преусмери', 'ПРЕУСМЕРИ', 'Preusmeri', 'preusmeri', 'PREUSMERI' ],
'tt': [ R, 'yünältü', 'перенаправление', 'перенапр' ],
'uk': [ R, 'ПЕРЕНАПРАВЛЕННЯ', 'ПЕРЕНАПР' ],
'vi': [ r, 'đổi' ],
'zh': [ R, '重定向'] // no comma
//</NOLITE>
};
var redirList=redirLists[ pg.wiki.lang ] || [r, R];
// Mediawiki is very tolerant about what comes after the #redirect at the start
pg.re.redirect=RegExp('^\\s*[#](' + redirList.join('|') + ').*?\\[{2}([^\\|\\]]*)(|[^\\]]*)?\\]{2}\\s*(.*)', 'i');
}
function setInterwiki() {
if (pg.wiki.wikimedia) {
// From https://meta.wikimedia.org/wiki/List_of_Wikipedias
pg.wiki.interwiki='aa|ab|ace|af|ak|als|am|an|ang|ar|arc|arz|as|ast|av|ay|az|ba|bar|bat-smg|bcl|be|be-x-old|bg|bh|bi|bjn|bm|bn|bo|bpy|br|bs|bug|bxr|ca|cbk-zam|cdo|ce|ceb|ch|cho|chr|chy|ckb|co|cr|crh|cs|csb|cu|cv|cy|da|de|diq|dsb|dv|dz|ee|el|eml|en|eo|es|et|eu|ext|fa|ff|fi|fiu-vro|fj|fo|fr|frp|frr|fur|fy|ga|gag|gan|gd|gl|glk|gn|got|gu|gv|ha|hak|haw|he|hi|hif|ho|hr|hsb|ht|hu|hy|hz|ia|id|ie|ig|ii|ik|ilo|io|is|it|iu|ja|jbo|jv|ka|kaa|kab|kbd|kg|ki|kj|kk|kl|km|kn|ko|koi|kr|krc|ks|ksh|ku|kv|kw|ky|la|lad|lb|lbe|lg|li|lij|lmo|ln|lo|lt|ltg|lv|map-bms|mdf|mg|mh|mhr|mi|mk|ml|mn|mo|mr|mrj|ms|mt|mus|mwl|my|myv|mzn|na|nah|nap|nds|nds-nl|ne|new|ng|nl|nn|no|nov|nrm|nv|ny|oc|om|or|os|pa|pag|pam|pap|pcd|pdc|pfl|pi|pih|pl|pms|pnb|pnt|ps|pt|qu|rm|rmy|rn|ro|roa-rup|roa-tara|ru|rue|rw|sa|sah|sc|scn|sco|sd|se|sg|sh|si|simple|sk|sl|sm|sn|so|sq|sr|srn|ss|st|stq|su|sv|sw|szl|ta|te|tet|tg|th|ti|tk|tl|tn|to|tpi|tr|ts|tt|tum|tw|ty|udm|ug|uk|ur|uz|ve|vec|vi|vls|vo|wa|war|wo|wuu|xal|xh|yi|yo|za|zea|zh|zh-classical|zh-min-nan|zh-yue|zu';
pg.re.interwiki=RegExp('^'+pg.wiki.interwiki+':');
} else {
pg.wiki.interwiki=null;
pg.re.interwiki=RegExp('^$');
}
}
// return a regexp pattern matching all variants to write the given namespace
function nsRe(namespaceId) {
var imageNamespaceVariants = [];
jQuery.each(mw.config.get('wgNamespaceIds'), function(_localizedNamespaceLc, _namespaceId) {
if (_namespaceId!=namespaceId) return;
_localizedNamespaceLc = upcaseFirst(_localizedNamespaceLc);
imageNamespaceVariants.push(mw.util.escapeRegExp(_localizedNamespaceLc).split(' ').join('[ _]'));
imageNamespaceVariants.push(mw.util.escapeRegExp(encodeURI(_localizedNamespaceLc)));
});
return '(?:' + imageNamespaceVariants.join('|') + ')';
}
function nsReImage() {
return nsRe(pg.nsImageId);
}
// ENDFILE: namespaces.js
// STARTFILE: selpop.js
//<NOLITE>
function getEditboxSelection() {
// see http://www.webgurusforum.com/8/12/0
var editbox;
try {
editbox=document.editform.wpTextbox1;
} catch (dang) { return; }
// IE, Opera
if (document.selection) { return document.selection.createRange().text; }
// Mozilla
var selStart = editbox.selectionStart;
var selEnd = editbox.selectionEnd;
return (editbox.value).substring(selStart, selEnd);
}
function doSelectionPopup() {
// popup if the selection looks like [[foo|anything afterwards at all
// or [[foo|bar]]text without ']]'
// or [[foo|bar]]
var sel=getEditboxSelection();
var open=sel.indexOf('[[');
var pipe=sel.indexOf('|');
var close=sel.indexOf(']]');
if (open == -1 || ( pipe == -1 && close == -1) ) { return; }
if (pipe != -1 && open > pipe || close != -1 && open > close) { return; }
if (getValueOf('popupOnEditSelection')=='boxpreview') {
return doSeparateSelectionPopup(sel);
}
var article=new Title(sel.substring(open+2, (pipe < 0) ? close : pipe)).urlString();
if (close > 0 && sel.substring(close+2).indexOf('[[') >= 0) {
return;
}
var a=document.createElement('a');
a.href=pg.wiki.titlebase + article;
mouseOverWikiLink2(a);
if (a.navpopup) {
a.navpopup.addHook(function(){runStopPopupTimer(a.navpopup);}, 'unhide', 'after');
}
}
function doSeparateSelectionPopup(str) {
var div=document.getElementById('selectionPreview');
if (!div) {
div = document.createElement('div');
div.id='selectionPreview';
try {
var box=document.editform.wpTextbox1;
box.parentNode.insertBefore(div, box);
} catch (error) {
return;
}
}
div.innerHTML=wiki2html(str);
div.ranSetupTooltipsAlready = false;
popTipsSoonFn('selectionPreview')();
}
//</NOLITE>
// ENDFILE: selpop.js
// STARTFILE: navpopup.js
/**
@fileoverview Defines two classes: {@link Navpopup} and {@link Mousetracker}.
<code>Navpopup</code> describes popups: when they appear, where, what
they look like and so on.
<code>Mousetracker</code> "captures" the mouse using
<code>document.onmousemove</code>.
*/
/**
Creates a new Mousetracker.
@constructor
@class The Mousetracker class. This monitors mouse movements and manages associated hooks.
*/
function Mousetracker() {
/**
Interval to regularly run the hooks anyway, in milliseconds.
@type Integer
*/
this.loopDelay=400;
/**
Timer for the loop.
@type Timer
*/
this.timer=null;
/**
Flag - are we switched on?
@type Boolean
*/
this.active=false;
/**
Flag - are we probably inaccurate, i.e. not reflecting the actual mouse position?
*/
this.dirty=true;
/**
Array of hook functions.
@private
@type Array
*/
this.hooks=[];
}
/**
Adds a hook, to be called when we get events.
@param {Function} f A function which is called as
<code>f(x,y)</code>. It should return <code>true</code> when it
wants to be removed, and <code>false</code> otherwise.
*/
Mousetracker.prototype.addHook = function (f) {
this.hooks.push(f);
};
/**
Runs hooks, passing them the x
and y coords of the mouse. Hook functions that return true are
passed to {@link Mousetracker#removeHooks} for removal.
@private
*/
Mousetracker.prototype.runHooks = function () {
if (!this.hooks || !this.hooks.length) { return; }
//log('Mousetracker.runHooks; we got some hooks to run');
var remove=false;
var removeObj={};
// this method gets called a LOT -
// pre-cache some variables
var x=this.x, y=this.y, len = this.hooks.length;
for (var i=0; i<len; ++i) {
//~ run the hook function, and remove it if it returns true
if (this.hooks[i](x, y)===true) {
remove=true;
removeObj[i]=true;
}
}
if (remove) { this.removeHooks(removeObj); }
};
/**
Removes hooks.
@private
@param {Object} removeObj An object whose keys are the index
numbers of functions for removal, with values that evaluate to true
*/
Mousetracker.prototype.removeHooks = function(removeObj) {
var newHooks=[];
var len = this.hooks.length;
for (var i=0; i<len; ++i) {
if (! removeObj[i]) { newHooks.push(this.hooks[i]); }
}
this.hooks=newHooks;
};
/**
Event handler for mouse wiggles.
We simply grab the event, set x and y and run the hooks.
This makes the cpu all hot and bothered :-(
@private
@param {Event} e Mousemove event
*/
Mousetracker.prototype.track=function (e) {
//~ Apparently this is needed in IE.
e = e || window.event;
var x, y;
if (e) {
if (e.pageX) { x=e.pageX; y=e.pageY; }
else if (typeof e.clientX!='undefined') {
var left, top, docElt = document.documentElement;
if (docElt) { left=docElt.scrollLeft; }
left = left || document.body.scrollLeft || document.scrollLeft || 0;
if (docElt) { top=docElt.scrollTop; }
top = top || document.body.scrollTop || document.scrollTop || 0;
x=e.clientX + left;
y=e.clientY + top;
} else { return; }
this.setPosition(x,y);
}
};
/**
Sets the x and y coordinates stored and takes appropriate action,
running hooks as appropriate.
@param {Integer} x, y Screen coordinates to set
*/
Mousetracker.prototype.setPosition=function(x,y) {
this.x = x;
this.y = y;
if (this.dirty || this.hooks.length === 0) { this.dirty=false; return; }
if (typeof this.lastHook_x != 'number') { this.lastHook_x = -100; this.lastHook_y=-100; }
var diff = (this.lastHook_x - x)*(this.lastHook_y - y);
diff = (diff >= 0) ? diff : -diff;
if ( diff > 1 ) {
this.lastHook_x=x;
this.lastHook_y=y;
if (this.dirty) { this.dirty = false; }
else { this.runHooks(); }
}
};
/**
Sets things in motion, unless they are already that is, registering an event handler on <code>document.onmousemove</code>.
A half-hearted attempt is made to preserve the old event handler if there is one.
*/
Mousetracker.prototype.enable = function () {
if (this.active) { return; }
this.active=true;
//~ Save the current handler for mousemove events. This isn't too
//~ robust, of course.
this.savedHandler=document.onmousemove;
//~ Gotta save @tt{this} again for the closure, and use apply for
//~ the member function.
var savedThis=this;
document.onmousemove=function (e) {savedThis.track.apply(savedThis, [e]);};
if (this.loopDelay) { this.timer = setInterval(function() { //log('loop delay in mousetracker is working');
savedThis.runHooks();}, this.loopDelay); }
};
/**
Disables the tracker, removing the event handler.
*/
Mousetracker.prototype.disable = function () {
if (!this.active) { return; }
if (typeof this.savedHandler === 'function') {
document.onmousemove=this.savedHandler;
} else { delete document.onmousemove; }
if (this.timer) { clearInterval(this.timer); }
this.active=false;
};
/**
Creates a new Navpopup.
Gets a UID for the popup and
@param init Contructor object. If <code>init.draggable</code> is true or absent, the popup becomes draggable.
@constructor
@class The Navpopup class. This generates popup hints, and does some management of them.
*/
function Navpopup(/*init*/) {
//alert('new Navpopup(init)');
/** UID for each Navpopup instance.
Read-only.
@type integer
*/
this.uid=Navpopup.uid++;
/**
Read-only flag for current visibility of the popup.
@type boolean
@private
*/
this.visible=false;
/** Flag to be set when we want to cancel a previous request to
show the popup in a little while.
@private
@type boolean
*/
this.noshow=false;
/** Categorised list of hooks.
@see #runHooks
@see #addHook
@private
@type Object
*/
this.hooks={
'create': [],
'unhide': [],
'hide': []
};
/** list of unique IDs of hook functions, to avoid duplicates
@private
*/
this.hookIds={};
/** List of downloads associated with the popup.
@private
@type Array
*/
this.downloads=[];
/** Number of uncompleted downloads.
@type integer
*/
this.pending=null;
/** Tolerance in pixels when detecting whether the mouse has left the popup.
@type integer
*/
this.fuzz=5;
/** Flag to toggle running {@link #limitHorizontalPosition} to regulate the popup's position.
@type boolean
*/
this.constrained=true;
/** The popup width in pixels.
@private
@type integer
*/
this.width=0;
/** The popup width in pixels.
@private
@type integer
*/
this.height=0;
/** The main content DIV element.
@type HTMLDivElement
*/
this.mainDiv=null;
this.createMainDiv();
// if (!init || typeof init.popups_draggable=='undefined' || init.popups_draggable) {
// this.makeDraggable(true);
// }
}
/**
A UID for each Navpopup. This constructor property is just a counter.
@type integer
@private
*/
Navpopup.uid=0;
/**
Retrieves the {@link #visible} attribute, indicating whether the popup is currently visible.
@type boolean
*/
Navpopup.prototype.isVisible=function() {
return this.visible;
};
/**
Repositions popup using CSS style.
@private
@param {integer} x x-coordinate (px)
@param {integer} y y-coordinate (px)
@param {boolean} noLimitHor Don't call {@link #limitHorizontalPosition}
*/
Navpopup.prototype.reposition= function (x,y, noLimitHor) {
log ('reposition('+x+','+y+','+noLimitHor+')');
if (typeof x != 'undefined' && x !== null) { this.left=x; }
if (typeof y != 'undefined' && y !== null) { this.top=y; }
if (typeof this.left != 'undefined' && typeof this.top != 'undefined') {
this.mainDiv.style.left=this.left + 'px';
this.mainDiv.style.top=this.top + 'px';
}
if (!noLimitHor) { this.limitHorizontalPosition(); }
//console.log('navpop'+this.uid+' - (left,top)=(' + this.left + ',' + this.top + '), css=('
//+ this.mainDiv.style.left + ',' + this.mainDiv.style.top + ')');
};
/**
Prevents popups from being in silly locations. Hopefully.
Should not be run if {@link #constrained} is true.
@private
*/
Navpopup.prototype.limitHorizontalPosition=function() {
if (!this.constrained || this.tooWide) { return; }
this.updateDimensions();
var x=this.left;
var w=this.width;
var cWidth=document.body.clientWidth;
// log('limitHorizontalPosition: x='+x+
// ', this.left=' + this.left +
// ', this.width=' + this.width +
// ', cWidth=' + cWidth);
if ( (x+w) >= cWidth ||
( x > 0 &&
this.maxWidth &&
this.width < this.maxWidth &&
this.height > this.width &&
x > cWidth - this.maxWidth ) ) {
// This is a very nasty hack. There has to be a better way!
// We find the "natural" width of the div by positioning it at the far left
// then reset it so that it should be flush right (well, nearly)
this.mainDiv.style.left='-10000px';
this.mainDiv.style.width = this.maxWidth + 'px';
var naturalWidth=parseInt(this.mainDiv.offsetWidth, 10);
var newLeft=cWidth - naturalWidth - 1;
if (newLeft < 0) { newLeft = 0; this.tooWide=true; } // still unstable for really wide popups?
log ('limitHorizontalPosition: moving to ('+newLeft + ','+ this.top+');' + ' naturalWidth=' + naturalWidth + ', clientWidth=' + cWidth);
this.reposition(newLeft, null, true);
}
};
/**
Counter indicating the z-order of the "highest" popup.
We start the z-index at 1000 so that popups are above everything
else on the screen.
@private
@type integer
*/
Navpopup.highest=1000;
/**
Brings popup to the top of the z-order.
We increment the {@link #highest} property of the contructor here.
@private
*/
Navpopup.prototype.raise = function () {
this.mainDiv.style.zIndex=Navpopup.highest + 1;
++Navpopup.highest;
};
/**
Shows the popup provided {@link #noshow} is not true.
Updates the position, brings the popup to the top of the z-order and unhides it.
*/
Navpopup.prototype.show = function () {
//document.title+='s';
if (this.noshow) { return; }
//document.title+='t';
this.reposition();
this.raise();
this.unhide();
};
/**
Checks to see if the mouse pointer has
stabilised (checking every <code>time</code>/2 milliseconds) and runs the
{@link #show} method if it has.
@param {integer} time The minimum time (ms) before the popup may be shown.
*/
Navpopup.prototype.showSoonIfStable = function (time) {
log ('showSoonIfStable, time='+time);
if (this.visible) { return; }
this.noshow = false;
//~ initialize these variables so that we never run @tt{show} after
//~ just half the time
this.stable_x = -10000; this.stable_y = -10000;
var stableShow = function() {
log('stableShow called');
var new_x = Navpopup.tracker.x, new_y = Navpopup.tracker.y;
var dx = savedThis.stable_x - new_x, dy = savedThis.stable_y - new_y;
var fuzz2 = 0; // savedThis.fuzz * savedThis.fuzz;
//document.title += '[' + [savedThis.stable_x,new_x, savedThis.stable_y,new_y, dx, dy, fuzz2].join(',') + '] ';
if ( dx * dx <= fuzz2 && dy * dy <= fuzz2 ) {
log ('mouse is stable');
clearInterval(savedThis.showSoonStableTimer);
savedThis.reposition.apply(savedThis, [new_x + 2, new_y + 2]);
savedThis.show.apply(savedThis, []);
savedThis.limitHorizontalPosition.apply(savedThis, []);
return;
}
savedThis.stable_x = new_x; savedThis.stable_y = new_y;
};
var savedThis = this;
this.showSoonStableTimer = setInterval(stableShow, time/2);
};
/**
Sets the {@link #noshow} flag and hides the popup. This should be called
when the mouse leaves the link before
(or after) it's actually been displayed.
*/
Navpopup.prototype.banish = function () {
log ('banish called');
// hide and prevent showing with showSoon in the future
this.noshow=true;
if (this.showSoonStableTimer) {
log('clearing showSoonStableTimer');
clearInterval(this.showSoonStableTimer);
}
this.hide();
};
/**
Runs hooks added with {@link #addHook}.
@private
@param {String} key Key name of the {@link #hooks} array - one of 'create', 'unhide', 'hide'
@param {String} when Controls exactly when the hook is run: either 'before' or 'after'
*/
Navpopup.prototype.runHooks = function (key, when) {
if (!this.hooks[key]) { return; }
var keyHooks=this.hooks[key];
var len=keyHooks.length;
for (var i=0; i< len; ++i) {
if (keyHooks[i] && keyHooks[i].when == when) {
if (keyHooks[i].hook.apply(this, [])) {
// remove the hook
if (keyHooks[i].hookId) {
delete this.hookIds[keyHooks[i].hookId];
}
keyHooks[i]=null;
}
}
}
};
/**
Adds a hook to the popup. Hook functions are run with <code>this</code> set to refer to the Navpopup instance, and no arguments.
@param {Function} hook The hook function. Functions that return true are deleted.
@param {String} key Key name of the {@link #hooks} array - one of 'create', 'unhide', 'hide'
@param {String} when Controls exactly when the hook is run: either 'before' or 'after'
@param {String} uid A truthy string identifying the hook function; if it matches another hook in this position, it won't be added again.
*/
Navpopup.prototype.addHook = function ( hook, key, when, uid ) {
when = when || 'after';
if (!this.hooks[key]) { return; }
// if uid is specified, don't add duplicates
var hookId=null;
if (uid) {
hookId=[key,when,uid].join('|');
if (this.hookIds[hookId]) {
return;
}
this.hookIds[hookId]=true;
}
this.hooks[key].push( {hook: hook, when: when, hookId: hookId} );
};
/**
Creates the main DIV element, which contains all the actual popup content.
Runs hooks with key 'create'.
@private
*/
Navpopup.prototype.createMainDiv = function () {
if (this.mainDiv) { return; }
this.runHooks('create', 'before');
var mainDiv=document.createElement('div');
var savedThis=this;
mainDiv.onclick=function(e) {savedThis.onclickHandler(e);};
mainDiv.className=(this.className) ? this.className : 'navpopup_maindiv';
mainDiv.id=mainDiv.className + this.uid;
mainDiv.style.position='absolute';
mainDiv.style.minWidth = '350px';
mainDiv.style.display='none';
mainDiv.className='navpopup';
// easy access to javascript object through DOM functions
mainDiv.navpopup=this;
this.mainDiv=mainDiv;
document.body.appendChild(mainDiv);
this.runHooks('create', 'after');
};
/**
Calls the {@link #raise} method.
@private
*/
Navpopup.prototype.onclickHandler=function(/*e*/) {
this.raise();
};
/**
Makes the popup draggable, using a {@link Drag} object.
@private
*/
Navpopup.prototype.makeDraggable=function(handleName) {
if (!this.mainDiv) { this.createMainDiv(); }
var drag=new Drag();
if (!handleName) {
drag.startCondition=function(e) {
try { if (!e.shiftKey) { return false; } } catch (err) { return false; }
return true;
};
}
var dragHandle;
if (handleName) dragHandle = document.getElementById(handleName);
if (!dragHandle) dragHandle = this.mainDiv;
var np=this;
drag.endHook=function(x,y) {
Navpopup.tracker.dirty=true;
np.reposition(x,y);
};
drag.init(dragHandle,this.mainDiv);
};
/** Hides the popup using CSS. Runs hooks with key 'hide'.
Sets {@link #visible} appropriately. {@link #banish} should be called externally instead of this method.
@private
*/
Navpopup.prototype.hide = function () {
this.runHooks('hide', 'before');
this.abortDownloads();
if (typeof this.visible != 'undefined' && this.visible) {
this.mainDiv.style.display='none';
this.visible=false;
}
this.runHooks('hide', 'after');
};
/** Shows the popup using CSS. Runs hooks with key 'unhide'.
Sets {@link #visible} appropriately. {@link #show} should be called externally instead of this method.
@private
*/
Navpopup.prototype.unhide = function () {
this.runHooks('unhide', 'before');
if (typeof this.visible != 'undefined' && !this.visible) {
this.mainDiv.style.display='inline';
this.visible=true;
}
this.runHooks('unhide', 'after');
};
/**
Sets the <code>innerHTML</code> attribute of the main div containing the popup content.
@param {String} html The HTML to set.
*/
Navpopup.prototype.setInnerHTML = function (html) {
this.mainDiv.innerHTML = html;
};
/**
Updates the {@link #width} and {@link #height} attributes with the CSS properties.
@private
*/
Navpopup.prototype.updateDimensions = function () {
this.width=parseInt(this.mainDiv.offsetWidth, 10);
this.height=parseInt(this.mainDiv.offsetHeight, 10);
};
/**
Checks if the point (x,y) is within {@link #fuzz} of the
{@link #mainDiv}.
@param {integer} x x-coordinate (px)
@param {integer} y y-coordinate (px)
@type boolean
*/
Navpopup.prototype.isWithin = function(x,y) {
//~ If we're not even visible, no point should be considered as
//~ being within the popup.
if (!this.visible) { return false; }
this.updateDimensions();
var fuzz=this.fuzz || 0;
//~ Use a simple box metric here.
return (x+fuzz >= this.left && x-fuzz <= this.left + this.width &&
y+fuzz >= this.top && y-fuzz <= this.top + this.height);
};
/**
Adds a download to {@link #downloads}.
@param {Downloader} download
*/
Navpopup.prototype.addDownload=function(download) {
if (!download) { return; }
this.downloads.push(download);
};
/**
Aborts the downloads listed in {@link #downloads}.
@see Downloader#abort
*/
Navpopup.prototype.abortDownloads=function() {
for(var i=0; i<this.downloads.length; ++i) {
var d=this.downloads[i];
if (d && d.abort) { d.abort(); }
}
this.downloads=[];
};
/**
A {@link Mousetracker} instance which is a property of the constructor (pseudo-global).
*/
Navpopup.tracker=new Mousetracker();
// ENDFILE: navpopup.js
// STARTFILE: diff.js
//<NOLITE>
/*
* Javascript Diff Algorithm
* By John Resig (http://ejohn.org/) and [[:en:User:Lupin]]
*
* More Info:
* http://ejohn.org/projects/javascript-diff-algorithm/
*/
function delFmt(x) {
if (!x.length) { return ''; }
return "<del class='popupDiff'>" + x.join('') +"</del>";
}
function insFmt(x) {
if (!x.length) { return ''; }
return "<ins class='popupDiff'>" + x.join('') +"</ins>";
}
function countCrossings(a, b, i, eject) {
// count the crossings on the edge starting at b[i]
if (!b[i].row && b[i].row !== 0) { return -1; }
var count=0;
for (var j=0; j<a.length; ++j) {
if (!a[j].row && a[j].row !== 0) { continue; }
if ( (j-b[i].row)*(i-a[j].row) > 0) {
if(eject) { return true; }
count++;
}
}
return count;
}
function shortenDiffString(str, context) {
var re=RegExp('(<del[\\s\\S]*?</del>|<ins[\\s\\S]*?</ins>)');
var splitted=str.parenSplit(re);
var ret=[''];
for (var i=0; i<splitted.length; i+=2) {
if (splitted[i].length < 2*context) {
ret[ret.length-1] += splitted[i];
if (i+1<splitted.length) { ret[ret.length-1] += splitted[i+1]; }
continue;
}
else {
if (i > 0) { ret[ret.length-1] += splitted[i].substring(0,context); }
if (i+1 < splitted.length) {
ret.push(splitted[i].substring(splitted[i].length-context) +
splitted[i+1]);
}
}
}
while (ret.length > 0 && !ret[0]) { ret = ret.slice(1); }
return ret;
}
function diffString( o, n, simpleSplit ) {
var splitRe=RegExp('([[]{2}|[\\]]{2}|[{]{2,3}|[}]{2,3}|[|]|=|<|>|[*:]+|\\s|\\b)');
// We need to split the strings o and n first, and entify() the parts
// individually, so that the HTML entities are never cut apart. (AxelBoldt)
var out, i, oSplitted, nSplitted;
if (simpleSplit) {
oSplitted=o.split(/\b/);
nSplitted=n.split(/\b/);
} else {
oSplitted=o.parenSplit(splitRe);
nSplitted=n.parenSplit(splitRe);
}
for (i=0; i<oSplitted.length; ++i) {oSplitted[i]=oSplitted[i].entify();}
for (i=0; i<nSplitted.length; ++i) {nSplitted[i]=nSplitted[i].entify();}
out = diff (oSplitted, nSplitted);
var str = "";
var acc=[]; // accumulator for prettier output
// crossing pairings -- eg 'A B' vs 'B A' -- cause problems, so let's iron them out
// this doesn't always do things optimally but it should be fast enough
var maxOutputPair=0;
for (i=0; i<out.n.length; ++i) {
if ( out.n[i].paired ) {
if( maxOutputPair > out.n[i].row ) {
// tangle - delete pairing
out.o[ out.n[i].row ]=out.o[ out.n[i].row ].text;
out.n[i]=out.n[i].text;
}
if (maxOutputPair < out.n[i].row) { maxOutputPair = out.n[i].row; }
}
}
// output the stuff preceding the first paired old line
for (i=0; i<out.o.length && !out.o[i].paired; ++i) { acc.push( out.o[i] ); }
str += delFmt(acc); acc=[];
// main loop
for ( i = 0; i < out.n.length; ++i ) {
// output unpaired new "lines"
while ( i < out.n.length && !out.n[i].paired ) { acc.push( out.n[i++] ); }
str += insFmt(acc); acc=[];
if ( i < out.n.length ) { // this new "line" is paired with the (out.n[i].row)th old "line"
str += out.n[i].text;
// output unpaired old rows starting after this new line's partner
var m = out.n[i].row + 1;
while ( m < out.o.length && !out.o[m].paired ) { acc.push ( out.o[m++] ); }
str += delFmt(acc); acc=[];
}
}
return str;
}
// see http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Object
// FIXME: use obj.hasOwnProperty instead of this kludge!
var jsReservedProperties=RegExp('^(constructor|prototype|__((define|lookup)[GS]etter)__' +
'|eval|hasOwnProperty|propertyIsEnumerable' +
'|to(Source|String|LocaleString)|(un)?watch|valueOf)$');
function diffBugAlert(word) {
if (!diffBugAlert.list[word]) {
diffBugAlert.list[word]=1;
alert('Bad word: '+word+'\n\nPlease report this bug.');
}
}
diffBugAlert.list={};
function makeDiffHashtable(src) {
var ret={};
for ( var i = 0; i < src.length; i++ ) {
if ( jsReservedProperties.test(src[i]) ) { src[i] += '<!-- -->'; }
if ( !ret[ src[i] ] ) { ret[ src[i] ] = []; }
try { ret[ src[i] ].push( i ); } catch (err) { diffBugAlert(src[i]); }
}
return ret;
}
function diff( o, n ) {
// pass 1: make hashtable ns with new rows as keys
var ns = makeDiffHashtable(n);
// pass 2: make hashtable os with old rows as keys
var os = makeDiffHashtable(o);
// pass 3: pair unique new rows and matching unique old rows
var i;
for ( i in ns ) {
if ( ns[i].length == 1 && os[i] && os[i].length == 1 ) {
n[ ns[i][0] ] = { text: n[ ns[i][0] ], row: os[i][0], paired: true };
o[ os[i][0] ] = { text: o[ os[i][0] ], row: ns[i][0], paired: true };
}
}
// pass 4: pair matching rows immediately following paired rows (not necessarily unique)
for ( i = 0; i < n.length - 1; i++ ) {
if ( n[i].paired && ! n[i+1].paired && n[i].row + 1 < o.length && ! o[ n[i].row + 1 ].paired &&
n[i+1] == o[ n[i].row + 1 ] ) {
n[i+1] = { text: n[i+1], row: n[i].row + 1, paired: true };
o[n[i].row+1] = { text: o[n[i].row+1], row: i + 1, paired: true };
}
}
// pass 5: pair matching rows immediately preceding paired rows (not necessarily unique)
for ( i = n.length - 1; i > 0; i-- ) {
if ( n[i].paired && ! n[i-1].paired && n[i].row > 0 && ! o[ n[i].row - 1 ].paired &&
n[i-1] == o[ n[i].row - 1 ] ) {
n[i-1] = { text: n[i-1], row: n[i].row - 1, paired: true };
o[n[i].row-1] = { text: o[n[i].row-1], row: i - 1, paired: true };
}
}
return { o: o, n: n };
}
//</NOLITE>
// ENDFILE: diff.js
// STARTFILE: init.js
function setSiteInfo() {
if (window.popupLocalDebug) {
pg.wiki.hostname = 'en.wikipedia.org';
} else {
pg.wiki.hostname = location.hostname; // use in preference to location.hostname for flexibility (?)
}
pg.wiki.wikimedia=RegExp('(wiki([pm]edia|source|books|news|quote|versity)|wiktionary|mediawiki)[.]org').test(pg.wiki.hostname);
pg.wiki.wikia=RegExp('[.]wikia[.]com$', 'i').test(pg.wiki.hostname);
pg.wiki.isLocal=RegExp('^localhost').test(pg.wiki.hostname);
pg.wiki.commons=( pg.wiki.wikimedia && pg.wiki.hostname != 'commons.wikimedia.org') ? 'commons.wikimedia.org' : null;
pg.wiki.lang = mw.config.get('wgContentLanguage');
var port = location.port ? ':' + location.port : '';
pg.wiki.sitebase = pg.wiki.hostname + port;
}
function setUserInfo() {
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'query',
list: 'users',
ususers: mw.config.get('wgUserName'),
usprop: 'rights'
};
pg.user.canReview = false;
if (getValueOf('popupReview')) {
api.get(params).done(function(data){
var rights = data.query.users[0].rights;
pg.user.canReview = rights.indexOf('review') !== -1; // TODO: Should it be a getValueOf('ReviewRight') ?
});
}
}
function setTitleBase() {
var protocol = ( window.popupLocalDebug ? 'http:' : location.protocol );
pg.wiki.articlePath = mw.config.get('wgArticlePath').replace(/\/\$1/, ""); // as in http://some.thing.com/wiki/Article
pg.wiki.botInterfacePath = mw.config.get('wgScript');
pg.wiki.APIPath = mw.config.get('wgScriptPath') +"/api.php";
// default mediawiki setting is paths like http://some.thing.com/articlePath/index.php?title=foo
var titletail = pg.wiki.botInterfacePath + '?title=';
//var titletail2 = joinPath([pg.wiki.botInterfacePath, 'wiki.phtml?title=']);
// other sites may need to add code here to set titletail depending on how their urls work
pg.wiki.titlebase = protocol + '//' + pg.wiki.sitebase + titletail;
//pg.wiki.titlebase2 = protocol + '//' + joinPath([pg.wiki.sitebase, titletail2]);
pg.wiki.wikibase = protocol + '//' + pg.wiki.sitebase + pg.wiki.botInterfacePath;
pg.wiki.apiwikibase = protocol + '//' + pg.wiki.sitebase + pg.wiki.APIPath;
pg.wiki.articlebase = protocol + '//' + pg.wiki.sitebase + pg.wiki.articlePath;
pg.wiki.commonsbase = protocol + '//' + pg.wiki.commons + pg.wiki.botInterfacePath;
pg.wiki.apicommonsbase = protocol + '//' + pg.wiki.commons + pg.wiki.APIPath;
pg.re.basenames = RegExp( '^(' +
map( literalizeRegex, [ pg.wiki.titlebase, //pg.wiki.titlebase2,
pg.wiki.articlebase ]).join('|') + ')' );
}
//////////////////////////////////////////////////
// Global regexps
function setMainRegex() {
var reStart='[^:]*://';
var preTitles = literalizeRegex( mw.config.get('wgScriptPath') ) + '/(?:index[.]php|wiki[.]phtml)[?]title=';
preTitles += '|' + literalizeRegex( pg.wiki.articlePath + '/' );
var reEnd='(' + preTitles + ')([^&?#]*)[^#]*(?:#(.+))?';
pg.re.main = RegExp(reStart + literalizeRegex(pg.wiki.sitebase) + reEnd);
}
function setRegexps() {
// TODO: We shoud use an api call to get the aliases for special pages, now it does not work for non-English wikipedias:
// E.g., https://ru.wikipedia.org/w/api.php?action=query&meta=siteinfo&siprop=specialpagealiases&formatversion=2
setMainRegex();
var sp=nsRe(pg.nsSpecialId);
pg.re.urlNoPopup=RegExp('((title=|/)' + sp + '(?:%3A|:)|section=[0-9]|^#$)') ;
pg.re.contribs =RegExp('(title=|/)' + sp + '(?:%3A|:)Contributions' + '(&target=|/|/' + nsRe(pg.nsUserId)+':)(.*)') ;
pg.re.email =RegExp('(title=|/)' + sp + '(?:%3A|:)EmailUser' + '(&target=|/|/(?:' + nsRe(pg.nsUserId)+':)?)(.*)') ;
pg.re.backlinks =RegExp('(title=|/)' + sp + '(?:%3A|:)WhatLinksHere' + '(&target=|/)([^&]*)');
pg.re.specialdiff=RegExp('/' + sp + '(?:%3A|:)Diff/([^?#]*)');
//<NOLITE>
var im=nsReImage();
// note: tries to get images in infobox templates too, e.g. movie pages, album pages etc
// (^|\[\[)image: *([^|\]]*[^|\] ]) *
// (^|\[\[)image: *([^|\]]*[^|\] ])([^0-9\]]*([0-9]+) *px)?
// $4 = 120 as in 120px
pg.re.image = RegExp('(^|\\[\\[)' + im + ': *([^|\\]]*[^|\\] ])' +
'([^0-9\\]]*([0-9]+) *px)?|(?:\\n *[|]?|[|]) *' +
'(' + getValueOf('popupImageVarsRegexp') + ')' +
' *= *(?:\\[\\[ *)?(?:' + im + ':)?' +
'([^|]*?)(?:\\]\\])? *[|]? *\\n', 'img') ;
pg.re.imageBracketCount = 6;
pg.re.category = RegExp('\\[\\[' +nsRe(pg.nsCategoryId) +
': *([^|\\]]*[^|\\] ]) *', 'i');
pg.re.categoryBracketCount = 1;
pg.re.ipUser=RegExp('^' +
// IPv6
'(?::(?::|(?::[0-9A-Fa-f]{1,4}){1,7})|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0,6}::|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){7})' +
// IPv4
'|(((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}' +
'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9]))$');
pg.re.stub= RegExp(getValueOf('popupStubRegexp'), 'im');
pg.re.disambig=RegExp(getValueOf('popupDabRegexp'), 'im');
//</NOLITE>
// FIXME replace with general parameter parsing function, this is daft
pg.re.oldid=RegExp('[?&]oldid=([^&]*)');
pg.re.diff=RegExp('[?&]diff=([^&]*)');
}
//////////////////////////////////////////////////
// miscellany
function setupCache() {
// page caching
pg.cache.pages = [];
}
function setMisc() {
pg.current.link=null;
pg.current.links=[];
pg.current.linksHash={};
setupCache();
pg.timer.checkPopupPosition=null;
pg.counter.loop=0;
// ids change with each popup: popupImage0, popupImage1 etc
pg.idNumber=0;
// for myDecodeURI
pg.misc.decodeExtras = [
{from: '%2C', to: ',' },
{from: '_', to: ' ' },
{from: '%24', to: '$'},
{from: '%26', to: '&' } // no ,
];
pg.misc.userAgent = 'Navigation popups/1.0 (' + mw.config.get( 'wgServerName' ) +')';
}
// We need a callback since this might end up asynchronous because of
// the mw.loader.using() call.
function setupPopups( callback ) {
if ( setupPopups.completed ) {
if ( typeof callback === 'function' ) {
callback();
}
return;
}
// These dependencies are also enforced from the gadget,
// but not everyone loads this as a gadget, so double check
mw.loader.using( ['mediawiki.util', 'mediawiki.user', 'user.options'] ).then( function() {
// NB translatable strings should be set up first (strings.js)
// basics
setupDebugging();
setSiteInfo();
setTitleBase();
setOptions(); // see options.js
setUserInfo();
// namespaces etc
setNamespaces();
setInterwiki();
// regexps
setRegexps();
setRedirs();
// other stuff
setMisc();
setupLivePreview();
// main deal here
setupTooltips();
log('In setupPopups(), just called setupTooltips()');
Navpopup.tracker.enable();
setupPopups.completed = true;
if ( typeof callback === 'function' ) {
callback();
}
});
}
// ENDFILE: init.js
// STARTFILE: navlinks.js
//<NOLITE>
//////////////////////////////////////////////////
// navlinks... let the fun begin
//
function defaultNavlinkSpec() {
var str='';
str += '<b><<mainlink|shortcut= >></b>';
if (getValueOf('popupLastEditLink')) {
str += '*<<lastEdit|shortcut=/>>|<<lastContrib>>|<<sinceMe>>if(oldid){|<<oldEdit>>|<<diffCur>>}';
}
// user links
// contribs - log - count - email - block
// count only if applicable; block only if popupAdminLinks
str += 'if(user){<br><<contribs|shortcut=c>>*<<userlog|shortcut=L|log>>';
str+='if(ipuser){*<<arin>>}if(wikimedia){*<<count|shortcut=#>>}';
str+='if(ipuser){}else{*<<email|shortcut=E>>}if(admin){*<<block|shortcut=b>>|<<blocklog|log>>}}';
// editing links
// talkpage -> edit|new - history - un|watch - article|edit
// other page -> edit - history - un|watch - talk|edit|new
var editstr='<<edit|shortcut=e>>';
var editOldidStr='if(oldid){<<editOld|shortcut=e>>|<<revert|shortcut=v|rv>>|<<edit|cur>>}else{' + editstr + '}';
var historystr='<<history|shortcut=h>>|<<editors|shortcut=E|>>';
var watchstr='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
str+='<br>if(talk){' +
editOldidStr+'|<<new|shortcut=+>>' + '*' + historystr+'*'+watchstr + '*' +
'<b><<article|shortcut=a>></b>|<<editArticle|edit>>' +
'}else{' + // not a talk page
editOldidStr + '*' + historystr + '*' + watchstr + '*' +
'<b><<talk|shortcut=t>></b>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>}';
// misc links
str += '<br><<whatLinksHere|shortcut=l>>*<<relatedChanges|shortcut=r>>*<<move|shortcut=m>>';
// admin links
str += 'if(admin){<br><<unprotect|unprotectShort>>|<<protect|shortcut=p>>|<<protectlog|log>>*' +
'<<undelete|undeleteShort>>|<<delete|shortcut=d>>|<<deletelog|log>>}';
return str;
}
function navLinksHTML (article, hint, params) { //oldid, rcid) {
var str = '<span class="popupNavLinks">' + defaultNavlinkSpec() + '</span>';
// BAM
return navlinkStringToHTML(str, article, params);
}
function expandConditionalNavlinkString(s,article,z,recursionCount) {
var oldid=z.oldid, rcid=z.rcid, diff=z.diff;
// nested conditionals (up to 10 deep) are ok, hopefully! (work from the inside out)
if (typeof recursionCount!=typeof 0) { recursionCount=0; }
var conditionalSplitRegex=RegExp(
//(1 if \\( (2 2) \\) {(3 3)} (4 else {(5 5)} 4)1)
'(;?\\s*if\\s*\\(\\s*([\\w]*)\\s*\\)\\s*\\{([^{}]*)\\}(\\s*else\\s*\\{([^{}]*?)\\}|))', 'i');
var splitted=s.parenSplit(conditionalSplitRegex);
// $1: whole conditional
// $2: test condition
// $3: true expansion
// $4: else clause (possibly empty)
// $5: false expansion (possibly null)
var numParens=5;
var ret = splitted[0];
for (var i=1; i<splitted.length; i=i+numParens+1) {
var testString=splitted[i+2-1];
var trueString=splitted[i+3-1];
var falseString=splitted[i+5-1];
if (typeof falseString=='undefined' || !falseString) { falseString=''; }
var testResult=null;
switch (testString) {
case 'user':
testResult=(article.userName())?true:false;
break;
case 'talk':
testResult=(article.talkPage())?false:true; // talkPage converts _articles_ to talkPages
break;
case 'admin':
testResult=getValueOf('popupAdminLinks')?true:false;
break;
case 'oldid':
testResult=(typeof oldid != 'undefined' && oldid)?true:false;
break;
case 'rcid':
testResult=(typeof rcid != 'undefined' && rcid)?true:false;
break;
case 'ipuser':
testResult=(article.isIpUser())?true:false;
break;
case 'mainspace_en':
testResult=isInMainNamespace(article) &&
pg.wiki.hostname=='en.wikipedia.org';
break;
case 'wikimedia':
testResult=(pg.wiki.wikimedia) ? true : false;
break;
case 'diff':
testResult=(typeof diff != 'undefined' && diff)?true:false;
break;
}
switch(testResult) {
case null: ret+=splitted[i]; break;
case true: ret+=trueString; break;
case false: ret+=falseString; break;
}
// append non-conditional string
ret += splitted[i+numParens];
}
if (conditionalSplitRegex.test(ret) && recursionCount < 10) {
return expandConditionalNavlinkString(ret,article,z,recursionCount+1);
}
return ret;
}
function navlinkStringToArray(s, article, params) {
s=expandConditionalNavlinkString(s,article,params);
var splitted=s.parenSplit(RegExp('<<(.*?)>>'));
var ret=[];
for (var i=0; i<splitted.length; ++i) {
if (i%2) { // i odd, so s is a tag
var t=new navlinkTag();
var ss=splitted[i].split('|');
t.id=ss[0];
for (var j=1; j<ss.length; ++j) {
var sss=ss[j].split('=');
if (sss.length>1) {
t[sss[0]]=sss[1];
}
else { // no assignment (no "="), so treat this as a title (overwriting the last one)
t.text=popupString(sss[0]);
}
}
t.article=article;
var oldid=params.oldid, rcid=params.rcid, diff=params.diff;
if (typeof oldid !== 'undefined' && oldid !== null) { t.oldid=oldid; }
if (typeof rcid !== 'undefined' && rcid !== null) { t.rcid=rcid; }
if (typeof diff !== 'undefined' && diff !== null) { t.diff=diff; }
if (!t.text && t.id !== 'mainlink') { t.text=popupString(t.id); }
ret.push(t);
}
else { // plain HTML
ret.push(splitted[i]);
}
}
return ret;
}
function navlinkSubstituteHTML(s) {
return s.split('*').join(getValueOf('popupNavLinkSeparator'))
.split('<menurow>').join('<li class="popup_menu_row">')
.split('</menurow>').join('</li>')
.split('<menu>').join('<ul class="popup_menu">')
.split('</menu>').join('</ul>');
}
function navlinkDepth(magic,s) {
return s.split('<' + magic + '>').length - s.split('</' + magic + '>').length;
}
// navlinkString: * becomes the separator
// <<foo|bar=baz|fubar>> becomes a foo-link with attribute bar='baz'
// and visible text 'fubar'
// if(test){...} and if(test){...}else{...} work too (nested ok)
function navlinkStringToHTML(s,article,params) {
//limitAlert(navlinkStringToHTML, 5, 'navlinkStringToHTML\n' + article + '\n' + (typeof article));
var p=navlinkStringToArray(s,article,params);
var html='';
var menudepth = 0; // nested menus not currently allowed, but doesn't do any harm to code for it
var menurowdepth = 0;
for (var i=0; i<p.length; ++i) {
if (typeof p[i] == typeof '') {
html+=navlinkSubstituteHTML(p[i]);
menudepth += navlinkDepth('menu', p[i]);
menurowdepth += navlinkDepth('menurow', p[i]);
// if (menudepth === 0) {
// tagType='span';
// } else if (menurowdepth === 0) {
// tagType='li';
// } else {
// tagType = null;
// }
} else if (typeof p[i].type != 'undefined' && p[i].type=='navlinkTag') {
if (menudepth > 0 && menurowdepth === 0) {
html += '<li class="popup_menu_item">' + p[i].html() + '</li>';
} else {
html+=p[i].html();
}
}
}
return html;
}
function navlinkTag() {
this.type='navlinkTag';
}
navlinkTag.prototype.html=function () {
this.getNewWin();
this.getPrintFunction();
var html='';
var opening, closing;
var tagType='span';
if (!tagType) {
opening = ''; closing = '';
} else {
opening = '<' + tagType + ' class="popup_' + this.id + '">';
closing = '</' + tagType + '>';
}
if (typeof this.print!='function') {
errlog ('Oh dear - invalid print function for a navlinkTag, id='+this.id);
} else {
html=this.print(this);
if (typeof html != typeof '') {html='';}
else if (typeof this.shortcut!='undefined') html=addPopupShortcut(html, this.shortcut);
}
return opening + html + closing;
};
navlinkTag.prototype.getNewWin=function() {
getValueOf('popupLinksNewWindow');
if (typeof pg.option.popupLinksNewWindow[this.id] === 'undefined') { this.newWin=null; }
this.newWin=pg.option.popupLinksNewWindow[this.id];
};
navlinkTag.prototype.getPrintFunction=function() { //think about this some more
// this.id and this.article should already be defined
if (typeof this.id!=typeof '' || typeof this.article!=typeof {} ) { return; }
this.noPopup=1;
switch (this.id) {
case 'contribs': case 'history': case 'whatLinksHere':
case 'userPage': case 'monobook': case 'userTalk':
case 'talk': case 'article': case 'lastEdit':
this.noPopup=null;
}
switch (this.id) {
case 'email': case 'contribs': case 'block': case 'unblock':
case 'userlog': case 'userSpace': case 'deletedContribs':
this.article=this.article.userName();
}
switch (this.id) {
case 'userTalk': case 'newUserTalk': case 'editUserTalk':
case 'userPage': case 'monobook': case 'editMonobook': case 'blocklog':
this.article=this.article.userName(true);
/* fall through */
case 'pagelog': case 'deletelog': case 'protectlog':
delete this.oldid;
}
if (this.id=='editMonobook' || this.id=='monobook') { this.article.append('/monobook.js'); }
if (this.id != 'mainlink') {
// FIXME anchor handling should be done differently with Title object
this.article=this.article.removeAnchor();
// if (typeof this.text=='undefined') this.text=popupString(this.id);
}
switch (this.id) {
case 'undelete': this.print=specialLink; this.specialpage='Undelete'; this.sep='/'; break;
case 'whatLinksHere': this.print=specialLink; this.specialpage='Whatlinkshere'; break;
case 'relatedChanges': this.print=specialLink; this.specialpage='Recentchangeslinked'; break;
case 'move': this.print=specialLink; this.specialpage='Movepage'; break;
case 'contribs': this.print=specialLink; this.specialpage='Contributions'; break;
case 'deletedContribs':this.print=specialLink; this.specialpage='Deletedcontributions'; break;
case 'email': this.print=specialLink; this.specialpage='EmailUser'; this.sep='/'; break;
case 'block': this.print=specialLink; this.specialpage='Blockip'; this.sep='&ip='; break;
case 'unblock': this.print=specialLink; this.specialpage='Ipblocklist'; this.sep='&action=unblock&ip='; break;
case 'userlog': this.print=specialLink; this.specialpage='Log'; this.sep='&user='; break;
case 'blocklog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=block&page='; break;
case 'pagelog': this.print=specialLink; this.specialpage='Log'; this.sep='&page='; break;
case 'protectlog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=protect&page='; break;
case 'deletelog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=delete&page='; break;
case 'userSpace': this.print=specialLink; this.specialpage='PrefixIndex'; this.sep='&namespace=2&prefix='; break;
case 'search': this.print=specialLink; this.specialpage='Search'; this.sep='&fulltext=Search&search='; break;
case 'thank': this.print=specialLink; this.specialpage='Thanks'; this.sep='/'; this.article.value = this.diff; break;
case 'unwatch': case 'watch':
this.print=magicWatchLink; this.action=this.id+'&autowatchlist=1&autoimpl=' + popupString('autoedit_version') + '&actoken='+autoClickToken(); break;
case 'history': case 'historyfeed':
case 'unprotect': case 'protect':
this.print=wikiLink; this.action=this.id; break;
case 'delete':
this.print=wikiLink; this.action='delete';
if (this.article.namespaceId()==pg.nsImageId) {
var img=this.article.stripNamespace();
this.action+='&image='+img;
}
break;
case 'markpatrolled':
case 'edit': // editOld should keep the oldid, but edit should not.
delete this.oldid;
/* fall through */
case 'view': case 'purge': case 'render':
this.print=wikiLink;
this.action=this.id; break;
case 'raw':
this.print=wikiLink; this.action='raw'; break;
case 'new':
this.print=wikiLink; this.action='edit§ion=new'; break;
case 'mainlink':
if (typeof this.text=='undefined') { this.text=this.article.toString().entify(); }
if (getValueOf('popupSimplifyMainLink') && isInStrippableNamespace(this.article)) {
// only show the /subpage part of the title text
var s=this.text.split('/'); this.text=s[s.length-1];
if (this.text==='' && s.length > 1) { this.text=s[s.length-2]; }
}
this.print=titledWikiLink;
if (typeof this.title==='undefined' && pg.current.link && typeof pg.current.link.href !== 'undefined') {
this.title=safeDecodeURI((pg.current.link.originalTitle)?pg.current.link.originalTitle:this.article);
if (typeof this.oldid !== 'undefined' && this.oldid) {
this.title=tprintf('Revision %s of %s', [this.oldid, this.title]);
}
}
this.action='view'; break;
case 'userPage':
case 'article':
case 'monobook':
case 'editMonobook':
case 'editArticle':
delete this.oldid;
//alert(this.id+'\n'+this.article + '\n'+ typeof this.article);
this.article=this.article.articleFromTalkOrArticle();
//alert(this.id+'\n'+this.article + '\n'+ typeof this.article);
this.print=wikiLink;
if (this.id.indexOf('edit')===0) {
this.action='edit';
} else { this.action='view';}
break;
case 'userTalk':
case 'talk':
this.article=this.article.talkPage();
delete this.oldid;
this.print=wikiLink;
this.action='view'; break;
case 'arin':
this.print=arinLink; break;
case 'count':
this.print=editCounterLink; break;
case 'google':
this.print=googleLink; break;
case 'editors':
this.print=editorListLink; break;
case 'globalsearch':
this.print=globalSearchLink; break;
case 'lastEdit':
this.print=titledDiffLink;
this.title=popupString('Show the last edit');
this.from='prev'; this.to='cur'; break;
case 'oldEdit':
this.print=titledDiffLink;
this.title=popupString('Show the edit made to get revision') + ' ' + this.oldid;
this.from='prev'; this.to=this.oldid; break;
case 'editOld':
this.print=wikiLink; this.action='edit'; break;
case 'undo':
this.print=wikiLink; this.action='edit&undo='; break;
case 'revert':
this.print=wikiLink; this.action='revert'; break;
case 'nullEdit':
this.print=wikiLink; this.action='nullEdit'; break;
case 'diffCur':
this.print=titledDiffLink;
this.title=tprintf('Show changes since revision %s', [this.oldid]);
this.from=this.oldid; this.to='cur'; break;
case 'editUserTalk':
case 'editTalk':
delete this.oldid;
this.article=this.article.talkPage();
this.action='edit'; this.print=wikiLink; break;
case 'newUserTalk':
case 'newTalk':
this.article=this.article.talkPage();
this.action='edit§ion=new'; this.print=wikiLink; break;
case 'lastContrib':
case 'sinceMe':
this.print=magicHistoryLink;
break;
case 'togglePreviews':
this.text=popupString(pg.option.simplePopups ? 'enable previews' : 'disable previews');
/* fall through */
case 'disablePopups': case 'purgePopups':
this.print=popupMenuLink;
break;
default:
this.print=function () {return 'Unknown navlink type: '+this.id+'';};
}
};
//
// end navlinks
//////////////////////////////////////////////////
//</NOLITE>
// ENDFILE: navlinks.js
// STARTFILE: shortcutkeys.js
//<NOLITE>
function popupHandleKeypress(evt) {
var keyCode = window.event ? window.event.keyCode : ( evt.keyCode ? evt.keyCode : evt.which);
if (!keyCode || !pg.current.link || !pg.current.link.navpopup) { return; }
if (keyCode==27) { // escape
killPopup();
return false; // swallow keypress
}
var letter=String.fromCharCode(keyCode);
var links=pg.current.link.navpopup.mainDiv.getElementsByTagName('A');
var startLink=0;
var i,j;
if (popupHandleKeypress.lastPopupLinkSelected) {
for (i=0; i<links.length; ++i) {
if (links[i]==popupHandleKeypress.lastPopupLinkSelected) { startLink=i; }
}
}
for (j=0; j<links.length; ++j) {
i=(startLink + j + 1) % links.length;
if (links[i].getAttribute('popupkey')==letter) {
if (evt && evt.preventDefault) evt.preventDefault();
links[i].focus();
popupHandleKeypress.lastPopupLinkSelected=links[i];
return false; // swallow keypress
}
}
// pass keypress on
if (document.oldPopupOnkeypress) { return document.oldPopupOnkeypress(evt); }
return true;
}
function addPopupShortcuts() {
if (document.onkeypress!=popupHandleKeypress) {
document.oldPopupOnkeypress=document.onkeypress;
}
document.onkeypress=popupHandleKeypress;
}
function rmPopupShortcuts() {
popupHandleKeypress.lastPopupLinkSelected=null;
try {
if (document.oldPopupOnkeypress && document.oldPopupOnkeypress==popupHandleKeypress) {
// panic
document.onkeypress=null; //function () {};
return;
}
document.onkeypress=document.oldPopupOnkeypress;
} catch (nasties) { /* IE goes here */ }
}
function addLinkProperty(html, property) {
// take "<a href=...>...</a> and add a property
// not sophisticated at all, easily broken
var i=html.indexOf('>');
if (i<0) { return html; }
return html.substring(0,i) + ' ' + property + html.substring(i);
}
function addPopupShortcut(html, key) {
if (!getValueOf('popupShortcutKeys')) { return html; }
var ret= addLinkProperty(html, 'popupkey="'+key+'"');
if (key==' ') { key=popupString('spacebar'); }
return ret.replace(RegExp('^(.*?)(title=")(.*?)(".*)$', 'i'),'$1$2$3 ['+key+']$4');
}
//</NOLITE>
// ENDFILE: shortcutkeys.js
// STARTFILE: diffpreview.js
//<NOLITE>
//lets jump through hoops to find the rev ids we need to retrieve
function loadDiff(article, oldid, diff, navpop) {
navpop.diffData={ oldRev: {}, newRev: {} };
mw.loader.using( 'mediawiki.api' ).then( function() {
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'compare',
prop: 'ids|title'
};
if(article.title){
params.fromtitle = article.title;
}
switch (diff) {
case 'cur':
switch ( oldid ) {
case null:
case '':
case 'prev':
// this can only work if we have the title
// cur -> prev
params.torelative = 'prev';
break;
default:
params.fromrev = oldid;
params.torelative = 'cur';
break;
}
break;
case 'prev':
if( oldid ) {
params.fromrev = oldid;
} else {
params.fromtitle;
}
params.torelative = 'prev';
break;
case 'next':
params.fromrev = oldid || 0;
params.torelative = 'next';
break;
default:
params.fromrev = oldid || 0;
params.torev = diff || 0;
break;
}
api.get( params ).then( function( data ) {
navpop.diffData.oldRev.revid = data.compare.fromrevid;
navpop.diffData.newRev.revid = data.compare.torevid;
addReviewLink(navpop, 'popupMiscTools');
var go = function() {
pendingNavpopTask(navpop);
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&';
url += 'revids=' + navpop.diffData.oldRev.revid + '|' + navpop.diffData.newRev.revid;
url += '&prop=revisions&rvprop=ids|timestamp|content';
getPageWithCaching(url, doneDiff, navpop);
return true; // remove hook once run
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'before', 'DOWNLOAD_DIFFS'); }
} );
} );
}
// Put a "mark patrolled" link to an element target
// TODO: Allow patrol a revision, as well as a diff
function addReviewLink (navpop, target) {
if (! pg.user.canReview) return;
// If 'newRev' is older than 'oldRev' than it could be confusing, so we do not show the review link.
if (navpop.diffData.newRev.revid <= navpop.diffData.oldRev.revid) return;
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'query',
prop: 'info|flagged',
revids: navpop.diffData.oldRev.revid,
formatversion : 2
};
api.get (params).then(function(data){
var stable_revid = data.query.pages[0].flagged && data.query.pages[0].flagged.stable_revid || 0;
// The diff can be reviewed if the old version is the last reviewed version
// TODO: Other possible conditions that we may want to implement instead of this one:
// * old version is patrolled and the new version is not patrolled
// * old version is patrolled and the new version is more recent than the last reviewed version
if (stable_revid == navpop.diffData.oldRev.revid) {
var a = document.createElement('a');
a.innerHTML = popupString('mark patrolled');
a.title=popupString('markpatrolledHint');
a.onclick = function() {
var params = {
action: 'review',
revid: navpop.diffData.newRev.revid,
comment: tprintf('defaultpopupReviewedSummary', [navpop.diffData.oldRev.revid, navpop.diffData.newRev.revid])
};
api.postWithToken('csrf',params).done(function(){
a.style.display = "none";
// TODO: Update current page and other already constructed popups
} ).fail(function(){
alert(popupString('Could not marked this edit as patrolled'));
});
};
setPopupHTML(a, target, navpop.idNumber,null,true);
}
});
}
function doneDiff(download) {
if (!download.owner || !download.owner.diffData) { return; }
var navpop=download.owner;
completedNavpopTask(navpop);
var pages, revisions=[];
try{
// Process the downloads
pages = getJsObj(download.data).query.pages;
for(var i=0; i < pages.length; i++ ) {
revisions = revisions.concat(pages[i].revisions);
}
for(i=0; i< revisions.length; i++){
if(revisions[i].revid == navpop.diffData.oldRev.revid) {
navpop.diffData.oldRev.revision = revisions[i];
} else if (revisions[i].revid == navpop.diffData.newRev.revid) {
navpop.diffData.newRev.revision = revisions[i];
}
}
} catch(someError) {
errlog( 'Could not get diff' );
}
insertDiff(navpop);
}
function rmBoringLines(a,b,context) {
if (typeof context == 'undefined') { context=2; }
// this is fairly slow... i think it's quicker than doing a word-based diff from the off, though
var aa=[], aaa=[];
var bb=[], bbb=[];
var i, j;
// first, gather all disconnected nodes in a and all crossing nodes in a and b
for (i=0; i<a.length; ++i ) {
if(!a[i].paired) { aa[i]=1; }
else if (countCrossings(b,a,i, true)) {
aa[i]=1;
bb[ a[i].row ] = 1;
}
}
// pick up remaining disconnected nodes in b
for (i=0; i<b.length; ++i ) {
if (bb[i]==1) { continue; }
if(!b[i].paired) { bb[i]=1; }
}
// another pass to gather context: we want the neighbours of included nodes which are not yet included
// we have to add in partners of these nodes, but we don't want to add context for *those* nodes in the next pass
for (i=0; i<b.length; ++i) {
if ( bb[i] == 1 ) {
for (j=Math.max(0,i-context); j < Math.min(b.length, i+context); ++j) {
if ( !bb[j] ) { bb[j] = 1; aa[ b[j].row ] = 0.5; }
}
}
}
for (i=0; i<a.length; ++i) {
if ( aa[i] == 1 ) {
for (j=Math.max(0,i-context); j < Math.min(a.length, i+context); ++j) {
if ( !aa[j] ) { aa[j] = 1; bb[ a[j].row ] = 0.5; }
}
}
}
for (i=0; i<bb.length; ++i) {
if (bb[i] > 0) { // it's a row we need
if (b[i].paired) { bbb.push(b[i].text); } // joined; partner should be in aa
else {
bbb.push(b[i]);
}
}
}
for (i=0; i<aa.length; ++i) {
if (aa[i] > 0) { // it's a row we need
if (a[i].paired) { aaa.push(a[i].text); } // joined; partner should be in aa
else {
aaa.push(a[i]);
}
}
}
return { a: aaa, b: bbb};
}
function stripOuterCommonLines(a,b,context) {
var i=0;
while (i<a.length && i < b.length && a[i]==b[i]) { ++i; }
var j=a.length-1; var k=b.length-1;
while ( j>=0 && k>=0 && a[j]==b[k] ) { --j; --k; }
return { a: a.slice(Math.max(0,i - 1 - context), Math.min(a.length+1, j + context+1)),
b: b.slice(Math.max(0,i - 1 - context), Math.min(b.length+1, k + context+1)) };
}
function insertDiff(navpop) {
// for speed reasons, we first do a line-based diff, discard stuff that seems boring, then do a word-based diff
// FIXME: sometimes this gives misleading diffs as distant chunks are squashed together
var oldlines = navpop.diffData.oldRev.revision.content.split('\n');
var newlines = navpop.diffData.newRev.revision.content.split('\n');
var inner=stripOuterCommonLines(oldlines,newlines,getValueOf('popupDiffContextLines'));
oldlines=inner.a; newlines=inner.b;
var truncated=false;
getValueOf('popupDiffMaxLines');
if (oldlines.length > pg.option.popupDiffMaxLines || newlines.length > pg.option.popupDiffMaxLines) {
// truncate
truncated=true;
inner=stripOuterCommonLines(oldlines.slice(0,pg.option.popupDiffMaxLines),
newlines.slice(0,pg.option.popupDiffMaxLines),
pg.option.popupDiffContextLines);
oldlines=inner.a; newlines=inner.b;
}
var lineDiff=diff(oldlines, newlines);
var lines2=rmBoringLines(lineDiff.o, lineDiff.n);
var oldlines2=lines2.a; var newlines2=lines2.b;
var simpleSplit = !String.prototype.parenSplit.isNative;
var html='<hr />';
if (getValueOf('popupDiffDates')) {
html += diffDatesTable(navpop);
html += '<hr />';
}
html += shortenDiffString(
diffString(oldlines2.join('\n'), newlines2.join('\n'), simpleSplit),
getValueOf('popupDiffContextCharacters') ).join('<hr />');
setPopupTipsAndHTML(html.split('\n').join('<br>') +
(truncated ? '<hr /><b>'+popupString('Diff truncated for performance reasons')+'</b>' : '') ,
'popupPreview', navpop.idNumber);
}
function diffDatesTable( navpop ) {
var html='<table class="popup_diff_dates">';
html += diffDatesTableRow( navpop.diffData.newRev.revision, tprintf('New revision'));
html += diffDatesTableRow( navpop.diffData.oldRev.revision, tprintf('Old revision'));
html += '</table>';
return html;
}
function diffDatesTableRow( revision, label ) {
var txt='';
var lastModifiedDate = new Date(revision.timestamp);
var datePrint=getValueOf('popupDiffDatePrinter');
if (typeof lastModifiedDate[datePrint] == 'function') {
var d2 = adjustDate(lastModifiedDate, getTimeOffset());
txt = dayFormat(d2, true) + ' ' + timeFormat(d2, true);
} else {
txt = tprintf('Invalid %s %s', ['popupDiffDatePrinter', datePrint]);
}
var revlink = generalLink({url: mw.config.get('wgScript') + '?oldid='+revision.revid,
text: label, title: label});
return simplePrintf('<tr><td>%s</td><td>%s</td></tr>', [ revlink, txt ]);
}
//</NOLITE>
// ENDFILE: diffpreview.js
// STARTFILE: links.js
//<NOLITE>
/////////////////////
// LINK GENERATION //
/////////////////////
// titledDiffLink --> titledWikiLink --> generalLink
// wikiLink --> titledWikiLink --> generalLink
// editCounterLink --> generalLink
// TODO Make these functions return Element objects, not just raw HTML strings.
function titledDiffLink(l) { // article, text, title, from, to) {
return titledWikiLink({article: l.article, action: l.to + '&oldid=' + l.from,
newWin: l.newWin,
noPopup: l.noPopup,
text: l.text, title: l.title,
/* hack: no oldid here */
actionName: 'diff'});
}
function wikiLink(l) {
//{article:article, action:action, text:text, oldid, newid}) {
if (! (typeof l.article == typeof {} &&
typeof l.action == typeof '' &&
typeof l.text==typeof '')) return null;
if (typeof l.oldid == 'undefined') { l.oldid=null; }
var savedOldid = l.oldid;
if (!/^(edit|view|revert|render)$|^raw/.test(l.action)) { l.oldid=null; }
var hint=popupString(l.action + 'Hint'); // revertHint etc etc etc
var oldidData=[l.oldid, safeDecodeURI(l.article)];
var revisionString = tprintf('revision %s of %s', oldidData);
log('revisionString='+revisionString);
switch (l.action) {
case 'edit§ion=new': hint = popupString('newSectionHint'); break;
case 'edit&undo=':
if (l.diff && l.diff != 'prev' && savedOldid ) {
l.action += l.diff + '&undoafter=' + savedOldid;
} else if (savedOldid) {
l.action += savedOldid;
}
hint = popupString('undoHint');
break;
case 'raw&ctype=text/css': hint=popupString('rawHint'); break;
case 'revert':
var p=parseParams(pg.current.link.href);
l.action='edit&autoclick=wpSave&actoken=' + autoClickToken() + '&autoimpl=' + popupString('autoedit_version') + '&autosummary=' + revertSummary(l.oldid, p.diff);
if (p.diff=='prev') {
l.action += '&direction=prev';
revisionString = tprintf('the revision prior to revision %s of %s', oldidData);
}
if (getValueOf('popupRevertSummaryPrompt')) { l.action += '&autosummaryprompt=true'; }
if (getValueOf('popupMinorReverts')) { l.action += '&autominor=true'; }
log('revisionString is now '+revisionString);
break;
case 'nullEdit':
l.action='edit&autoclick=wpSave&actoken=' + autoClickToken() + '&autoimpl=' + popupString('autoedit_version') + '&autosummary=null';
break;
case 'historyfeed':
l.action='history&feed=rss';
break;
case 'markpatrolled':
l.action='markpatrolled&rcid='+l.rcid;
}
if (hint) {
if (l.oldid) {
hint = simplePrintf(hint, [revisionString]);
}
else {
hint = simplePrintf(hint, [safeDecodeURI(l.article)]);
}
}
else {
hint = safeDecodeURI(l.article + '&action=' + l.action) + (l.oldid) ? '&oldid='+l.oldid : '';
}
return titledWikiLink({article: l.article, action: l.action, text: l.text, newWin:l.newWin,
title: hint, oldid: l.oldid, noPopup: l.noPopup, onclick: l.onclick});
}
function revertSummary(oldid, diff) {
var ret='';
if (diff == 'prev') {
ret=getValueOf('popupQueriedRevertToPreviousSummary');
} else { ret = getValueOf('popupQueriedRevertSummary'); }
return ret + '&autorv=' + oldid;
}
function titledWikiLink(l) {
// possible properties of argument:
// article, action, text, title, oldid, actionName, className, noPopup
// oldid = null is fine here
// article and action are mandatory args
if (typeof l.article == 'undefined' || typeof l.action=='undefined') {
errlog('got undefined article or action in titledWikiLink');
return null;
}
var base = pg.wiki.titlebase + l.article.urlString();
var url=base;
if (typeof l.actionName=='undefined' || !l.actionName) { l.actionName='action'; }
// no need to add &action=view, and this confuses anchors
if (l.action != 'view') { url = base + '&' + l.actionName + '=' + l.action; }
if (typeof l.oldid!='undefined' && l.oldid) { url+='&oldid='+l.oldid; }
var cssClass=pg.misc.defaultNavlinkClassname;
if (typeof l.className!='undefined' && l.className) { cssClass=l.className; }
return generalNavLink({url: url, newWin: l.newWin,
title: (typeof l.title != 'undefined') ? l.title : null,
text: (typeof l.text!='undefined')?l.text:null,
className: cssClass, noPopup:l.noPopup, onclick:l.onclick});
}
pg.fn.getLastContrib = function getLastContrib(wikipage, newWin) {
getHistoryInfo(wikipage, function(x) {
processLastContribInfo(x, {page: wikipage, newWin: newWin});
});
};
function processLastContribInfo(info, stuff) {
if(!info.edits || !info.edits.length) { alert('Popups: an odd thing happened. Please retry.'); return; }
if(!info.firstNewEditor) {
alert(tprintf('Only found one editor: %s made %s edits', [info.edits[0].editor,info.edits.length]));
return;
}
var newUrl=pg.wiki.titlebase + new Title(stuff.page).urlString() + '&diff=cur&oldid='+info.firstNewEditor.oldid;
displayUrl(newUrl, stuff.newWin);
}
pg.fn.getDiffSinceMyEdit = function getDiffSinceMyEdit(wikipage, newWin) {
getHistoryInfo(wikipage, function(x){
processDiffSinceMyEdit(x, {page: wikipage, newWin: newWin});
});
};
function processDiffSinceMyEdit(info, stuff) {
if(!info.edits || !info.edits.length) { alert('Popups: something fishy happened. Please try again.'); return; }
var friendlyName=stuff.page.split('_').join(' ');
if(!info.myLastEdit) {
alert(tprintf('Couldn\'t find an edit by %s\nin the last %s edits to\n%s',
[info.userName, getValueOf('popupHistoryLimit'), friendlyName]));
return;
}
if(info.myLastEdit.index === 0) {
alert(tprintf("%s seems to be the last editor to the page %s", [info.userName, friendlyName]));
return;
}
var newUrl=pg.wiki.titlebase + new Title(stuff.page).urlString() + '&diff=cur&oldid='+ info.myLastEdit.oldid;
displayUrl(newUrl, stuff.newWin);
}
function displayUrl(url, newWin){
if(newWin) { window.open(url); }
else { document.location=url; }
}
pg.fn.purgePopups = function purgePopups() {
processAllPopups(true);
setupCache(); // deletes all cached items (not browser cached, though...)
pg.option={};
abortAllDownloads();
};
function processAllPopups(nullify, banish) {
for (var i=0; pg.current.links && i<pg.current.links.length; ++i) {
if (!pg.current.links[i].navpopup) { continue; }
if (nullify || banish) pg.current.links[i].navpopup.banish();
pg.current.links[i].simpleNoMore=false;
if (nullify) pg.current.links[i].navpopup=null;
}
}
pg.fn.disablePopups = function disablePopups(){
processAllPopups(false, true);
setupTooltips(null, true);
};
pg.fn.togglePreviews = function togglePreviews() {
processAllPopups(true, true);
pg.option.simplePopups=!pg.option.simplePopups;
abortAllDownloads();
};
function magicWatchLink(l) {
//Yuck!! Would require a thorough redesign to add this as a click event though ...
l.onclick = simplePrintf( 'pg.fn.modifyWatchlist(\'%s\',\'%s\');return false;', [l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), this.id] );
return wikiLink(l);
}
pg.fn.modifyWatchlist = function modifyWatchlist(title, action) {
var reqData = {
'action': 'watch',
'formatversion': 2,
'titles': title,
'uselang': mw.config.get('wgUserLanguage')
};
if ( action === 'unwatch' ) reqData.unwatch = true;
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
// Load the Addedwatchtext or Removedwatchtext message and show it
var mwTitle = mw.Title.newFromText( title );
var messageName;
if ( mwTitle && mwTitle.getNamespaceId() > 0 && mwTitle.getNamespaceId() % 2 === 1 ) {
messageName = action === 'watch' ? 'addedwatchtext-talk' : 'removedwatchtext-talk';
} else {
messageName = action === 'watch' ? 'addedwatchtext' : 'removedwatchtext';
}
$.when(
api.postWithToken( 'watch', reqData ),
mw.loader.using( [ 'mediawiki.api', 'mediawiki.jqueryMsg' ] ).then( function () {
return api.loadMessagesIfMissing( [ messageName ] );
} )
).done( function () {
mw.notify( mw.message( messageName, title ).parseDom() );
} );
};
function magicHistoryLink(l) {
// FIXME use onclick change href trick to sort this out instead of window.open
var jsUrl='', title='', onClick='';
switch(l.id) {
case 'lastContrib':
onClick=simplePrintf('pg.fn.getLastContrib(\'%s\',%s)',
[l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), l.newWin]);
title=popupString('lastContribHint');
break;
case 'sinceMe':
onClick=simplePrintf('pg.fn.getDiffSinceMyEdit(\'%s\',%s)',
[l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), l.newWin]);
title=popupString('sinceMeHint');
break;
}
jsUrl = 'javascript:' + onClick; // jshint ignore:line
onClick += ';return false;';
return generalNavLink({url: jsUrl, newWin: false, // can't have new windows with JS links, I think
title: title, text: l.text, noPopup: l.noPopup, onclick: onClick });
}
function popupMenuLink(l) {
var jsUrl=simplePrintf('javascript:pg.fn.%s()', [l.id]); // jshint ignore:line
var title=popupString(simplePrintf('%sHint', [l.id]));
var onClick=simplePrintf('pg.fn.%s();return false;', [l.id]);
return generalNavLink({url: jsUrl, newWin:false, title:title, text:l.text, noPopup:l.noPopup, onclick: onClick});
}
function specialLink(l) {
// properties: article, specialpage, text, sep
if (typeof l.specialpage=='undefined'||!l.specialpage) return null;
var base = pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId]+':'+l.specialpage;
if (typeof l.sep == 'undefined' || l.sep === null) l.sep='&target=';
var article=l.article.urlString({keepSpaces: l.specialpage=='Search'});
var hint=popupString(l.specialpage+'Hint');
switch (l.specialpage) {
case 'Log':
switch (l.sep) {
case '&user=': hint=popupString('userLogHint'); break;
case '&type=block&page=': hint=popupString('blockLogHint'); break;
case '&page=': hint=popupString('pageLogHint'); break;
case '&type=protect&page=': hint=popupString('protectLogHint'); break;
case '&type=delete&page=': hint=popupString('deleteLogHint'); break;
default: log('Unknown log type, sep=' + l.sep); hint='Missing hint (FIXME)';
}
break;
case 'PrefixIndex': article += '/'; break;
}
if (hint) hint = simplePrintf(hint, [safeDecodeURI(l.article)]);
else hint = safeDecodeURI(l.specialpage+':'+l.article) ;
var url = base + l.sep + article;
return generalNavLink({url: url, title: hint, text: l.text, newWin:l.newWin, noPopup:l.noPopup});
}
function generalLink(l) {
// l.url, l.text, l.title, l.newWin, l.className, l.noPopup, l.onclick
if (typeof l.url=='undefined') return null;
// only quotation marks in the url can screw us up now... I think
var url=l.url.split('"').join('%22');
var ret='<a href="' + url + '"';
if (typeof l.title!='undefined' && l.title) { ret += ' title="' + pg.escapeQuotesHTML(l.title) + '"'; }
if (typeof l.onclick!='undefined' && l.onclick) { ret += ' onclick="' + pg.escapeQuotesHTML(l.onclick) + '"'; }
if (l.noPopup) { ret += ' noPopup=1'; }
var newWin;
if (typeof l.newWin=='undefined' || l.newWin === null) { newWin=getValueOf('popupNewWindows'); }
else { newWin=l.newWin; }
if (newWin) { ret += ' target="_blank"'; }
if (typeof l.className!='undefined'&&l.className) { ret+=' class="'+l.className+'"'; }
ret += '>';
if (typeof l.text==typeof '') { ret+= l.text; }
ret +='</a>';
return ret;
}
function appendParamsToLink(linkstr, params) {
var sp=linkstr.parenSplit(RegExp('(href="[^"]+?)"', 'i'));
if (sp.length<2) return null;
var ret=sp.shift() + sp.shift();
ret += '&' + params + '"';
ret += sp.join('');
return ret;
}
function changeLinkTargetLink(x) { // newTarget, text, hint, summary, clickButton, minor, title (optional), alsoChangeLabel {
if (x.newTarget) {
log ('changeLinkTargetLink: newTarget=' + x.newTarget);
}
if (x.oldTarget !== decodeURIComponent( x.oldTarget ) ) {
log ('This might be an input problem: ' + x.oldTarget );
}
// FIXME: first character of page title as well as namespace should be case insensitive
// eg [[category:X1]] and [[Category:X1]] are equivalent
// this'll break if charAt(0) is nasty
var cA = mw.util.escapeRegExp(x.oldTarget);
var chs = cA.charAt(0).toUpperCase();
chs='['+chs + chs.toLowerCase()+']';
var currentArticleRegexBit=chs+cA.substring(1);
currentArticleRegexBit=currentArticleRegexBit
.split(RegExp('(?:[_ ]+|%20)', 'g')).join('(?:[_ ]+|%20)')
.split('\\(').join('(?:%28|\\()')
.split('\\)').join('(?:%29|\\))'); // why does this need to match encoded strings ? links in the document ?
// leading and trailing space should be ignored, and anchor bits optional:
currentArticleRegexBit = '\\s*(' + currentArticleRegexBit + '(?:#[^\\[\\|]*)?)\\s*';
// e.g. Computer (archaic) -> \s*([Cc]omputer[_ ](?:%2528|\()archaic(?:%2528|\)))\s*
// autoedit=s~\[\[([Cc]ad)\]\]~[[Computer-aided%20design|$1]]~g;s~\[\[([Cc]AD)[|]~[[Computer-aided%20design|~g
var title=x.title || mw.config.get('wgPageName').split('_').join(' ');
var lk=titledWikiLink({article: new Title(title), newWin:x.newWin,
action: 'edit',
text: x.text,
title: x.hint,
className: 'popup_change_title_link'
});
var cmd='';
if (x.newTarget) {
// escape '&' and other nasties
var t = x.newTarget;
var s = mw.util.escapeRegExp(x.newTarget);
if (x.alsoChangeLabel) {
cmd += 's~\\[\\[' + currentArticleRegexBit + '\\]\\]~[[' + t + ']]~g;';
cmd += 's~\\[\\[' + currentArticleRegexBit + '[|]~[[' + t + '|~g;';
cmd += 's~\\[\\[' + s + '\\|' + s + '\\]\\]~[[' + t + ']]~g';
} else {
cmd += 's~\\[\\[' + currentArticleRegexBit + '\\]\\]~[[' + t + '|$1]]~g;';
cmd += 's~\\[\\[' + currentArticleRegexBit + '[|]~[[' + t + '|~g;';
cmd += 's~\\[\\[' + s + '\\|' + s + '\\]\\]~[[' + t + ']]~g';
}
} else {
cmd += 's~\\[\\['+currentArticleRegexBit+'\\]\\]~$1~g;';
cmd += 's~\\[\\['+currentArticleRegexBit+'[|](.*?)\\]\\]~$2~g';
}
// Build query
cmd = 'autoedit=' + encodeURIComponent ( cmd );
cmd += '&autoclick='+ encodeURIComponent( x.clickButton ) + '&actoken=' + encodeURIComponent( autoClickToken() );
cmd += ( x.minor === null ) ? '' : '&autominor='+ encodeURIComponent( x.minor );
cmd += ( x.watch === null ) ? '' : '&autowatch='+ encodeURIComponent( x.watch );
cmd += '&autosummary='+encodeURIComponent(x.summary);
cmd += '&autoimpl='+encodeURIComponent( popupString('autoedit_version') );
return appendParamsToLink(lk, cmd);
}
function redirLink(redirMatch, article) {
// NB redirMatch is in wikiText
var ret='';
if (getValueOf('popupAppendRedirNavLinks') && getValueOf('popupNavLinks')) {
ret += '<hr />';
if (getValueOf('popupFixRedirs') && typeof autoEdit != 'undefined' && autoEdit) {
ret += popupString('Redirects to: (Fix ');
log('redirLink: newTarget=' + redirMatch);
ret += addPopupShortcut(changeLinkTargetLink({
newTarget: redirMatch,
text: popupString('target'),
hint: popupString('Fix this redirect, changing just the link target'),
summary: simplePrintf(getValueOf('popupFixRedirsSummary'),[article.toString(), redirMatch]),
oldTarget: article.toString(),
clickButton: getValueOf('popupRedirAutoClick'),
minor: true,
watch: getValueOf('popupWatchRedirredPages')
}), 'R');
ret += popupString(' or ');
ret += addPopupShortcut(changeLinkTargetLink({
newTarget: redirMatch,
text: popupString('target & label'),
hint: popupString('Fix this redirect, changing the link target and label'),
summary: simplePrintf(getValueOf('popupFixRedirsSummary'),[article.toString(), redirMatch]),
oldTarget: article.toString(),
clickButton: getValueOf('popupRedirAutoClick'),
minor: true,
watch: getValueOf('popupWatchRedirredPages'),
alsoChangeLabel: true
}), 'R');
ret += popupString(')');
}
else ret += popupString('Redirects') + popupString(' to ');
return ret;
}
else return '<br> ' + popupString('Redirects') + popupString(' to ') +
titledWikiLink({article: new Title().fromWikiText(redirMatch), action: 'view', /* FIXME: newWin */
text: safeDecodeURI(redirMatch), title: popupString('Bypass redirect')});
}
function arinLink(l) {
if (!saneLinkCheck(l)) { return null; }
if ( ! l.article.isIpUser() || ! pg.wiki.wikimedia) return null;
var uN=l.article.userName();
return generalNavLink({url:'http://ws.arin.net/cgi-bin/whois.pl?queryinput=' + encodeURIComponent(uN), newWin:l.newWin,
title: tprintf('Look up %s in ARIN whois database', [uN]),
text: l.text, noPopup:1});
}
function toolDbName(cookieStyle) {
var ret = mw.config.get('wgDBname');
if (!cookieStyle) { ret+= '_p'; }
return ret;
}
function saneLinkCheck(l) {
if (typeof l.article != typeof {} || typeof l.text != typeof '') { return false; }
return true;
}
function editCounterLink(l) {
if(!saneLinkCheck(l)) return null;
if (! pg.wiki.wikimedia) return null;
var uN=l.article.userName();
var tool=getValueOf('popupEditCounterTool');
var url;
var defaultToolUrl='//tools.wmflabs.org/supercount/index.php?user=$1&project=$2.$3';
switch(tool) {
case 'custom':
url=simplePrintf(getValueOf('popupEditCounterUrl'), [ encodeURIComponent(uN), toolDbName() ]);
break;
case 'soxred': // no longer available
case 'kate': // no longer available
case 'interiot':// no longer available
/* fall through */
case 'supercount':
default:
var theWiki=pg.wiki.hostname.split('.');
url=simplePrintf(defaultToolUrl, [ encodeURIComponent(uN), theWiki[0], theWiki[1] ]);
}
return generalNavLink({url:url, title: tprintf('editCounterLinkHint', [uN]),
newWin:l.newWin, text: l.text, noPopup:1});
}
function globalSearchLink(l) {
if(!saneLinkCheck(l)) return null;
var base='http://vs.aka-online.de/cgi-bin/globalwpsearch.pl?timeout=120&search=';
var article=l.article.urlString({keepSpaces:true});
return generalNavLink({url:base + article, newWin:l.newWin,
title: tprintf('globalSearchHint', [safeDecodeURI(l.article)]),
text: l.text, noPopup:1});
}
function googleLink(l) {
if(!saneLinkCheck(l)) return null;
var base='https://www.google.com/search?q=';
var article=l.article.urlString({keepSpaces:true});
return generalNavLink({url:base + '%22' + article + '%22', newWin:l.newWin,
title: tprintf('googleSearchHint', [safeDecodeURI(l.article)]),
text: l.text, noPopup:1});
}
function editorListLink(l) {
if(!saneLinkCheck(l)) return null;
var article= l.article.articleFromTalkPage() || l.article;
var url='https://xtools.wmflabs.org/articleinfo/' +
encodeURI( pg.wiki.hostname ) + '/' +
article.urlString() +
'?uselang=' + mw.config.get('wgUserLanguage');
return generalNavLink({url:url,
title: tprintf('editorListHint', [article]),
newWin:l.newWin, text: l.text, noPopup:1});
}
function generalNavLink(l) {
l.className = (l.className === null) ? 'popupNavLink' : l.className;
return generalLink(l);
}
//////////////////////////////////////////////////
// magic history links
//
function getHistoryInfo(wikipage, whatNext) {
log('getHistoryInfo');
getHistory(wikipage, whatNext ? function(d){whatNext(processHistory(d));} : processHistory);
}
// FIXME eliminate pg.idNumber ... how? :-(
function getHistory(wikipage, onComplete) {
log('getHistory');
var url = pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&prop=revisions&titles=' +
new Title(wikipage).urlString() + '&rvlimit=' + getValueOf('popupHistoryLimit');
log('getHistory: url='+url);
return startDownload(url, pg.idNumber+'history', onComplete);
}
function processHistory(download) {
var jsobj = getJsObj(download.data);
try {
var revisions = anyChild(jsobj.query.pages).revisions;
var edits=[];
for (var i=0; i<revisions.length; ++i) {
edits.push({ oldid: revisions[i].revid, editor: revisions[i].user });
}
log('processed ' + edits.length + ' edits');
return finishProcessHistory( edits, mw.config.get('wgUserName') );
} catch (someError) {
log('Something went wrong with JSON business');
return finishProcessHistory([]);
}
}
function finishProcessHistory(edits, userName) {
var histInfo={};
histInfo.edits=edits;
histInfo.userName=userName;
for (var i=0; i<edits.length; ++i) {
if (typeof histInfo.myLastEdit === 'undefined' && userName && edits[i].editor==userName) {
histInfo.myLastEdit={index: i, oldid: edits[i].oldid, previd: (i === 0 ? null : edits[i-1].oldid)};
}
if (typeof histInfo.firstNewEditor === 'undefined' && edits[i].editor != edits[0].editor) {
histInfo.firstNewEditor={index:i, oldid:edits[i].oldid, previd: (i === 0 ? null : edits[i-1].oldid)};
}
}
//pg.misc.historyInfo=histInfo;
return histInfo;
}
//</NOLITE>
// ENDFILE: links.js
// STARTFILE: options.js
//////////////////////////////////////////////////
// options
// check for existing value, else use default
function defaultize(x) {
if (pg.option[x]===null || typeof pg.option[x]=='undefined') {
if (typeof window[x] != 'undefined' ) pg.option[x]=window[x];
else pg.option[x]=pg.optionDefault[x];
}
}
function newOption(x, def) {
pg.optionDefault[x]=def;
}
function setDefault(x, def) {
return newOption(x, def);
}
function getValueOf(varName) {
defaultize(varName);
return pg.option[varName];
}
/*eslint-disable */
function useDefaultOptions() { // for testing
for (var p in pg.optionDefault) {
pg.option[p]=pg.optionDefault[p];
if (typeof window[p]!='undefined') { delete window[p]; }
}
}
/*eslint-enable */
function setOptions() {
// user-settable parameters and defaults
var userIsSysop = false;
if ( mw.config.get('wgUserGroups') ) {
for ( var g = 0; g < mw.config.get('wgUserGroups').length; ++g ) {
if ( mw.config.get('wgUserGroups')[g] == "sysop" )
userIsSysop = true;
}
}
// Basic options
newOption('popupDelay', 0.5);
newOption('popupHideDelay', 0.5);
newOption('simplePopups', false);
newOption('popupStructure', 'shortmenus'); // see later - default for popupStructure is 'original' if simplePopups is true
newOption('popupActionsMenu', true);
newOption('popupSetupMenu', true);
newOption('popupAdminLinks', userIsSysop);
newOption('popupShortcutKeys', false);
newOption('popupHistoricalLinks', true);
newOption('popupOnlyArticleLinks', true);
newOption('removeTitles', true);
newOption('popupMaxWidth', 350);
newOption('popupSimplifyMainLink', true);
newOption('popupAppendRedirNavLinks', true);
newOption('popupTocLinks', false);
newOption('popupSubpopups', true);
newOption('popupDragHandle', false /* 'popupTopLinks'*/);
newOption('popupLazyPreviews', true);
newOption('popupLazyDownloads', true);
newOption('popupAllDabsStubs', false);
newOption('popupDebugging', false);
newOption('popupActiveNavlinks', true);
newOption('popupModifier', false); // ctrl, shift, alt or meta
newOption('popupModifierAction', 'enable'); // or 'disable'
newOption('popupDraggable', true);
newOption('popupReview', false);
//<NOLITE>
// images
newOption('popupImages', true);
newOption('imagePopupsForImages', true);
newOption('popupNeverGetThumbs', false);
//newOption('popupImagesToggleSize', true);
newOption('popupThumbAction', 'imagepage'); //'sizetoggle');
newOption('popupImageSize', 60);
newOption('popupImageSizeLarge', 200);
// redirs, dabs, reversion
newOption('popupFixRedirs', false);
newOption('popupRedirAutoClick', 'wpDiff');
newOption('popupFixDabs', false);
newOption('popupDabsAutoClick', 'wpDiff');
newOption('popupRevertSummaryPrompt', false);
newOption('popupMinorReverts', false);
newOption('popupRedlinkRemoval', false);
newOption('popupRedlinkAutoClick', 'wpDiff');
newOption('popupWatchDisambiggedPages', null);
newOption('popupWatchRedirredPages', null);
newOption('popupDabWiktionary', 'last');
// navlinks
newOption('popupNavLinks', true);
newOption('popupNavLinkSeparator', ' ⋅ ');
newOption('popupLastEditLink', true);
newOption('popupEditCounterTool', 'supercount');
newOption('popupEditCounterUrl', '');
//</NOLITE>
// previews etc
newOption('popupPreviews', true);
newOption('popupSummaryData', true);
newOption('popupMaxPreviewSentences', 9);
newOption('popupMaxPreviewCharacters', 900);
newOption('popupLastModified', true);
newOption('popupPreviewKillTemplates', true);
newOption('popupPreviewRawTemplates', true);
newOption('popupPreviewFirstParOnly', false);
newOption('popupPreviewCutHeadings', false);
newOption('popupPreviewButton', false);
newOption('popupPreviewButtonEvent', 'click');
//<NOLITE>
// diffs
newOption('popupPreviewDiffs', true);
newOption('popupDiffMaxLines', 100);
newOption('popupDiffContextLines', 2);
newOption('popupDiffContextCharacters', 40);
newOption('popupDiffDates', true);
newOption('popupDiffDatePrinter', 'toLocaleString');
// edit summaries. God, these are ugly.
newOption('popupReviewedSummary', popupString('defaultpopupReviewedSummary') );
newOption('popupFixDabsSummary', popupString('defaultpopupFixDabsSummary') );
newOption('popupExtendedRevertSummary', popupString('defaultpopupExtendedRevertSummary') );
newOption('popupRevertSummary', popupString('defaultpopupRevertSummary') );
newOption('popupRevertToPreviousSummary', popupString('defaultpopupRevertToPreviousSummary') );
newOption('popupQueriedRevertSummary', popupString('defaultpopupQueriedRevertSummary') );
newOption('popupQueriedRevertToPreviousSummary', popupString('defaultpopupQueriedRevertToPreviousSummary') );
newOption('popupFixRedirsSummary', popupString('defaultpopupFixRedirsSummary') );
newOption('popupRedlinkSummary', popupString('defaultpopupRedlinkSummary') );
newOption('popupRmDabLinkSummary', popupString('defaultpopupRmDabLinkSummary') );
//</NOLITE>
// misc
newOption('popupHistoryLimit', 50);
//<NOLITE>
newOption('popupFilters', [popupFilterStubDetect, popupFilterDisambigDetect,
popupFilterPageSize, popupFilterCountLinks,
popupFilterCountImages, popupFilterCountCategories,
popupFilterLastModified]);
newOption('extraPopupFilters', []);
newOption('popupOnEditSelection', 'cursor');
newOption('popupPreviewHistory', true);
newOption('popupImageLinks', true);
newOption('popupCategoryMembers', true);
newOption('popupUserInfo', true);
newOption('popupHistoryPreviewLimit', 25);
newOption('popupContribsPreviewLimit',25);
newOption('popupRevDelUrl', '//en.wikipedia.org/wiki/Wikipedia:Revision_deletion');
newOption('popupShowGender', true);
//</NOLITE>
// new windows
newOption('popupNewWindows', false);
newOption('popupLinksNewWindow', {'lastContrib': true, 'sinceMe': true});
// regexps
newOption('popupDabRegexp', '(\\{\\{\\s*disambig(?!uation needed)|disambig(uation|)\\s*\\}\\}|disamb\\s*\\}\\}|dab\\s*\\}\\})|\\{\\{\\s*(((geo|hn|road?|school|number)dis)|[234][lc][acw]|(road|ship)index)(\\s*[|][^}]*)?\\s*[}][}]|is a .*disambiguation.*page');
newOption('popupAnchorRegexp', 'anchors?'); //how to identify an anchors template
newOption('popupStubRegexp', '(sect)?stub[}][}]|This .*-related article is a .*stub');
newOption('popupImageVarsRegexp', 'image|image_(?:file|skyline|name|flag|seal)|cover|badge|logo');
}
// ENDFILE: options.js
// STARTFILE: strings.js
//<NOLITE>
//////////////////////////////////////////////////
// Translatable strings
//////////////////////////////////////////////////
//
// See instructions at
// https://en.wikipedia.org/wiki/Wikipedia:Tools/Navigation_popups/Translation
pg.string = {
/////////////////////////////////////
// summary data, searching etc.
/////////////////////////////////////
'article': 'article',
'category': 'category',
'categories': 'categories',
'image': 'image',
'images': 'images',
'stub': 'stub',
'section stub': 'section stub',
'Empty page': 'Empty page',
'kB': 'kB',
'bytes': 'bytes',
'day': 'day',
'days': 'days',
'hour': 'hour',
'hours': 'hours',
'minute': 'minute',
'minutes': 'minutes',
'second': 'second',
'seconds': 'seconds',
'week': 'week',
'weeks': 'weeks',
'search': 'search',
'SearchHint': 'Find English Wikipedia articles containing %s',
'web': 'web',
'global': 'global',
'globalSearchHint': 'Search across Wikipedias in different languages for %s',
'googleSearchHint': 'Google for %s',
/////////////////////////////////////
// article-related actions and info
// (some actions also apply to user pages)
/////////////////////////////////////
'actions': 'actions', ///// view articles and view talk
'popupsMenu': 'popups',
'togglePreviewsHint': 'Toggle preview generation in popups on this page',
'enable previews': 'enable previews',
'disable previews': 'disable previews',
'toggle previews': 'toggle previews',
'show preview': 'show preview',
'reset': 'reset',
'more...': 'more...',
'disable': 'disable popups',
'disablePopupsHint': 'Disable popups on this page. Reload page to re-enable.',
'historyfeedHint': 'RSS feed of recent changes to this page',
'purgePopupsHint': 'Reset popups, clearing all cached popup data.',
'PopupsHint': 'Reset popups, clearing all cached popup data.',
'spacebar': 'space',
'view': 'view',
'view article': 'view article',
'viewHint': 'Go to %s',
'talk': 'talk',
'talk page': 'talk page',
'this revision': 'this revision',
'revision %s of %s': 'revision %s of %s',
'Revision %s of %s': 'Revision %s of %s',
'the revision prior to revision %s of %s': 'the revision prior to revision %s of %s',
'Toggle image size': 'Click to toggle image size',
'del': 'del', ///// delete, protect, move
'delete': 'delete',
'deleteHint': 'Delete %s',
'undeleteShort': 'un',
'UndeleteHint': 'Show the deletion history for %s',
'protect': 'protect',
'protectHint': 'Restrict editing rights to %s',
'unprotectShort': 'un',
'unprotectHint': 'Allow %s to be edited by anyone again',
'send thanks': 'send thanks',
'ThanksHint': 'Send a thank you notification to this user',
'move': 'move',
'move page': 'move page',
'MovepageHint': 'Change the title of %s',
'edit': 'edit', ///// edit articles and talk
'edit article': 'edit article',
'editHint': 'Change the content of %s',
'edit talk': 'edit talk',
'new': 'new',
'new topic': 'new topic',
'newSectionHint': 'Start a new section on %s',
'null edit': 'null edit',
'nullEditHint': 'Submit an edit to %s, making no changes ',
'hist': 'hist', ///// history, diffs, editors, related
'history': 'history',
'historyHint': 'List the changes made to %s',
'last': 'prev', // For labelling the previous revision in history pages; the key is "last" for backwards compatibility
'lastEdit': 'lastEdit',
'mark patrolled': 'mark patrolled',
'markpatrolledHint': 'Mark this edit as patrolled',
'Could not marked this edit as patrolled': 'Could not marked this edit as patrolled',
'show last edit': 'most recent edit',
'Show the last edit': 'Show the effects of the most recent change',
'lastContrib': 'lastContrib',
'last set of edits': 'latest edits',
'lastContribHint': 'Show the net effect of changes made by the last editor',
'cur': 'cur',
'diffCur': 'diffCur',
'Show changes since revision %s': 'Show changes since revision %s',
'%s old': '%s old', // as in 4 weeks old
'oldEdit': 'oldEdit',
'purge': 'purge',
'purgeHint': 'Demand a fresh copy of %s',
'raw': 'source',
'rawHint': 'Download the source of %s',
'render': 'simple',
'renderHint': 'Show a plain HTML version of %s',
'Show the edit made to get revision': 'Show the edit made to get revision',
'sinceMe': 'sinceMe',
'changes since mine': 'diff my edit',
'sinceMeHint': 'Show changes since my last edit',
'Couldn\'t find an edit by %s\nin the last %s edits to\n%s': 'Couldn\'t find an edit by %s\nin the last %s edits to\n%s',
'eds': 'eds',
'editors': 'editors',
'editorListHint': 'List the users who have edited %s',
'related': 'related',
'relatedChanges': 'relatedChanges',
'related changes': 'related changes',
'RecentchangeslinkedHint': 'Show changes in articles related to %s',
'editOld': 'editOld', ///// edit old version, or revert
'rv': 'rv',
'revert': 'revert',
'revertHint': 'Revert to %s',
'defaultpopupReviewedSummary': 'Accepted by reviewing the [[Special:diff/%s/%s|difference]] between this version and previously accepted version using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRedlinkSummary': 'Removing link to empty page [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupFixDabsSummary': 'Disambiguate [[%s]] to [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupFixRedirsSummary': 'Redirect bypass from [[%s]] to [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupExtendedRevertSummary': 'Revert to revision dated %s by %s, oldid %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRevertToPreviousSummary': 'Revert to the revision prior to revision %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRevertSummary': 'Revert to revision %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupQueriedRevertToPreviousSummary': 'Revert to the revision prior to revision $1 dated $2 by $3 using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupQueriedRevertSummary': 'Revert to revision $1 dated $2 by $3 using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRmDabLinkSummary': 'Remove link to dab page [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'Redirects': 'Redirects', // as in Redirects to ...
' to ': ' to ', // as in Redirects to ...
'Bypass redirect': 'Bypass redirect',
'Fix this redirect': 'Fix this redirect',
'disambig': 'disambig', ///// add or remove dab etc.
'disambigHint': 'Disambiguate this link to [[%s]]',
'Click to disambiguate this link to:': 'Click to disambiguate this link to:',
'remove this link': 'remove this link',
'remove all links to this page from this article': 'remove all links to this page from this article',
'remove all links to this disambig page from this article': 'remove all links to this disambig page from this article',
'mainlink': 'mainlink', ///// links, watch, unwatch
'wikiLink': 'wikiLink',
'wikiLinks': 'wikiLinks',
'links here': 'links here',
'whatLinksHere': 'whatLinksHere',
'what links here': 'what links here',
'WhatlinkshereHint': 'List the pages that are hyperlinked to %s',
'unwatchShort': 'un',
'watchThingy': 'watch', // called watchThingy because {}.watch is a function
'watchHint': 'Add %s to my watchlist',
'unwatchHint': 'Remove %s from my watchlist',
'Only found one editor: %s made %s edits': 'Only found one editor: %s made %s edits',
'%s seems to be the last editor to the page %s': '%s seems to be the last editor to the page %s',
'rss': 'rss',
/////////////////////////////////////
// diff previews
/////////////////////////////////////
'Diff truncated for performance reasons': 'Diff truncated for performance reasons',
'Old revision': 'Old revision',
'New revision': 'New revision',
'Something went wrong :-(': 'Something went wrong :-(',
'Empty revision, maybe non-existent': 'Empty revision, maybe non-existent',
'Unknown date': 'Unknown date',
/////////////////////////////////////
// other special previews
/////////////////////////////////////
'Empty category': 'Empty category',
'Category members (%s shown)': 'Category members (%s shown)',
'No image links found': 'No image links found',
'File links': 'File links',
'No image found': 'No image found',
'Image from Commons': 'Image from Commons',
'Description page': 'Description page',
'Alt text:': 'Alt text:',
'revdel':'Hidden revision',
/////////////////////////////////////
// user-related actions and info
/////////////////////////////////////
'user': 'user', ///// user page, talk, email, space
'user page': 'user page',
'user talk': 'user talk',
'edit user talk': 'edit user talk',
'leave comment': 'leave comment',
'email': 'email',
'email user': 'email user',
'EmailuserHint': 'Send an email to %s',
'space': 'space', // short form for userSpace link
'PrefixIndexHint': 'Show pages in the userspace of %s',
'count': 'count', ///// contributions, log
'edit counter': 'edit counter',
'editCounterLinkHint': 'Count the contributions made by %s',
'contribs': 'contribs',
'contributions': 'contributions',
'deletedContribs': 'deleted contributions',
'DeletedcontributionsHint': 'List deleted edits made by %s',
'ContributionsHint': 'List the contributions made by %s',
'log': 'log',
'user log': 'user log',
'userLogHint': 'Show %s\'s user log',
'arin': 'ARIN lookup', ///// ARIN lookup, block user or IP
'Look up %s in ARIN whois database': 'Look up %s in the ARIN whois database',
'unblockShort': 'un',
'block': 'block',
'block user': 'block user',
'IpblocklistHint': 'Unblock %s',
'BlockipHint': 'Prevent %s from editing',
'block log': 'block log',
'blockLogHint': 'Show the block log for %s',
'protectLogHint': 'Show the protection log for %s',
'pageLogHint': 'Show the page log for %s',
'deleteLogHint': 'Show the deletion log for %s',
'Invalid %s %s': 'The option %s is invalid: %s',
'No backlinks found': 'No backlinks found',
' and more': ' and more',
'undo': 'undo',
'undoHint': 'undo this edit',
'Download preview data': 'Download preview data',
'Invalid or IP user': 'Invalid or IP user',
'Not a registered username': 'Not a registered username',
'BLOCKED': 'BLOCKED',
'Has blocks': 'Has blocks',
' edits since: ': ' edits since: ',
'last edit on ': 'last edit on ',
/////////////////////////////////////
// Autoediting
/////////////////////////////////////
'Enter a non-empty edit summary or press cancel to abort': 'Enter a non-empty edit summary or press cancel to abort',
'Failed to get revision information, please edit manually.\n\n': 'Failed to get revision information, please edit manually.\n\n',
'The %s button has been automatically clicked. Please wait for the next page to load.': 'The %s button has been automatically clicked. Please wait for the next page to load.',
'Could not find button %s. Please check the settings in your javascript file.': 'Could not find button %s. Please check the settings in your javascript file.',
/////////////////////////////////////
// Popups setup
/////////////////////////////////////
'Open full-size image': 'Open full-size image',
'zxy': 'zxy',
'autoedit_version': 'np20140416'
};
function popupString(str) {
if (typeof popupStrings != 'undefined' && popupStrings && popupStrings[str]) { return popupStrings[str]; }
if (pg.string[str]) { return pg.string[str]; }
return str;
}
function tprintf(str,subs) {
if (typeof subs != typeof []) { subs = [subs]; }
return simplePrintf(popupString(str), subs);
}
//</NOLITE>
// ENDFILE: strings.js
// STARTFILE: run.js
////////////////////////////////////////////////////////////////////
// Run things
////////////////////////////////////////////////////////////////////
// For some reason popups requires a fully loaded page jQuery.ready(...) causes problems for some.
// The old addOnloadHook did something similar to the below
if (document.readyState=="complete")
autoEdit(); //will setup popups
else
$( window ).on( 'load', autoEdit );
// Support for MediaWiki's live preview, VisualEditor's saves and Echo's flyout.
( function () {
var once = true;
function dynamicContentHandler( $content ) {
// Try to detect the hook fired on initial page load and disregard
// it, we already hook to onload (possibly to different parts of
// page - it's configurable) and running twice might be bad. Ugly…
if ( $content.attr( 'id' ) == 'mw-content-text' ) {
if ( once ) {
once = false;
return;
}
}
function registerHooksForVisibleNavpops () {
for (var i=0; pg.current.links && i<pg.current.links.length; ++i) {
var navpop = pg.current.links[i].navpopup;
if (!navpop || !navpop.isVisible()) { continue; }
Navpopup.tracker.addHook(posCheckerHook(navpop));
}
}
function doIt () {
registerHooksForVisibleNavpops();
$content.each( function () {
this.ranSetupTooltipsAlready = false;
setupTooltips( this );
} );
}
setupPopups( doIt );
}
// This hook is also fired after page load.
mw.hook( 'wikipage.content' ).add( dynamicContentHandler );
mw.hook( 'ext.echo.overlay.beforeShowingOverlay' ).add( function($overlay){
dynamicContentHandler( $overlay.find(".mw-echo-state") );
});
} )();
// Forked from [[User:Connel MacKenzie/mess-with-popups.js]]
Previewmaker.prototype.firstBit = function() {
try{
if (mw.config.get("wgNamespaceNumber") !== 0){
// dont't be givin' me no subsequent paragraphs, you hear me?
/// first we "normalize" section headings, removing whitespace after, adding before
var d = this.data;
if (getValueOf('popupPreviewCutHeadings')) {
this.data = this.data.replace(RegExp('\\s*(==+[^=]*==+)\\s*', 'g'), '\n\n$1 ');
/// then we want to get rid of paragraph breaks whose text ends badly
this.data = this.data.replace(RegExp('([:;]) *\\n{2,}', 'g'), '$1\n');
this.data = this.data.replace(RegExp('^[\\s\\n]*'), '');
stuff = (RegExp('^([^\\n]|\\n[^\\n\\s])*')).exec(this.data);
if (stuff) {
d = stuff[0];
}
if (!getValueOf('popupPreviewFirstParOnly')) {
d = this.data;
}
/// now put \n\n after sections so that bullets and numbered lists work
d = d.replace(RegExp('(==+[^=]*==+)\\s*', 'g'), '$1\n\n');
}
// superfluous sentences are RIGHT OUT.
// note: exactly 1 set of parens here needed to make the slice work
d = d.parenSplit(RegExp('([!?.]+["' + "'" + ']*\\s)', 'g'));
// leading space is bad, mmkay?
d[0] = d[0].replace(RegExp('^\\s*'), '');
var notSentenceEnds = RegExp('([^.][a-z][.][a-z]|etc|sic|Dr|Mr|Mrs|Ms|St|no|\\[[^\\]]*|\\s[A-Zvclm])$', 'i');
d = this.fixSentenceEnds(d, notSentenceEnds);
var maxChars=getValueOf('popupMaxPreviewCharacters');
var n = getValueOf('popupMaxPreviewSentences');
var dd;
do {
dd = this.firstSentences(d, n);
--n;
}
while (dd.length > maxChars && n > 0);
this.data = dd;
} else {
//TODO: Limit this override to urls that start with http://*.wiktionary.org/
//TODO: extend the above section to also preview Wikipedia links
var charsWikt = 0;
var linesWikt = this.data.split('\n');
for (jWikt = 0; jWikt < linesWikt.length; jWikt++) {
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{[in]transitive}}/g, '');
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{[un]countable}}/g, '');
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{cattag\|(.*?)}}/g, "");
linesWikt[jWikt] = linesWikt[jWikt].replace(/[\{\}]/g, "'");
if (linesWikt[jWikt].search(/^[#]/) == -1) {
if (linesWikt[jWikt].search(/^==[A-Z]/) == -1) {
linesWikt[jWikt] = '';
}
}
charsWikt += linesWikt[jWikt].length;
}
if (charsWikt < 2) {
linesWikt[0] = "No properly formatted content";
}
var maxChars = getValueOf('popupMaxPreviewCharacters');
var n = getValueOf('popupMaxPreviewSentences');
var dd = '';
for (jWikt = 0; jWikt < linesWikt.length; jWikt++) {
if (linesWikt[jWikt] !== '') {
dd += linesWikt[jWikt] + '\n';
}
if (dd.length < maxChars) {
this.data = dd;
}
}
}
}
catch (e) {
console.log("error from mess with popups: ", e);
}
};
});
orsd53nf3vg9wb097lspoxfv57tbbij
MediaWiki:Gadget-PagePreviews.js
8
17963
88928
2026-06-20T23:36:13Z
en>Ioaxxere
0
Prefix page-preview CSS class; update constants
88928
javascript
text/javascript
// Page Previews for Wiktionary, by [[User:Ioaxxere]]
// <nowiki>
// Generated using: [[MediaWiki:Gadget-PagePreviews.js/ConstGeneration]]
const INTERWIKI_PREFIXES = new Map([["aa","aa"],["ab","ab"],["abstract","abstract"],["acc",0],["ace","ace"],["acronym",0],["advisory",0],["ady","ady"],["af","af"],["ak","ak"],["als","als"],["alt","alt"],["am","am"],["ami","ami"],["an","an"],["ang","ang"],["ann","ann"],["anp","anp"],["antwiki",0],["appropedia",0],["aquariumwiki",0],["ar","ar"],["arbcom-zh",0],["arborwiki",0],["arc","arc"],["arxiv",0],["ary","ary"],["arz","arz"],["as","as"],["ast","ast"],["atj","atj"],["av","av"],["avk","avk"],["awa","awa"],["ay","ay"],["az","az"],["azb","azb"],["b",0],["ba","ba"],["ban","ban"],["bar","bar"],["bat-smg","bat-smg"],["battlestarwiki",0],["bbc","bbc"],["bcl","bcl"],["bcnbio",0],["bdr","bdr"],["be","be"],["be-tarask","be-tarask"],["be-x-old","be-tarask"],["beacha",0],["betawiki",0],["betawikiversity",0],["bew","bew"],["bg","bg"],["bh","bh"],["bi","bi"],["bibcode",0],["bibliowiki",0],["bjn","bjn"],["blk","blk"],["bm","bm"],["bn","bn"],["bo","bo"],["botwiki",0],["boxrec",0],["bpy","bpy"],["br","br"],["bs","bs"],["btm","btm"],["bug","bug"],["bugzilla",0],["bulba",0],["bxr","bxr"],["c",0],["c2",0],["ca","ca"],["cache",0],["cbk-zam","cbk-zam"],["ccorg",0],["cdo","cdo"],["ce","ce"],["ceb","ceb"],["centralwikia",0],["ch","ch"],["chapter",0],["cho","cho"],["choralwiki",0],["chr","chr"],["chy","chy"],["citizendium",0],["ckb","ckb"],["cmn","zh"],["co","co"],["commons",0],["communitywiki",0],["comune",0],["cr","cr"],["creativecommons",0],["creativecommonswiki",0],["crh","crh"],["cs","cs"],["csb","csb"],["cu","cu"],["cv","cv"],["cy","cy"],["cz","cs"],["d",0],["da","da"],["dag","dag"],["dbdump",0],["dcdatabase",0],["dcw",0],["de","de"],["debian",0],["devmo",0],["dga","dga"],["dico",0],["dicoado",0],["dict",0],["dictionary",0],["diffblog",0],["din","din"],["diq","diq"],["discord",0],["disinfopedia",0],["dk","da"],["dmoz",0],["dmozs",0],["doi",0],["donate",0],["doom_wiki",0],["download",0],["dpd",0],["dpla",0],["drae",0],["dsb","dsb"],["dtp","dtp"],["dty","dty"],["dv","dv"],["dz","dz"],["ee","ee"],["egl","eml"],["el","el"],["elibre",0],["emacswiki",0],["eml","eml"],["en","en"],["en-simple","simple"],["encyc",0],["englyphwiki",0],["enkol",0],["eo","eo"],["epo","eo"],["es","es"],["esolang",0],["et","et"],["etherpad",0],["etherpadbackup",0],["ethnologue",0],["ethnologuefamily",0],["eu","eu"],["exkcd",0],["exotica",0],["ext","ext"],["f",0],["fa","fa"],["fandom",0],["fanimutationwiki",0],["fat","fat"],["fedora",0],["ff","ff"],["fi","fi"],["finalfantasy",0],["finnix",0],["fiu-vro","fiu-vro"],["fj","fj"],["flickrphoto",0],["flickruser",0],["fo","fo"],["foldoc",0],["fon","fon"],["foundation",0],["foundationsite",0],["fr","fr"],["freebsdman",0],["freedomdefined",0],["freenode",0],["freesoft",0],["frp","frp"],["frr","frr"],["fur","fur"],["fy","fy"],["ga","ga"],["gag","gag"],["gan","gan"],["gcr","gcr"],["gd","gd"],["gentoo",0],["genwiki",0],["gerrit",0],["git",0],["github",0],["gitiles",0],["gitlab",0],["gl","gl"],["glk","glk"],["globalcontribs",0],["glottolog",0],["glottopedia",0],["gn","gn"],["gom","gom"],["google",0],["googledefine",0],["googlegroups",0],["gor","gor"],["got","got"],["gpe","gpe"],["gs",0],["gsw","als"],["gu","gu"],["guc","guc"],["gucprefix",0],["guildwarswiki",0],["gur","gur"],["gutenberg",0],["gutenbergwiki",0],["guw","guw"],["gv","gv"],["ha","ha"],["hackerspaces",0],["hak","hak"],["hammondwiki",0],["haw","haw"],["hdl",0],["he","he"],["heraldik",0],["hi","hi"],["hif","hif"],["ho","ho"],["horizonlabs",0],["hr","hr"],["hrfwiki",0],["hrwiki",0],["hsb","hsb"],["ht","ht"],["hu","hu"],["hy","hy"],["hyw","hyw"],["hz","hz"],["ia","ia"],["iarchive",0],["iba","iba"],["id","id"],["ie","ie"],["ig","ig"],["igl","igl"],["ii","ii"],["ik","ik"],["ilo","ilo"],["imdbcompany",0],["imdbname",0],["imdbtitle",0],["incubator",0],["infosphere",0],["inh","inh"],["io","io"],["irc",0],["ircrc",0],["ircs",0],["is","is"],["isni",0],["iso639-3",0],["issn",0],["it","it"],["iu","iu"],["ja","ja"],["jaglyphwiki",0],["jam","jam"],["jbo","jbo"],["jira",0],["jp","ja"],["jstor",0],["jv","jv"],["ka","ka"],["kaa","kaa"],["kab","kab"],["kai","kai"],["kaj","kaj"],["kamelo",0],["karlsruhe",0],["kbd","kbd"],["kbp","kbp"],["kcg","kcg"],["kg","kg"],["kge","kge"],["ki","ki"],["kj","kj"],["kk","kk"],["kl","kl"],["km","km"],["kn","kn"],["knc","knc"],["ko","ko"],["koi","koi"],["kr","kr"],["krc","krc"],["ks","ks"],["ksh","ksh"],["ku","ku"],["kus","kus"],["kv","kv"],["kw","kw"],["ky","ky"],["la","la"],["labsconsole",0],["lad","lad"],["lb","lb"],["lbe","lbe"],["lexemes",0],["lez","lez"],["lfn","lfn"],["lg","lg"],["li","li"],["liberachat",0],["libreplanet",0],["lij","lij"],["lingualibre",0],["linguistlist",0],["listarchive",0],["lld","lld"],["lmo","lmo"],["ln","ln"],["lo","lo"],["localwiki",0],["lofc",0],["lojban",0],["lokalhistoriewiki",0],["lostpedia",0],["lrc","lrc"],["lt","lt"],["ltg","ltg"],["luxo",0],["lv","lv"],["lzh","zh-classical"],["m",0],["mad","mad"],["mag","mag"],["mai","mai"],["mail",0],["mailarchive",0],["map-bms","map-bms"],["mariowiki",0],["marveldatabase",0],["mdf","mdf"],["mdwiki",0],["meatball",0],["mediawikiwiki",0],["mediazilla",0],["memoryalpha",0],["meta",0],["metawiki",0],["metawikimedia",0],["metawikipedia",0],["mg","mg"],["mh","mh"],["mhr","mhr"],["mi","mi"],["min","min"],["mineralienatlas",0],["miraheze",0],["mixnmatch",0],["mk","mk"],["ml","ml"],["mn","mn"],["mni","mni"],["mnw","mnw"],["mo","mo"],["moinmoin",0],["mos","mos"],["mosapedia",0],["mozillawiki",0],["mozillazinekb",0],["mr","mr"],["mrj","mrj"],["ms","ms"],["mt","mt"],["mus","mus"],["mw",0],["mwl","mwl"],["mwod",0],["mwot",0],["my","my"],["myv","myv"],["mzn","mzn"],["n",0],["na","na"],["nah","nah"],["nan","zh-min-nan"],["nap","nap"],["nara",0],["nb","no"],["nds","nds"],["nds-nl","nds-nl"],["ne","ne"],["new","new"],["ng","ng"],["nia","nia"],["nl","nl"],["nlab",0],["nn","nn"],["no","no"],["nost","nostalgia"],["nostalgia","nostalgia"],["nov","nov"],["nqo","nqo"],["nr","nr"],["nrm","nrm"],["nso","nso"],["nup","nup"],["nv","nv"],["ny","ny"],["oc","oc"],["oclc",0],["oeis",0],["oewiki",0],["oldwikisource",0],["olo","olo"],["olpc",0],["om","om"],["openlibrary",0],["openstreetmap",0],["openwetware",0],["or","or"],["organicdesign",0],["orthodoxwiki",0],["os","os"],["osmwiki",0],["otrs",0],["otrswiki",0],["outreach",0],["outreachwiki",0],["owasp",0],["pa","pa"],["pag","pag"],["pam","pam"],["pap","pap"],["paws",0],["pcd","pcd"],["pcm","pcm"],["pdc","pdc"],["petscan",0],["pfl","pfl"],["phab",0],["phabricator",0],["pi","pi"],["pih","pih"],["pl","pl"],["planetmath",0],["pmid",0],["pms","pms"],["pnb","pnb"],["pnt","pnt"],["pokewiki",0],["pokéwiki",0],["policy",0],["ppl","ppl"],["proofwiki",0],["ps","ps"],["pt","pt"],["pwn","pwn"],["pyrev",0],["pythoninfo",0],["q",0],["qu","qu"],["quality",0],["quarry",0],["rcirc",0],["regiowiki",0],["rev",0],["revo",0],["rfc",0],["rheinneckar",0],["rki","rki"],["rm","rm"],["rmy","rmy"],["rn","rn"],["ro","ro"],["roa-rup","roa-rup"],["roa-tara","roa-tara"],["rodovid",0],["rsk","rsk"],["rt",0],["ru","ru"],["rue","rue"],["rup","roa-rup"],["rw","rw"],["s",0],["sa","sa"],["sah","sah"],["sat","sat"],["sc","sc"],["scholar",0],["schoolswp",0],["schoolwiki",0],["scn","scn"],["sco","sco"],["scores",0],["scoutwiki",0],["sd","sd"],["se","se"],["securewikidc",0],["semantic-mw",0],["senseislibrary",0],["sep11",0],["sg","sg"],["sgs","bat-smg"],["sh","sh"],["sharemap",0],["shi","shi"],["shn","shn"],["shoutwiki",0],["shy","shy"],["si","si"],["silcode",0],["simple","simple"],["sk","sk"],["skr","skr"],["sl","sl"],["slashdot",0],["sm","sm"],["smn","smn"],["sn","sn"],["so","so"],["sourceforge",0],["spamcheck",0],["spcom",0],["species",0],["sq","sq"],["sr","sr"],["srn","srn"],["ss","ss"],["st","st"],["stats",0],["stewardry",0],["stq","stq"],["strategy",0],["strategywiki",0],["su","su"],["sulutil",0],["sv","sv"],["svn",0],["sw","sw"],["swtrain",0],["syl","syl"],["szl","szl"],["szy","szy"],["ta","ta"],["tardis",0],["tay","tay"],["tclerswiki",0],["tcy","tcy"],["tdd","tdd"],["te","te"],["tenwiki","ten"],["test2wiki","test2"],["testcommons",0],["testwiki","test"],["testwikidata",0],["tet","tet"],["tfwiki",0],["tg","tg"],["th","th"],["thelemapedia",0],["theopedia",0],["ti","ti"],["ticket",0],["tig","tig"],["tk","tk"],["tl","tl"],["tly","tly"],["tmbw",0],["tn","tn"],["to","to"],["tok","tok"],["tolkiengateway",0],["toolforge",0],["toolhub",0],["toollabs",0],["tools",0],["tpi","tpi"],["tr","tr"],["translatewiki",0],["trv","trv"],["ts","ts"],["tswiki",0],["tt","tt"],["tum","tum"],["tviv",0],["tw","tw"],["twiki",0],["twl",0],["ty","ty"],["tyv","tyv"],["tyvawiki",0],["udm","udm"],["ug","ug"],["uk","uk"],["umap",0],["uncyclopedia",0],["unihan",0],["ur","ur"],["urbandict",0],["usability",0],["usemod",0],["utrs",0],["uz","uz"],["v",0],["ve","ve"],["vec","vec"],["vep","vep"],["vi","vi"],["viaf",0],["vikidia",0],["vlos",0],["vls","vls"],["vo","vo"],["votewiki",0],["voy",0],["vro","fiu-vro"],["vrts",0],["vrtwiki",0],["w","en"],["wa","wa"],["war","war"],["wcna",0],["weirdgloop",0],["werelate",0],["wg","wg-en"],["wikia",0],["wikiapiary",0],["wikiasite",0],["wikibooks",0],["wikicities",0],["wikicity",0],["wikiconference",0],["wikidata",0],["wikiedudashboard",0],["wikifunctions",0],["wikifur",0],["wikihow",0],["wikiindex",0],["wikilivres",0],["wikilivresru",0],["wikimania",0],["wikimedia",0],["wikinews",0],["wikinfo",0],["wikinvest",0],["wikipapers",0],["wikipedia",0],["wikipediawikipedia",0],["wikiquote",0],["wikiskripta",0],["wikisophia",0],["wikisource",0],["wikisp",0],["wikispecies",0],["wikispore",0],["wikispot",0],["wikitech",0],["wikiti",0],["wikitrek",0],["wikiversity",0],["wikivoyage",0],["wikiwikiweb",0],["wikt",0],["wiktionary",0],["wm2005",0],["wm2006",0],["wm2007",0],["wm2008",0],["wm2009",0],["wm2010",0],["wm2011",0],["wm2012",0],["wm2013",0],["wm2014",0],["wm2015",0],["wm2016",0],["wm2017",0],["wm2018",0],["wmam",0],["wmania",0],["wmar",0],["wmat",0],["wmau",0],["wmbd",0],["wmbe",0],["wmbr",0],["wmca",0],["wmch",0],["wmcl",0],["wmcn",0],["wmco",0],["wmcz",0],["wmcz_docs",0],["wmcz_old",0],["wmdc",0],["wmde",0],["wmdeblog",0],["wmdk",0],["wmdoc",0],["wmec",0],["wmee",0],["wmes",0],["wmet",0],["wmf",0],["wmfblog",0],["wmfdashboard",0],["wmfi",0],["wmfr",0],["wmge",0],["wmhi",0],["wmhk",0],["wmhu",0],["wmid",0],["wmil",0],["wmin",0],["wmit",0],["wmke",0],["wmmk",0],["wmmx",0],["wmnl",0],["wmno",0],["wmnoc",0],["wmnoch",0],["wmnyc",0],["wmpa-us",0],["wmph",0],["wmpl",0],["wmplsite",0],["wmpt",0],["wmpunjabi",0],["wmromd",0],["wmrs",0],["wmru",0],["wmse",0],["wmsk",0],["wmteam",0],["wmtr",0],["wmtw",0],["wmua",0],["wmuk",0],["wmve",0],["wmza",0],["wo","wo"],["wookieepedia",0],["wowwiki",0],["wplibrary",0],["wurmpedia",0],["wuu","wuu"],["xal","xal"],["xh","xh"],["xkcd",0],["xmf","xmf"],["xtools",0],["yi","yi"],["yo","yo"],["yue","zh-yue"],["za","za"],["zea","zea"],["zgh","zgh"],["zh","zh"],["zh-classical","zh-classical"],["zh-cn","zh"],["zh-min-nan","zh-min-nan"],["zh-tw","zh"],["zh-yue","zh-yue"],["zu","zu"],["zum",0]]);
const WP_LANGUAGES = new Map([["ab","Abkhazian"],["ace","Acehnese"],["ady","Adyghe"],["af","Afrikaans"],["als","Alemannic"],["alt","Southern Altai"],["am","Amharic"],["ami","Amis"],["an","Aragonese"],["ang","Old English"],["ann","Obolo"],["anp","Angika"],["ar","Arabic"],["arc","Aramaic"],["ary","Moroccan Arabic"],["arz","Egyptian Arabic"],["as","Assamese"],["ast","Asturian"],["atj","Atikamekw"],["av","Avaric"],["avk","Kotava"],["awa","Awadhi"],["ay","Aymara"],["az","Azerbaijani"],["azb","South Azerbaijani"],["ba","Bashkir"],["ban","Balinese"],["bar","Bavarian"],["bat-smg","Samogitian"],["bbc","Batak Toba"],["bcl","Central Bikol"],["bdr","West Coast Bajau"],["be","Belarusian"],["be-tarask","Belarusian (Taraškievica orthography)"],["bew","Betawi"],["bg","Bulgarian"],["bh","Bhojpuri"],["bi","Bislama"],["bjn","Banjar"],["blk","Pa'O"],["bm","Bambara"],["bn","Bangla"],["bo","Tibetan"],["bpy","Bishnupriya"],["br","Breton"],["bs","Bosnian"],["btm","Batak Mandailing"],["bug","Buginese"],["bxr","Russia Buriat"],["ca","Catalan"],["cbk-zam","Chavacano"],["cdo","Mindong"],["ce","Chechen"],["ceb","Cebuano"],["ch","Chamorro"],["chr","Cherokee"],["chy","Cheyenne"],["ckb","Central Kurdish"],["co","Corsican"],["crh","Crimean Tatar"],["cs","Czech"],["csb","Kashubian"],["cu","Church Slavic"],["cv","Chuvash"],["cy","Welsh"],["da","Danish"],["dag","Dagbani"],["de","German"],["dga","Southern Dagaare"],["din","Dinka"],["diq","Dimli"],["dsb","Lower Sorbian"],["dtp","Central Dusun"],["dty","Doteli"],["dv","Divehi"],["dz","Dzongkha"],["ee","Ewe"],["el","Greek"],["eml","Emiliano-Romagnolo"],["en","English"],["eo","Esperanto"],["es","Spanish"],["et","Estonian"],["eu","Basque"],["ext","Extremaduran"],["fa","Persian"],["fat","Fanti"],["ff","Fula"],["fi","Finnish"],["fiu-vro","Võro"],["fj","Fijian"],["fo","Faroese"],["fon","Fon"],["fr","French"],["frp","Arpitan"],["frr","Northern Frisian"],["fur","Friulian"],["fy","Western Frisian"],["ga","Irish"],["gag","Gagauz"],["gan","Gan"],["gcr","Guianan Creole"],["gd","Scottish Gaelic"],["gl","Galician"],["glk","Gilaki"],["gn","Guarani"],["gom","Goan Konkani"],["gor","Gorontalo"],["got","Gothic"],["gpe","Ghanaian Pidgin"],["gu","Gujarati"],["guc","Wayuu"],["gur","Frafra"],["guw","Gun"],["gv","Manx"],["ha","Hausa"],["hak","Hakka Chinese"],["haw","Hawaiian"],["he","Hebrew"],["hi","Hindi"],["hif","Fiji Hindi"],["hr","Croatian"],["hsb","Upper Sorbian"],["ht","Haitian Creole"],["hu","Hungarian"],["hy","Armenian"],["hyw","Western Armenian"],["ia","Interlingua"],["iba","Iban"],["id","Indonesian"],["ie","Interlingue"],["ig","Igbo"],["igl","Igala"],["ik","Inupiaq"],["ilo","Iloko"],["inh","Ingush"],["io","Ido"],["is","Icelandic"],["it","Italian"],["iu","Inuktitut"],["ja","Japanese"],["jam","Jamaican Creole English"],["jbo","Lojban"],["jv","Javanese"],["ka","Georgian"],["kaa","Kara-Kalpak"],["kab","Kabyle"],["kai","Karekare"],["kaj","Jju"],["kbd","Kabardian"],["kbp","Kabiye"],["kcg","Tyap"],["kg","Kongo"],["kge","Komering"],["ki","Kikuyu"],["kk","Kazakh"],["km","Khmer"],["kn","Kannada"],["knc","Central Kanuri"],["ko","Korean"],["koi","Komi-Permyak"],["krc","Karachay-Balkar"],["ks","Kashmiri"],["ksh","Colognian"],["ku","Kurdish"],["kus","Kusaal"],["kv","Komi"],["kw","Cornish"],["ky","Kyrgyz"],["la","Latin"],["lad","Ladino"],["lb","Luxembourgish"],["lbe","Lak"],["lez","Lezghian"],["lfn","Lingua Franca Nova"],["lg","Ganda"],["li","Limburgish"],["lij","Ligurian"],["lld","Ladin"],["lmo","Lombard"],["ln","Lingala"],["lo","Lao"],["lt","Lithuanian"],["ltg","Latgalian"],["lv","Latvian"],["mad","Madurese"],["mag","Magahi"],["mai","Maithili"],["map-bms","Banyumasan"],["mdf","Moksha"],["mg","Malagasy"],["mhr","Eastern Mari"],["mi","Māori"],["min","Minangkabau"],["mk","Macedonian"],["ml","Malayalam"],["mn","Mongolian"],["mni","Manipuri"],["mnw","Mon"],["mos","Mossi"],["mr","Marathi"],["mrj","Western Mari"],["ms","Malay"],["mt","Maltese"],["mwl","Mirandese"],["my","Burmese"],["myv","Erzya"],["mzn","Mazanderani"],["nah","Nahuatl"],["nap","Neapolitan"],["nds","Low German"],["nds-nl","Low Saxon"],["ne","Nepali"],["new","Newari"],["nia","Nias"],["nl","Dutch"],["nn","Norwegian Nynorsk"],["no","Norwegian"],["nov","Novial"],["nqo","N’Ko"],["nr","South Ndebele"],["nrm","Norman"],["nso","Northern Sotho"],["nup","Nupe"],["nv","Navajo"],["ny","Nyanja"],["oc","Occitan"],["olo","Livvi-Karelian"],["om","Oromo"],["or","Odia"],["os","Ossetic"],["pa","Punjabi"],["pag","Pangasinan"],["pam","Pampanga"],["pap","Papiamento"],["pcd","Picard"],["pcm","Nigerian Pidgin"],["pdc","Pennsylvania German"],["pfl","Palatine German"],["pi","Pali"],["pl","Polish"],["pms","Piedmontese"],["pnb","Western Punjabi"],["pnt","Pontic"],["ppl","Nawat"],["ps","Pashto"],["pt","Portuguese"],["pwn","Paiwan"],["qu","Quechua"],["rki","Arakanese"],["rm","Romansh"],["rmy","Vlax Romani"],["rn","Rundi"],["ro","Romanian"],["roa-rup","Aromanian"],["roa-tara","Tarantino"],["rsk","Pannonian Rusyn"],["ru","Russian"],["rue","Rusyn"],["rw","Kinyarwanda"],["sa","Sanskrit"],["sah","Yakut"],["sat","Santali"],["sc","Sardinian"],["scn","Sicilian"],["sco","Scots"],["sd","Sindhi"],["se","Northern Sami"],["sg","Sango"],["sh","Serbo-Croatian"],["shi","Tachelhit"],["shn","Shan"],["si","Sinhala"],["simple","Simple English"],["sk","Slovak"],["skr","Saraiki"],["sl","Slovenian"],["sm","Samoan"],["smn","Inari Sami"],["sn","Shona"],["so","Somali"],["sq","Albanian"],["sr","Serbian"],["srn","Sranan Tongo"],["ss","Swati"],["st","Southern Sotho"],["stq","Saterland Frisian"],["su","Sundanese"],["sv","Swedish"],["sw","Swahili"],["syl","Sylheti"],["szl","Silesian"],["szy","Sakizaya"],["ta","Tamil"],["tay","Atayal"],["tcy","Tulu"],["tdd","Tai Nuea"],["te","Telugu"],["tet","Tetum"],["tg","Tajik"],["th","Thai"],["ti","Tigrinya"],["tig","Tigre"],["tk","Turkmen"],["tl","Tagalog"],["tly","Talysh"],["tn","Tswana"],["to","Tongan"],["tok","Toki Pona"],["tpi","Tok Pisin"],["tr","Turkish"],["trv","Taroko"],["ts","Tsonga"],["tt","Tatar"],["tum","Tumbuka"],["tw","Twi"],["ty","Tahitian"],["tyv","Tuvinian"],["udm","Udmurt"],["ug","Uyghur"],["uk","Ukrainian"],["ur","Urdu"],["uz","Uzbek"],["ve","Venda"],["vec","Venetian"],["vep","Veps"],["vi","Vietnamese"],["vls","West Flemish"],["vo","Volapük"],["wa","Walloon"],["war","Waray"],["wo","Wolof"],["wuu","Wu"],["xal","Kalmyk"],["xh","Xhosa"],["xmf","Mingrelian"],["yi","Yiddish"],["yo","Yoruba"],["za","Zhuang"],["zea","Zeelandic"],["zgh","Standard Moroccan Tamazight"],["zh","Chinese"],["zh-classical","Literary Chinese"],["zh-min-nan","Minnan"],["zh-yue","Cantonese"],["zu","Zulu"]]);
const appendixWithSlash = ["Adûni", "Afrihili", "Arabic_roots", "Belter_Creole", "Bibliography", "Black_Speech", "Bolak", "Communicationssprache", "Dothraki", "Eloi", "Gestures", "Glosa", "Goa'uld", "High_Valyrian", "Idiom_Neutral", "Interlingue", "Interslavic", "Klingon", "Kotava", "Láadan", "Lapine", "Lingua_Franca_Nova", "Lojban", "Mandalorian", "Medefaidrin", "Minecraft", "Mundolinco", "Na'vi", "Neo", "Novial", "Noxilo", "Protologisms/Long_words", "Quenya", "Romanova", "Sindarin", "Snowclones", "Talossan", "Toki_Pona", "Unas"];
const appendixNoSlash = ["Boldface", "Capital_letter", "Italics", "Lowercase_letter", "Possessive", "Repetition", "Small_caps", "Strikethrough", "Subscript", "Superscript", "Underline", "Uppercase_letter"];
const animationSpeed = 0.2; // seconds
mw.util.addCSS(`
.wikt-page-preview ol {
margin: 0 0.5em 0 1.5em;
padding: 0;
}
.wikt-page-preview dl {
margin-bottom: 0;
}
.wikt-page-preview p {
margin: 0;
}
/* popupContainer has the opacity animation, while popup gets translated. */
.popup-fade-in-up, .popup-fade-in-down {
animation: popup-fade-in ${animationSpeed}s ease forwards;
}
.popup-fade-out-up, .popup-fade-out-down {
animation: popup-fade-out ${animationSpeed}s ease forwards;
}
.popup-fade-in-up > div {
animation: popup-move-in-up ${animationSpeed}s ease forwards;
}
.popup-fade-in-down > div {
animation: popup-move-in-down ${animationSpeed}s ease forwards;
}
.popup-fade-out-up > div {
animation: popup-move-out-up ${animationSpeed}s ease forwards;
}
.popup-fade-out-down > div {
animation: popup-move-out-down ${animationSpeed}s ease forwards;
}
@keyframes popup-move-in-up {
0% {
transform: translate(0, 20px);
}
}
@keyframes popup-move-in-down {
0% {
transform: translate(0, -20px);
}
}
@keyframes popup-move-out-up {
100% {
transform: translate(0, -20px);
}
}
@keyframes popup-move-out-down {
100% {
transform: translate(0, 20px);
}
}
@keyframes popup-fade-out {
100% {
opacity: 0;
}
}
@keyframes popup-fade-in {
0% {
opacity: 0;
}
}
.ring-loader {
margin: auto;
width: 24px;
height: 24px;
border-radius: 50%;
border-top: 5px solid var(--wikt-palette-black);
border-bottom: 5px solid var(--wikt-palette-black);
border-left: 5px solid transparent;
border-right: 5px solid transparent;
animation: spin 1.2s linear infinite;
}
@keyframes spin {
100% {
transform: rotate(360deg);
}
}
.preview-headerlink, .preview-headerlink:visited {
color: inherit;
font-weight: bold;
}
/* Hack: get the speaker icon on Wikipedia articles without having to load Phonos. */
.ext-phonos .oo-ui-buttonElement-button:after {
content: "🔊";
}
.ext-phonos * {
display: inline !important;
padding: 0 !important;
margin: 0 !important;
}
`);
const definitionIcon = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" fill="currentColor"><g><path d="M15 2a7.65 7.65 0 00-5 2 7.65 7.65 0 00-5-2H1v15h4a7.65 7.65 0 015 2 7.65 7.65 0 015-2h4V2zm2.5 13.5H14a4.38 4.38 0 00-3 1V5s1-1.5 4-1.5h2.5z"></path></g></svg>`;
const glossaryIcon = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" fill="currentColor"><g><path d="M12.43 14.34A5 5 0 0110 15a5 5 0 113.95-2L17 16.09V3a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h10a2 2 0 001.45-.63z"></path><circle cx="10" cy="10" r="3"></circle></g></svg>`;
const articleIcon = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" fill="currentColor"><g><path d="M5 1a2 2 0 00-2 2v14a2 2 0 002 2h10a2 2 0 002-2V3a2 2 0 00-2-2zm0 3h5v1H5zm0 2h5v1H5zm0 2h5v1H5zm10 7H5v-1h10zm0-2H5v-1h10zm0-2H5v-1h10zm0-2h-4V4h4z"></path></g></svg>`;
const bibliographyIcon = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" fill="currentColor"><g><path d="M3 2h12c.5 0 1 .5 1 1v1c0 .5-.5 1-1 1H3c0 0 .5-.5.5-1V3c0-.5-.5-1-.5-1zM4 6h12c.5 0 1 .5 1 1v1c0 .5-.5 1-1 1H4c0 0 .5-.5.5-1V7c0-.5-.5-1-.5-1zM3 10h12c.5 0 1 .5 1 1v1c0 .5-.5 1-1 1H3c0 0 .5-.5.5-1v-1c0-.5-.5-1-.5-1zM4 14h12c.5 0 1 .5 1 1v1c0 .5-.5 1-1 1H4c0 0 .5-.5.5-1v-1c0-.5-.5-1-.5-1z"/></g></svg>`;
const loader = document.createElement("div");
loader.className = "ring-loader";
const popupContainer = document.createElement("div");
const popup = document.createElement("div");
const popupContent = document.createElement("div");
popupContainer.style = `display: none; position: absolute; filter: drop-shadow(0px 30px 30px rgba(0, 0, 0, 0.15)) drop-shadow(0px 0px 0.75px var(--wikt-palette-dullblue)); z-index: 801`;
popupContainer.className = "wikt-page-preview";
popup.style = "box-sizing: border-box; height: 100%; background: var(--wikt-palette-white)";
popupContent.style = "display: flex; flex-direction: column; border-radius: 2px; box-sizing: border-box; color: var(--wikt-palette-black); overflow: auto; height: 100%; overflow-wrap: break-word; scrollbar-width: thin";
document.body.append(popupContainer);
popupContainer.append(popup);
popup.append(popupContent);
let popupTimer;
let openLink;
let mouseX, mouseY;
let API_controller = new AbortController();
function closePopup() {
if (!openLink) return;
openLink = null;
API_controller.abort();
API_controller = new AbortController();
if (popupContainer.classList.contains("popup-fade-in-up")) {
popupContainer.classList.remove("popup-fade-in-up");
popupContainer.classList.add("popup-fade-out-down");
} else {
popupContainer.classList.remove("popup-fade-in-down");
popupContainer.classList.add("popup-fade-out-up");
}
// After the animation has completed, reset the popup to its initial state.
setTimeout(() => {
popupContainer.style.display = "none";
popupContainer.classList.remove("popup-fade-out-down", "popup-fade-out-up");
}, animationSpeed * 1000);
}
popup.addEventListener("pointerenter", () => {
clearTimeout(popupTimer);
});
popup.addEventListener("pointerleave", () => {
clearTimeout(popupTimer);
popupTimer = setTimeout(closePopup, 300);
});
function processLink(link) {
let linkTitle = decodeURIComponent(link.pathname.split("/wiki/")[1]);
let titlePrefix = linkTitle.includes(":") ? linkTitle.toLowerCase().split(":")[0] : "";
let resolvedTitle = linkTitle;
let API_domain = link.origin;
let linkAnchor = decodeURIComponent(link.hash.slice(1) || "");
let isWPlink = link.href.match(/^https:\/\/[^.]+.wikipedia.org\/wiki\//);
let isPreviewLink = Boolean(link.closest(".wikt-page-preview"));
let isGlossary = /^Appendix:.*[Gg]lossary$/.test(linkTitle);
// Start with various checks to determine whether a link should be processed.
if (link.matches(`nav a, .cancelLink a, .mw-widget-titleOptionWidget a, .new, .preview-headerlink, .external, [href$="#"]`)) {
return;
} else if (isGlossary) {
// Links to glossary *must* have an anchor.
if (!linkAnchor) return;
} else if (linkTitle.startsWith("Appendix:Bibliography/")) {
// Bibliography subpages must have an anchor starting with `bib-`.
if (!linkAnchor || !linkAnchor.startsWith("bib-")) return;
} else if (!isWPlink) {
// Other Wiktionary pages must not have a lowercase-starting anchor (filters out some unhelpful links).
if (/^[a-z]/.test(linkAnchor)) return;
}
if (link.href.startsWith("https://en.wiktionary.org/wiki/")) {
// Filter out all interwiki prefixes.
if (INTERWIKI_PREFIXES.has(titlePrefix))
return;
let {namespace, title} = new mw.Title(linkTitle);
if (namespace === 0) { // Mainspace
if (title.endsWith("/translations"))
return;
} else if (namespace === 100) { // Appendix space
if (!appendixWithSlash.some(lang => title.startsWith(lang + "/")) && !appendixNoSlash.includes(title) && !isGlossary)
return;
} else if (namespace !== 118) { // Reconstruction space
return;
}
} else if (isWPlink) {
// Filter out invalid interwiki prefixes.
if (INTERWIKI_PREFIXES.get(titlePrefix) === 0)
return;
// Get the resolved title if it's a language interwiki.
if (INTERWIKI_PREFIXES.has(titlePrefix)) {
API_domain = `https://${INTERWIKI_PREFIXES.get(titlePrefix)}.wikipedia.org`;
resolvedTitle = linkTitle.substring(linkTitle.indexOf(":") + 1);
// If the resolved title contains another interwiki, return.
let resolvedTitlePrefix = resolvedTitle.includes(":") ? resolvedTitle.toLowerCase().split(":")[0] : ""; // same as titlePrefix
if (INTERWIKI_PREFIXES.has(resolvedTitlePrefix))
return;
}
} else {
return;
}
link.addEventListener("pointerover", event => {
[mouseX, mouseY] = [event.clientX, event.clientY];
clearTimeout(popupTimer);
// Ignore links which are already open.
if (link === openLink)
return;
if (!isPreviewLink)
closePopup();
// Fetch popup text immediately on hover to reduce delay.
let responsePromise = fetch(`${API_domain}/api/rest_v1/page/html/${encodeURIComponent(resolvedTitle)}`, {
headers: {"Api-User-Agent": "Gadget developed by [[User:Ioaxxere]]"},
signal: API_controller.signal
}).then(r => r.text()).catch(() => { /* fetch was aborted */ });
popupTimer = setTimeout(() => {
popupContainer.style.display = "";
popupContent.innerHTML = "";
popupContent.append(loader);
popupContent.style.padding = "14px 16px 8px";
// Disable link hovering until animation has completed.
popupContent.style.pointerEvents = "none";
setTimeout(() => popupContent.style.pointerEvents = "", animationSpeed * 1000);
// Adapt the popup for large, standard, and small size preference in Vector 2022.
let sizeSetting = 0;
if (document.documentElement.matches(".vector-feature-custom-font-size-clientpref-1"))
sizeSetting = 1;
else if (document.documentElement.matches(".vector-feature-custom-font-size-clientpref-2"))
sizeSetting = 2;
let width = [320, 340, 360][sizeSetting];
let height = [210, 225, 240][sizeSetting];
let triangleSize = [9, 10, 11][sizeSetting];
popupContainer.style.fontSize = ["14px", "15px", "17px"][sizeSetting];
popupContainer.style.lineHeight = ["21px", "22px", "24px"][sizeSetting];
popupContainer.style.width = width + "px";
popupContainer.style.height = height + "px";
if (isPreviewLink) {
// Trigger fade-in-down animation.
popupContainer.classList.remove("popup-fade-in-up", "popup-fade-in-down");
void popupContainer.offsetLeft; // force reflow
popupContainer.classList.add("popup-fade-in-down");
} else {
openLink = link;
// Get list of rects (lines) of the target element, then find the one whose midpoint is closest to mouseY.
// This ensures that the code can correctly handle multi-line links.
let linkBlock = [...event.target.getClientRects()].reduce((prev, next) => Math.abs((next.top + next.bottom) / 2 - mouseY) < Math.abs((prev.top + prev.bottom) / 2 - mouseY) ? next : prev);
// Horizontal position. Choose left or right depending on the side of the screen the mouse is in.
let leftPosition = mouseX - 30;
if (mouseX > document.documentElement.clientWidth / 2)
leftPosition = mouseX - width + 30;
// Ensure that the popup is at least 5px away from the side of the screen.
leftPosition = Math.max(5, Math.min(leftPosition, document.documentElement.clientWidth - width - 5));
popupContainer.style.left = leftPosition + document.documentElement.scrollLeft + "px";
// Vertical position. Prioritize fade-in-down unless there would be less than 10px of room above.
if (linkBlock.top > height + 10 || linkBlock.top > document.documentElement.clientHeight - linkBlock.bottom) {
popupContainer.style.top = linkBlock.top + document.documentElement.scrollTop - height + "px";
popupContainer.classList.add("popup-fade-in-down");
popup.style.padding = `0 0 ${triangleSize}px`;
// Create a triangle on the bottom.
popup.style.clipPath = `polygon(0 0, 100% 0, 100% calc(100% - ${triangleSize}px), ${mouseX - leftPosition + triangleSize}px calc(100% - ${triangleSize}px), ${mouseX - leftPosition}px 100%, ${mouseX - leftPosition - triangleSize}px calc(100% - ${triangleSize}px), 0 calc(100% - ${triangleSize}px))`;
} else {
popupContainer.style.top = linkBlock.bottom + document.documentElement.scrollTop + "px";
popupContainer.classList.add("popup-fade-in-up");
popup.style.padding = `${triangleSize}px 0 0`;
// Create a triangle on top.
popup.style.clipPath = `polygon(0 ${triangleSize}px, ${mouseX - leftPosition - triangleSize}px ${triangleSize}px, ${mouseX - leftPosition}px 0, ${mouseX - leftPosition + triangleSize}px ${triangleSize}px, 100% ${triangleSize}px, 100% 100%, 0 100%)`;
}
}
responsePromise.then(response => {
if (!response) return; // if the fetch was aborted
popupContent.innerHTML = "";
let responseDocument = new DOMParser().parseFromString(response, "text/html");
// Convert to absolute URLs.
responseDocument.querySelectorAll("a[href]").forEach(link => link.setAttribute("href", link.href));
let anchoredElement = responseDocument.getElementById(linkAnchor);
let popupHeader = document.createElement("div");
popupHeader.style = "font-size: 90%; color: var(--wikt-palette-deepblue)";
popupContent.append(popupHeader);
let iconContainer = document.createElement("span");
iconContainer.style = "float: right; margin-left: 10px; height: 20px; color: var(--wikt-palette-black)";
popupHeader.append(iconContainer);
let titleLink = document.createElement("a");
titleLink.href = link.href;
titleLink.title = link.title;
titleLink.className = "skin-invert-image preview-headerlink";
// Scrape entry content.
if (isWPlink) {
iconContainer.innerHTML = articleIcon;
titleLink.textContent = linkTitle.replaceAll("_", " ");
if (responseDocument.title)
titleLink.innerHTML = responseDocument.title; // sometimes gives HTML text
let WikipediaName = WP_LANGUAGES.get(API_domain.substr(8).split(".")[0]);
popupHeader.append(WikipediaName + " Wikipedia article for ", titleLink);
let articleContent = document.createElement("div");
articleContent.style.margin = "5px 0 0 10px";
let firstSection = responseDocument.querySelector("section");
if (firstSection && responseDocument.querySelector(`meta[property="mw:pageNamespace"][content="0"]`)) {
firstSection.querySelectorAll("b").forEach(boldElem => boldElem.outerHTML = boldElem.innerHTML);
firstSection.querySelectorAll(":scope > p, :scope > ul, :scope > ol").forEach(elem => articleContent.append(elem));
}
if (articleContent.childElementCount)
popupContent.append(articleContent);
} else if (isGlossary) {
iconContainer.innerHTML = glossaryIcon;
titleLink.textContent = linkAnchor.replaceAll("_", " ");
popupHeader.append("Glossary definition of ", titleLink);
if (anchoredElement && anchoredElement.matches(".template-anchor")) {
// Get succeeding `dd` elements. The HTML is <dt>...<span class="template-anchor"></span>...</dt><dd>...</dd><dd>...</dd>
for (let elem = anchoredElement.parentElement.nextElementSibling; elem && elem.matches("dd"); elem = elem.nextElementSibling) {
elem.style = "margin: 5px 0 0 15px";
popupContent.append(elem.cloneNode(true));
}
}
} else if (linkTitle.startsWith("Appendix:Bibliography/")) {
iconContainer.innerHTML = bibliographyIcon;
let language = linkTitle.slice(22).replaceAll("_", " "); // trim "Appendix:Bibliography/" prefix
popupHeader.append(`${language} bibliography`);
if (anchoredElement && anchoredElement.matches(".bibliography-id")) {
let bibliographyCitation = document.createElement("div");
bibliographyCitation.style = "margin: 5px 0 0 15px";
bibliographyCitation.innerHTML = anchoredElement.innerHTML;
popupContent.append(bibliographyCitation);
}
} else {
const hasDefinitions = elem => Boolean(elem.querySelector("ol:not(.references), .ja-see, .zh-see"));
iconContainer.innerHTML = definitionIcon;
// Try to resolve the anchor element to #Chinese.
if (!anchoredElement && ["Cantonese", "Gan", "Hakka", "Hokkien", "Literary_Chinese", "Mandarin", "Middle_Chinese", "Old_Chinese", "Wu", "Xiang"].includes(linkAnchor))
anchoredElement = responseDocument.querySelector("#Chinese");
// Try to guess the anchor target in the following order: English, Chinese, Translingual, [first h2 on page]
// Always prioritize an L2 section which contains definitions.
if (!linkAnchor) {
let pageH2s = [...responseDocument.querySelectorAll("h2")];
if (pageH2s.some(h2 => hasDefinitions(h2.parentElement)))
pageH2s = pageH2s.filter(h2 => hasDefinitions(h2.parentElement));
anchoredElement = pageH2s.find(h2 => h2.id === "English");
if (!anchoredElement) anchoredElement = pageH2s.find(h2 => h2.id === "Chinese");
if (!anchoredElement) anchoredElement = pageH2s.find(h2 => h2.id === "Translingual");
if (!anchoredElement) anchoredElement = pageH2s[0];
}
let displayTitle = document.createElement("strong");
displayTitle.textContent = linkTitle.split("/").pop().replaceAll("_", " ");
if (linkTitle.startsWith("Reconstruction:"))
displayTitle.textContent = "*" + displayTitle.textContent;
// Find localest section containing an h2.
let languageSection = anchoredElement;
while (languageSection && !languageSection.querySelector(":scope > h2"))
languageSection = languageSection.parentElement.closest("section");
// Make sure that the entry is well-formed.
if (languageSection) {
let language = languageSection.querySelector(":scope > h2").textContent;
let scrapeSection = anchoredElement.closest("section");
// Find localest section which contains any definitions.
while (scrapeSection && !hasDefinitions(scrapeSection))
scrapeSection = scrapeSection.parentElement.closest("section");
if (scrapeSection) {
let headwords = [...scrapeSection.querySelectorAll(".headword-line > strong")];
if (new Set(headwords.map(h => h.textContent)).size === 1) {
// If there is a single unique headword, replace the display title with that.
displayTitle = headwords[0];
if (displayTitle.querySelector("a > img"))
displayTitle = displayTitle.querySelector("a > img").cloneNode();
} else if (new Set(headwords.map(h => h.cloneNode().outerHTML)).size === 1) {
// If there is a single unique set of element attributes, use that with the display title.
headwords[0].innerHTML = displayTitle.innerHTML;
displayTitle = headwords[0];
}
// Remove existing links in the headword.
displayTitle.querySelectorAll("a").forEach(elem => elem.replaceWith(elem.textContent));
let ols = scrapeSection.querySelectorAll(":scope > ol:not(.references), section > ol:not(.references)");
if (anchoredElement.matches(".senseid")) {
[...anchoredElement.parentElement.childNodes].forEach(child => {
if (child !== anchoredElement) child.remove();
});
ols = [anchoredElement.closest("ol")];
}
for (let ol of ols) {
let POS_container = document.createElement("div");
POS_container.style.margin = "8px 0 5px 0";
let POS = document.createElement("span");
POS.style = "font-size: 110%; font-weight: bold";
POS.textContent = ol.closest("section").firstChild.textContent;
POS_container.append(POS);
let tlb = ol.closest("section").querySelector(".headword-line ~ .usage-label-term");
if (tlb)
POS_container.append(document.createElement("br"), tlb);
popupContent.append(POS_container, ol);
}
// {{ja-see}} and {{zh-see}} don't follow the normal format, so just insert them verbatim.
for (let seeTemplate of scrapeSection.querySelectorAll(".ja-see, .zh-see"))
popupContent.append(seeTemplate);
}
titleLink.append(displayTitle);
popupHeader.append("Preview definitions of " + language + " ", titleLink);
} else {
titleLink.append(displayTitle);
popupHeader.append("Preview definitions of ", titleLink);
}
}
// Post-fetch error handling.
if (popupContent.childElementCount > 1) {
// Clean up HTML.
popupContent.querySelectorAll("link, .previewonly, .maintenance-line, .mw-empty-elt, .reference, .Inline-Template, .vsHide").forEach(elem => elem.remove());
if (!isWPlink)
popupContent.querySelectorAll("li > ul").forEach(elem => elem.remove()); // remove quotations
for (let elem of popupContent.querySelectorAll("*"))
elem.removeAttribute("id"); // avoid inadvertently repeating IDs within a page
} else if (isWPlink) {
// Display a message if the Wikipedia article was invalid or not in mainspace.
let noArticle = document.createElement("div");
noArticle.style = "margin: 5px 0 0 15px; font-size: 90%";
noArticle.textContent = "(article content could not be previewed)";
popupContent.append(noArticle);
} else if (linkAnchor && !anchoredElement) {
// Display a message if the anchor is invalid.
let noSectionFound = document.createElement("div");
noSectionFound.style = "margin: 10px 0 0 7.5px; font-size: 90%";
let strong = document.createElement("strong");
strong.textContent = linkAnchor.replaceAll("_", " ");
noSectionFound.append("The ", strong, " section was not found on this page.");
popupContent.append(noSectionFound);
} else {
// Display a message if no definitions were found in the section.
let noDefinitionsFound = document.createElement("div");
noDefinitionsFound.style = "margin: 5px 0 0 15px; font-size: 90%";
noDefinitionsFound.textContent = "(no definitions found)";
popupContent.append(noDefinitionsFound);
}
// Reduce the padding if a scrollbar is present. The intended padding is 16px on each side.
if (width - popupHeader.clientWidth > 32)
popupContent.style.paddingRight = Math.max(4, 48 + popupHeader.clientWidth - width) + "px";
});
// (Hack?) make sure that `link pointerleave` doesn't cause the popup to immediately close.
setTimeout(() => clearTimeout(popupTimer), 0);
}, isPreviewLink ? 1200 : 400);
});
link.addEventListener("pointerout", () => {
clearTimeout(popupTimer);
if (link === openLink) {
popupTimer = setTimeout(closePopup, 300);
} else {
API_controller.abort();
API_controller = new AbortController();
}
});
link.addEventListener("pointermove", event => [mouseX, mouseY] = [event.clientX, event.clientY]);
link.addEventListener("click", () => clearTimeout(popupTimer));
}
// Process all links.
document.querySelectorAll("a").forEach(processLink);
// Process links which are added to the DOM after the gadget has run.
(new MutationObserver(events => events.flatMap(event => [...event.addedNodes]).forEach(node => {
if (node instanceof HTMLAnchorElement)
processLink(node);
if (node instanceof HTMLElement)
node.querySelectorAll("a").forEach(processLink);
}))).observe(document.body, {childList: true, subtree: true});
// Dispatch hover events to support [[w:MediaWiki:Gadget-ReferenceTooltips.js]].
const linkEvent = ev => openLink && openLink.dispatchEvent(new Event(ev, {bubbles: true, cancelable: true}));
// Note: for some reason an event listener on `popup` doesn't catch everything...
popupContainer.addEventListener("pointerenter", () => setTimeout(() => linkEvent("mouseover")));
popupContainer.addEventListener("pointerleave", () => linkEvent("mouseout"));
// Custom events
popupContainer.addEventListener("pointerenter", () => linkEvent("linkPopupHover"));
popupContainer.addEventListener("pointerleave", () => linkEvent("linkPopupUnhover"));
// </nowiki>
m7wjlrwv96nlex8o1osdzwat04davci
88929
88928
2026-08-06T23:15:37Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-PagePreviews.js]]
88928
javascript
text/javascript
// Page Previews for Wiktionary, by [[User:Ioaxxere]]
// <nowiki>
// Generated using: [[MediaWiki:Gadget-PagePreviews.js/ConstGeneration]]
const INTERWIKI_PREFIXES = new Map([["aa","aa"],["ab","ab"],["abstract","abstract"],["acc",0],["ace","ace"],["acronym",0],["advisory",0],["ady","ady"],["af","af"],["ak","ak"],["als","als"],["alt","alt"],["am","am"],["ami","ami"],["an","an"],["ang","ang"],["ann","ann"],["anp","anp"],["antwiki",0],["appropedia",0],["aquariumwiki",0],["ar","ar"],["arbcom-zh",0],["arborwiki",0],["arc","arc"],["arxiv",0],["ary","ary"],["arz","arz"],["as","as"],["ast","ast"],["atj","atj"],["av","av"],["avk","avk"],["awa","awa"],["ay","ay"],["az","az"],["azb","azb"],["b",0],["ba","ba"],["ban","ban"],["bar","bar"],["bat-smg","bat-smg"],["battlestarwiki",0],["bbc","bbc"],["bcl","bcl"],["bcnbio",0],["bdr","bdr"],["be","be"],["be-tarask","be-tarask"],["be-x-old","be-tarask"],["beacha",0],["betawiki",0],["betawikiversity",0],["bew","bew"],["bg","bg"],["bh","bh"],["bi","bi"],["bibcode",0],["bibliowiki",0],["bjn","bjn"],["blk","blk"],["bm","bm"],["bn","bn"],["bo","bo"],["botwiki",0],["boxrec",0],["bpy","bpy"],["br","br"],["bs","bs"],["btm","btm"],["bug","bug"],["bugzilla",0],["bulba",0],["bxr","bxr"],["c",0],["c2",0],["ca","ca"],["cache",0],["cbk-zam","cbk-zam"],["ccorg",0],["cdo","cdo"],["ce","ce"],["ceb","ceb"],["centralwikia",0],["ch","ch"],["chapter",0],["cho","cho"],["choralwiki",0],["chr","chr"],["chy","chy"],["citizendium",0],["ckb","ckb"],["cmn","zh"],["co","co"],["commons",0],["communitywiki",0],["comune",0],["cr","cr"],["creativecommons",0],["creativecommonswiki",0],["crh","crh"],["cs","cs"],["csb","csb"],["cu","cu"],["cv","cv"],["cy","cy"],["cz","cs"],["d",0],["da","da"],["dag","dag"],["dbdump",0],["dcdatabase",0],["dcw",0],["de","de"],["debian",0],["devmo",0],["dga","dga"],["dico",0],["dicoado",0],["dict",0],["dictionary",0],["diffblog",0],["din","din"],["diq","diq"],["discord",0],["disinfopedia",0],["dk","da"],["dmoz",0],["dmozs",0],["doi",0],["donate",0],["doom_wiki",0],["download",0],["dpd",0],["dpla",0],["drae",0],["dsb","dsb"],["dtp","dtp"],["dty","dty"],["dv","dv"],["dz","dz"],["ee","ee"],["egl","eml"],["el","el"],["elibre",0],["emacswiki",0],["eml","eml"],["en","en"],["en-simple","simple"],["encyc",0],["englyphwiki",0],["enkol",0],["eo","eo"],["epo","eo"],["es","es"],["esolang",0],["et","et"],["etherpad",0],["etherpadbackup",0],["ethnologue",0],["ethnologuefamily",0],["eu","eu"],["exkcd",0],["exotica",0],["ext","ext"],["f",0],["fa","fa"],["fandom",0],["fanimutationwiki",0],["fat","fat"],["fedora",0],["ff","ff"],["fi","fi"],["finalfantasy",0],["finnix",0],["fiu-vro","fiu-vro"],["fj","fj"],["flickrphoto",0],["flickruser",0],["fo","fo"],["foldoc",0],["fon","fon"],["foundation",0],["foundationsite",0],["fr","fr"],["freebsdman",0],["freedomdefined",0],["freenode",0],["freesoft",0],["frp","frp"],["frr","frr"],["fur","fur"],["fy","fy"],["ga","ga"],["gag","gag"],["gan","gan"],["gcr","gcr"],["gd","gd"],["gentoo",0],["genwiki",0],["gerrit",0],["git",0],["github",0],["gitiles",0],["gitlab",0],["gl","gl"],["glk","glk"],["globalcontribs",0],["glottolog",0],["glottopedia",0],["gn","gn"],["gom","gom"],["google",0],["googledefine",0],["googlegroups",0],["gor","gor"],["got","got"],["gpe","gpe"],["gs",0],["gsw","als"],["gu","gu"],["guc","guc"],["gucprefix",0],["guildwarswiki",0],["gur","gur"],["gutenberg",0],["gutenbergwiki",0],["guw","guw"],["gv","gv"],["ha","ha"],["hackerspaces",0],["hak","hak"],["hammondwiki",0],["haw","haw"],["hdl",0],["he","he"],["heraldik",0],["hi","hi"],["hif","hif"],["ho","ho"],["horizonlabs",0],["hr","hr"],["hrfwiki",0],["hrwiki",0],["hsb","hsb"],["ht","ht"],["hu","hu"],["hy","hy"],["hyw","hyw"],["hz","hz"],["ia","ia"],["iarchive",0],["iba","iba"],["id","id"],["ie","ie"],["ig","ig"],["igl","igl"],["ii","ii"],["ik","ik"],["ilo","ilo"],["imdbcompany",0],["imdbname",0],["imdbtitle",0],["incubator",0],["infosphere",0],["inh","inh"],["io","io"],["irc",0],["ircrc",0],["ircs",0],["is","is"],["isni",0],["iso639-3",0],["issn",0],["it","it"],["iu","iu"],["ja","ja"],["jaglyphwiki",0],["jam","jam"],["jbo","jbo"],["jira",0],["jp","ja"],["jstor",0],["jv","jv"],["ka","ka"],["kaa","kaa"],["kab","kab"],["kai","kai"],["kaj","kaj"],["kamelo",0],["karlsruhe",0],["kbd","kbd"],["kbp","kbp"],["kcg","kcg"],["kg","kg"],["kge","kge"],["ki","ki"],["kj","kj"],["kk","kk"],["kl","kl"],["km","km"],["kn","kn"],["knc","knc"],["ko","ko"],["koi","koi"],["kr","kr"],["krc","krc"],["ks","ks"],["ksh","ksh"],["ku","ku"],["kus","kus"],["kv","kv"],["kw","kw"],["ky","ky"],["la","la"],["labsconsole",0],["lad","lad"],["lb","lb"],["lbe","lbe"],["lexemes",0],["lez","lez"],["lfn","lfn"],["lg","lg"],["li","li"],["liberachat",0],["libreplanet",0],["lij","lij"],["lingualibre",0],["linguistlist",0],["listarchive",0],["lld","lld"],["lmo","lmo"],["ln","ln"],["lo","lo"],["localwiki",0],["lofc",0],["lojban",0],["lokalhistoriewiki",0],["lostpedia",0],["lrc","lrc"],["lt","lt"],["ltg","ltg"],["luxo",0],["lv","lv"],["lzh","zh-classical"],["m",0],["mad","mad"],["mag","mag"],["mai","mai"],["mail",0],["mailarchive",0],["map-bms","map-bms"],["mariowiki",0],["marveldatabase",0],["mdf","mdf"],["mdwiki",0],["meatball",0],["mediawikiwiki",0],["mediazilla",0],["memoryalpha",0],["meta",0],["metawiki",0],["metawikimedia",0],["metawikipedia",0],["mg","mg"],["mh","mh"],["mhr","mhr"],["mi","mi"],["min","min"],["mineralienatlas",0],["miraheze",0],["mixnmatch",0],["mk","mk"],["ml","ml"],["mn","mn"],["mni","mni"],["mnw","mnw"],["mo","mo"],["moinmoin",0],["mos","mos"],["mosapedia",0],["mozillawiki",0],["mozillazinekb",0],["mr","mr"],["mrj","mrj"],["ms","ms"],["mt","mt"],["mus","mus"],["mw",0],["mwl","mwl"],["mwod",0],["mwot",0],["my","my"],["myv","myv"],["mzn","mzn"],["n",0],["na","na"],["nah","nah"],["nan","zh-min-nan"],["nap","nap"],["nara",0],["nb","no"],["nds","nds"],["nds-nl","nds-nl"],["ne","ne"],["new","new"],["ng","ng"],["nia","nia"],["nl","nl"],["nlab",0],["nn","nn"],["no","no"],["nost","nostalgia"],["nostalgia","nostalgia"],["nov","nov"],["nqo","nqo"],["nr","nr"],["nrm","nrm"],["nso","nso"],["nup","nup"],["nv","nv"],["ny","ny"],["oc","oc"],["oclc",0],["oeis",0],["oewiki",0],["oldwikisource",0],["olo","olo"],["olpc",0],["om","om"],["openlibrary",0],["openstreetmap",0],["openwetware",0],["or","or"],["organicdesign",0],["orthodoxwiki",0],["os","os"],["osmwiki",0],["otrs",0],["otrswiki",0],["outreach",0],["outreachwiki",0],["owasp",0],["pa","pa"],["pag","pag"],["pam","pam"],["pap","pap"],["paws",0],["pcd","pcd"],["pcm","pcm"],["pdc","pdc"],["petscan",0],["pfl","pfl"],["phab",0],["phabricator",0],["pi","pi"],["pih","pih"],["pl","pl"],["planetmath",0],["pmid",0],["pms","pms"],["pnb","pnb"],["pnt","pnt"],["pokewiki",0],["pokéwiki",0],["policy",0],["ppl","ppl"],["proofwiki",0],["ps","ps"],["pt","pt"],["pwn","pwn"],["pyrev",0],["pythoninfo",0],["q",0],["qu","qu"],["quality",0],["quarry",0],["rcirc",0],["regiowiki",0],["rev",0],["revo",0],["rfc",0],["rheinneckar",0],["rki","rki"],["rm","rm"],["rmy","rmy"],["rn","rn"],["ro","ro"],["roa-rup","roa-rup"],["roa-tara","roa-tara"],["rodovid",0],["rsk","rsk"],["rt",0],["ru","ru"],["rue","rue"],["rup","roa-rup"],["rw","rw"],["s",0],["sa","sa"],["sah","sah"],["sat","sat"],["sc","sc"],["scholar",0],["schoolswp",0],["schoolwiki",0],["scn","scn"],["sco","sco"],["scores",0],["scoutwiki",0],["sd","sd"],["se","se"],["securewikidc",0],["semantic-mw",0],["senseislibrary",0],["sep11",0],["sg","sg"],["sgs","bat-smg"],["sh","sh"],["sharemap",0],["shi","shi"],["shn","shn"],["shoutwiki",0],["shy","shy"],["si","si"],["silcode",0],["simple","simple"],["sk","sk"],["skr","skr"],["sl","sl"],["slashdot",0],["sm","sm"],["smn","smn"],["sn","sn"],["so","so"],["sourceforge",0],["spamcheck",0],["spcom",0],["species",0],["sq","sq"],["sr","sr"],["srn","srn"],["ss","ss"],["st","st"],["stats",0],["stewardry",0],["stq","stq"],["strategy",0],["strategywiki",0],["su","su"],["sulutil",0],["sv","sv"],["svn",0],["sw","sw"],["swtrain",0],["syl","syl"],["szl","szl"],["szy","szy"],["ta","ta"],["tardis",0],["tay","tay"],["tclerswiki",0],["tcy","tcy"],["tdd","tdd"],["te","te"],["tenwiki","ten"],["test2wiki","test2"],["testcommons",0],["testwiki","test"],["testwikidata",0],["tet","tet"],["tfwiki",0],["tg","tg"],["th","th"],["thelemapedia",0],["theopedia",0],["ti","ti"],["ticket",0],["tig","tig"],["tk","tk"],["tl","tl"],["tly","tly"],["tmbw",0],["tn","tn"],["to","to"],["tok","tok"],["tolkiengateway",0],["toolforge",0],["toolhub",0],["toollabs",0],["tools",0],["tpi","tpi"],["tr","tr"],["translatewiki",0],["trv","trv"],["ts","ts"],["tswiki",0],["tt","tt"],["tum","tum"],["tviv",0],["tw","tw"],["twiki",0],["twl",0],["ty","ty"],["tyv","tyv"],["tyvawiki",0],["udm","udm"],["ug","ug"],["uk","uk"],["umap",0],["uncyclopedia",0],["unihan",0],["ur","ur"],["urbandict",0],["usability",0],["usemod",0],["utrs",0],["uz","uz"],["v",0],["ve","ve"],["vec","vec"],["vep","vep"],["vi","vi"],["viaf",0],["vikidia",0],["vlos",0],["vls","vls"],["vo","vo"],["votewiki",0],["voy",0],["vro","fiu-vro"],["vrts",0],["vrtwiki",0],["w","en"],["wa","wa"],["war","war"],["wcna",0],["weirdgloop",0],["werelate",0],["wg","wg-en"],["wikia",0],["wikiapiary",0],["wikiasite",0],["wikibooks",0],["wikicities",0],["wikicity",0],["wikiconference",0],["wikidata",0],["wikiedudashboard",0],["wikifunctions",0],["wikifur",0],["wikihow",0],["wikiindex",0],["wikilivres",0],["wikilivresru",0],["wikimania",0],["wikimedia",0],["wikinews",0],["wikinfo",0],["wikinvest",0],["wikipapers",0],["wikipedia",0],["wikipediawikipedia",0],["wikiquote",0],["wikiskripta",0],["wikisophia",0],["wikisource",0],["wikisp",0],["wikispecies",0],["wikispore",0],["wikispot",0],["wikitech",0],["wikiti",0],["wikitrek",0],["wikiversity",0],["wikivoyage",0],["wikiwikiweb",0],["wikt",0],["wiktionary",0],["wm2005",0],["wm2006",0],["wm2007",0],["wm2008",0],["wm2009",0],["wm2010",0],["wm2011",0],["wm2012",0],["wm2013",0],["wm2014",0],["wm2015",0],["wm2016",0],["wm2017",0],["wm2018",0],["wmam",0],["wmania",0],["wmar",0],["wmat",0],["wmau",0],["wmbd",0],["wmbe",0],["wmbr",0],["wmca",0],["wmch",0],["wmcl",0],["wmcn",0],["wmco",0],["wmcz",0],["wmcz_docs",0],["wmcz_old",0],["wmdc",0],["wmde",0],["wmdeblog",0],["wmdk",0],["wmdoc",0],["wmec",0],["wmee",0],["wmes",0],["wmet",0],["wmf",0],["wmfblog",0],["wmfdashboard",0],["wmfi",0],["wmfr",0],["wmge",0],["wmhi",0],["wmhk",0],["wmhu",0],["wmid",0],["wmil",0],["wmin",0],["wmit",0],["wmke",0],["wmmk",0],["wmmx",0],["wmnl",0],["wmno",0],["wmnoc",0],["wmnoch",0],["wmnyc",0],["wmpa-us",0],["wmph",0],["wmpl",0],["wmplsite",0],["wmpt",0],["wmpunjabi",0],["wmromd",0],["wmrs",0],["wmru",0],["wmse",0],["wmsk",0],["wmteam",0],["wmtr",0],["wmtw",0],["wmua",0],["wmuk",0],["wmve",0],["wmza",0],["wo","wo"],["wookieepedia",0],["wowwiki",0],["wplibrary",0],["wurmpedia",0],["wuu","wuu"],["xal","xal"],["xh","xh"],["xkcd",0],["xmf","xmf"],["xtools",0],["yi","yi"],["yo","yo"],["yue","zh-yue"],["za","za"],["zea","zea"],["zgh","zgh"],["zh","zh"],["zh-classical","zh-classical"],["zh-cn","zh"],["zh-min-nan","zh-min-nan"],["zh-tw","zh"],["zh-yue","zh-yue"],["zu","zu"],["zum",0]]);
const WP_LANGUAGES = new Map([["ab","Abkhazian"],["ace","Acehnese"],["ady","Adyghe"],["af","Afrikaans"],["als","Alemannic"],["alt","Southern Altai"],["am","Amharic"],["ami","Amis"],["an","Aragonese"],["ang","Old English"],["ann","Obolo"],["anp","Angika"],["ar","Arabic"],["arc","Aramaic"],["ary","Moroccan Arabic"],["arz","Egyptian Arabic"],["as","Assamese"],["ast","Asturian"],["atj","Atikamekw"],["av","Avaric"],["avk","Kotava"],["awa","Awadhi"],["ay","Aymara"],["az","Azerbaijani"],["azb","South Azerbaijani"],["ba","Bashkir"],["ban","Balinese"],["bar","Bavarian"],["bat-smg","Samogitian"],["bbc","Batak Toba"],["bcl","Central Bikol"],["bdr","West Coast Bajau"],["be","Belarusian"],["be-tarask","Belarusian (Taraškievica orthography)"],["bew","Betawi"],["bg","Bulgarian"],["bh","Bhojpuri"],["bi","Bislama"],["bjn","Banjar"],["blk","Pa'O"],["bm","Bambara"],["bn","Bangla"],["bo","Tibetan"],["bpy","Bishnupriya"],["br","Breton"],["bs","Bosnian"],["btm","Batak Mandailing"],["bug","Buginese"],["bxr","Russia Buriat"],["ca","Catalan"],["cbk-zam","Chavacano"],["cdo","Mindong"],["ce","Chechen"],["ceb","Cebuano"],["ch","Chamorro"],["chr","Cherokee"],["chy","Cheyenne"],["ckb","Central Kurdish"],["co","Corsican"],["crh","Crimean Tatar"],["cs","Czech"],["csb","Kashubian"],["cu","Church Slavic"],["cv","Chuvash"],["cy","Welsh"],["da","Danish"],["dag","Dagbani"],["de","German"],["dga","Southern Dagaare"],["din","Dinka"],["diq","Dimli"],["dsb","Lower Sorbian"],["dtp","Central Dusun"],["dty","Doteli"],["dv","Divehi"],["dz","Dzongkha"],["ee","Ewe"],["el","Greek"],["eml","Emiliano-Romagnolo"],["en","English"],["eo","Esperanto"],["es","Spanish"],["et","Estonian"],["eu","Basque"],["ext","Extremaduran"],["fa","Persian"],["fat","Fanti"],["ff","Fula"],["fi","Finnish"],["fiu-vro","Võro"],["fj","Fijian"],["fo","Faroese"],["fon","Fon"],["fr","French"],["frp","Arpitan"],["frr","Northern Frisian"],["fur","Friulian"],["fy","Western Frisian"],["ga","Irish"],["gag","Gagauz"],["gan","Gan"],["gcr","Guianan Creole"],["gd","Scottish Gaelic"],["gl","Galician"],["glk","Gilaki"],["gn","Guarani"],["gom","Goan Konkani"],["gor","Gorontalo"],["got","Gothic"],["gpe","Ghanaian Pidgin"],["gu","Gujarati"],["guc","Wayuu"],["gur","Frafra"],["guw","Gun"],["gv","Manx"],["ha","Hausa"],["hak","Hakka Chinese"],["haw","Hawaiian"],["he","Hebrew"],["hi","Hindi"],["hif","Fiji Hindi"],["hr","Croatian"],["hsb","Upper Sorbian"],["ht","Haitian Creole"],["hu","Hungarian"],["hy","Armenian"],["hyw","Western Armenian"],["ia","Interlingua"],["iba","Iban"],["id","Indonesian"],["ie","Interlingue"],["ig","Igbo"],["igl","Igala"],["ik","Inupiaq"],["ilo","Iloko"],["inh","Ingush"],["io","Ido"],["is","Icelandic"],["it","Italian"],["iu","Inuktitut"],["ja","Japanese"],["jam","Jamaican Creole English"],["jbo","Lojban"],["jv","Javanese"],["ka","Georgian"],["kaa","Kara-Kalpak"],["kab","Kabyle"],["kai","Karekare"],["kaj","Jju"],["kbd","Kabardian"],["kbp","Kabiye"],["kcg","Tyap"],["kg","Kongo"],["kge","Komering"],["ki","Kikuyu"],["kk","Kazakh"],["km","Khmer"],["kn","Kannada"],["knc","Central Kanuri"],["ko","Korean"],["koi","Komi-Permyak"],["krc","Karachay-Balkar"],["ks","Kashmiri"],["ksh","Colognian"],["ku","Kurdish"],["kus","Kusaal"],["kv","Komi"],["kw","Cornish"],["ky","Kyrgyz"],["la","Latin"],["lad","Ladino"],["lb","Luxembourgish"],["lbe","Lak"],["lez","Lezghian"],["lfn","Lingua Franca Nova"],["lg","Ganda"],["li","Limburgish"],["lij","Ligurian"],["lld","Ladin"],["lmo","Lombard"],["ln","Lingala"],["lo","Lao"],["lt","Lithuanian"],["ltg","Latgalian"],["lv","Latvian"],["mad","Madurese"],["mag","Magahi"],["mai","Maithili"],["map-bms","Banyumasan"],["mdf","Moksha"],["mg","Malagasy"],["mhr","Eastern Mari"],["mi","Māori"],["min","Minangkabau"],["mk","Macedonian"],["ml","Malayalam"],["mn","Mongolian"],["mni","Manipuri"],["mnw","Mon"],["mos","Mossi"],["mr","Marathi"],["mrj","Western Mari"],["ms","Malay"],["mt","Maltese"],["mwl","Mirandese"],["my","Burmese"],["myv","Erzya"],["mzn","Mazanderani"],["nah","Nahuatl"],["nap","Neapolitan"],["nds","Low German"],["nds-nl","Low Saxon"],["ne","Nepali"],["new","Newari"],["nia","Nias"],["nl","Dutch"],["nn","Norwegian Nynorsk"],["no","Norwegian"],["nov","Novial"],["nqo","N’Ko"],["nr","South Ndebele"],["nrm","Norman"],["nso","Northern Sotho"],["nup","Nupe"],["nv","Navajo"],["ny","Nyanja"],["oc","Occitan"],["olo","Livvi-Karelian"],["om","Oromo"],["or","Odia"],["os","Ossetic"],["pa","Punjabi"],["pag","Pangasinan"],["pam","Pampanga"],["pap","Papiamento"],["pcd","Picard"],["pcm","Nigerian Pidgin"],["pdc","Pennsylvania German"],["pfl","Palatine German"],["pi","Pali"],["pl","Polish"],["pms","Piedmontese"],["pnb","Western Punjabi"],["pnt","Pontic"],["ppl","Nawat"],["ps","Pashto"],["pt","Portuguese"],["pwn","Paiwan"],["qu","Quechua"],["rki","Arakanese"],["rm","Romansh"],["rmy","Vlax Romani"],["rn","Rundi"],["ro","Romanian"],["roa-rup","Aromanian"],["roa-tara","Tarantino"],["rsk","Pannonian Rusyn"],["ru","Russian"],["rue","Rusyn"],["rw","Kinyarwanda"],["sa","Sanskrit"],["sah","Yakut"],["sat","Santali"],["sc","Sardinian"],["scn","Sicilian"],["sco","Scots"],["sd","Sindhi"],["se","Northern Sami"],["sg","Sango"],["sh","Serbo-Croatian"],["shi","Tachelhit"],["shn","Shan"],["si","Sinhala"],["simple","Simple English"],["sk","Slovak"],["skr","Saraiki"],["sl","Slovenian"],["sm","Samoan"],["smn","Inari Sami"],["sn","Shona"],["so","Somali"],["sq","Albanian"],["sr","Serbian"],["srn","Sranan Tongo"],["ss","Swati"],["st","Southern Sotho"],["stq","Saterland Frisian"],["su","Sundanese"],["sv","Swedish"],["sw","Swahili"],["syl","Sylheti"],["szl","Silesian"],["szy","Sakizaya"],["ta","Tamil"],["tay","Atayal"],["tcy","Tulu"],["tdd","Tai Nuea"],["te","Telugu"],["tet","Tetum"],["tg","Tajik"],["th","Thai"],["ti","Tigrinya"],["tig","Tigre"],["tk","Turkmen"],["tl","Tagalog"],["tly","Talysh"],["tn","Tswana"],["to","Tongan"],["tok","Toki Pona"],["tpi","Tok Pisin"],["tr","Turkish"],["trv","Taroko"],["ts","Tsonga"],["tt","Tatar"],["tum","Tumbuka"],["tw","Twi"],["ty","Tahitian"],["tyv","Tuvinian"],["udm","Udmurt"],["ug","Uyghur"],["uk","Ukrainian"],["ur","Urdu"],["uz","Uzbek"],["ve","Venda"],["vec","Venetian"],["vep","Veps"],["vi","Vietnamese"],["vls","West Flemish"],["vo","Volapük"],["wa","Walloon"],["war","Waray"],["wo","Wolof"],["wuu","Wu"],["xal","Kalmyk"],["xh","Xhosa"],["xmf","Mingrelian"],["yi","Yiddish"],["yo","Yoruba"],["za","Zhuang"],["zea","Zeelandic"],["zgh","Standard Moroccan Tamazight"],["zh","Chinese"],["zh-classical","Literary Chinese"],["zh-min-nan","Minnan"],["zh-yue","Cantonese"],["zu","Zulu"]]);
const appendixWithSlash = ["Adûni", "Afrihili", "Arabic_roots", "Belter_Creole", "Bibliography", "Black_Speech", "Bolak", "Communicationssprache", "Dothraki", "Eloi", "Gestures", "Glosa", "Goa'uld", "High_Valyrian", "Idiom_Neutral", "Interlingue", "Interslavic", "Klingon", "Kotava", "Láadan", "Lapine", "Lingua_Franca_Nova", "Lojban", "Mandalorian", "Medefaidrin", "Minecraft", "Mundolinco", "Na'vi", "Neo", "Novial", "Noxilo", "Protologisms/Long_words", "Quenya", "Romanova", "Sindarin", "Snowclones", "Talossan", "Toki_Pona", "Unas"];
const appendixNoSlash = ["Boldface", "Capital_letter", "Italics", "Lowercase_letter", "Possessive", "Repetition", "Small_caps", "Strikethrough", "Subscript", "Superscript", "Underline", "Uppercase_letter"];
const animationSpeed = 0.2; // seconds
mw.util.addCSS(`
.wikt-page-preview ol {
margin: 0 0.5em 0 1.5em;
padding: 0;
}
.wikt-page-preview dl {
margin-bottom: 0;
}
.wikt-page-preview p {
margin: 0;
}
/* popupContainer has the opacity animation, while popup gets translated. */
.popup-fade-in-up, .popup-fade-in-down {
animation: popup-fade-in ${animationSpeed}s ease forwards;
}
.popup-fade-out-up, .popup-fade-out-down {
animation: popup-fade-out ${animationSpeed}s ease forwards;
}
.popup-fade-in-up > div {
animation: popup-move-in-up ${animationSpeed}s ease forwards;
}
.popup-fade-in-down > div {
animation: popup-move-in-down ${animationSpeed}s ease forwards;
}
.popup-fade-out-up > div {
animation: popup-move-out-up ${animationSpeed}s ease forwards;
}
.popup-fade-out-down > div {
animation: popup-move-out-down ${animationSpeed}s ease forwards;
}
@keyframes popup-move-in-up {
0% {
transform: translate(0, 20px);
}
}
@keyframes popup-move-in-down {
0% {
transform: translate(0, -20px);
}
}
@keyframes popup-move-out-up {
100% {
transform: translate(0, -20px);
}
}
@keyframes popup-move-out-down {
100% {
transform: translate(0, 20px);
}
}
@keyframes popup-fade-out {
100% {
opacity: 0;
}
}
@keyframes popup-fade-in {
0% {
opacity: 0;
}
}
.ring-loader {
margin: auto;
width: 24px;
height: 24px;
border-radius: 50%;
border-top: 5px solid var(--wikt-palette-black);
border-bottom: 5px solid var(--wikt-palette-black);
border-left: 5px solid transparent;
border-right: 5px solid transparent;
animation: spin 1.2s linear infinite;
}
@keyframes spin {
100% {
transform: rotate(360deg);
}
}
.preview-headerlink, .preview-headerlink:visited {
color: inherit;
font-weight: bold;
}
/* Hack: get the speaker icon on Wikipedia articles without having to load Phonos. */
.ext-phonos .oo-ui-buttonElement-button:after {
content: "🔊";
}
.ext-phonos * {
display: inline !important;
padding: 0 !important;
margin: 0 !important;
}
`);
const definitionIcon = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" fill="currentColor"><g><path d="M15 2a7.65 7.65 0 00-5 2 7.65 7.65 0 00-5-2H1v15h4a7.65 7.65 0 015 2 7.65 7.65 0 015-2h4V2zm2.5 13.5H14a4.38 4.38 0 00-3 1V5s1-1.5 4-1.5h2.5z"></path></g></svg>`;
const glossaryIcon = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" fill="currentColor"><g><path d="M12.43 14.34A5 5 0 0110 15a5 5 0 113.95-2L17 16.09V3a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h10a2 2 0 001.45-.63z"></path><circle cx="10" cy="10" r="3"></circle></g></svg>`;
const articleIcon = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" fill="currentColor"><g><path d="M5 1a2 2 0 00-2 2v14a2 2 0 002 2h10a2 2 0 002-2V3a2 2 0 00-2-2zm0 3h5v1H5zm0 2h5v1H5zm0 2h5v1H5zm10 7H5v-1h10zm0-2H5v-1h10zm0-2H5v-1h10zm0-2h-4V4h4z"></path></g></svg>`;
const bibliographyIcon = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" fill="currentColor"><g><path d="M3 2h12c.5 0 1 .5 1 1v1c0 .5-.5 1-1 1H3c0 0 .5-.5.5-1V3c0-.5-.5-1-.5-1zM4 6h12c.5 0 1 .5 1 1v1c0 .5-.5 1-1 1H4c0 0 .5-.5.5-1V7c0-.5-.5-1-.5-1zM3 10h12c.5 0 1 .5 1 1v1c0 .5-.5 1-1 1H3c0 0 .5-.5.5-1v-1c0-.5-.5-1-.5-1zM4 14h12c.5 0 1 .5 1 1v1c0 .5-.5 1-1 1H4c0 0 .5-.5.5-1v-1c0-.5-.5-1-.5-1z"/></g></svg>`;
const loader = document.createElement("div");
loader.className = "ring-loader";
const popupContainer = document.createElement("div");
const popup = document.createElement("div");
const popupContent = document.createElement("div");
popupContainer.style = `display: none; position: absolute; filter: drop-shadow(0px 30px 30px rgba(0, 0, 0, 0.15)) drop-shadow(0px 0px 0.75px var(--wikt-palette-dullblue)); z-index: 801`;
popupContainer.className = "wikt-page-preview";
popup.style = "box-sizing: border-box; height: 100%; background: var(--wikt-palette-white)";
popupContent.style = "display: flex; flex-direction: column; border-radius: 2px; box-sizing: border-box; color: var(--wikt-palette-black); overflow: auto; height: 100%; overflow-wrap: break-word; scrollbar-width: thin";
document.body.append(popupContainer);
popupContainer.append(popup);
popup.append(popupContent);
let popupTimer;
let openLink;
let mouseX, mouseY;
let API_controller = new AbortController();
function closePopup() {
if (!openLink) return;
openLink = null;
API_controller.abort();
API_controller = new AbortController();
if (popupContainer.classList.contains("popup-fade-in-up")) {
popupContainer.classList.remove("popup-fade-in-up");
popupContainer.classList.add("popup-fade-out-down");
} else {
popupContainer.classList.remove("popup-fade-in-down");
popupContainer.classList.add("popup-fade-out-up");
}
// After the animation has completed, reset the popup to its initial state.
setTimeout(() => {
popupContainer.style.display = "none";
popupContainer.classList.remove("popup-fade-out-down", "popup-fade-out-up");
}, animationSpeed * 1000);
}
popup.addEventListener("pointerenter", () => {
clearTimeout(popupTimer);
});
popup.addEventListener("pointerleave", () => {
clearTimeout(popupTimer);
popupTimer = setTimeout(closePopup, 300);
});
function processLink(link) {
let linkTitle = decodeURIComponent(link.pathname.split("/wiki/")[1]);
let titlePrefix = linkTitle.includes(":") ? linkTitle.toLowerCase().split(":")[0] : "";
let resolvedTitle = linkTitle;
let API_domain = link.origin;
let linkAnchor = decodeURIComponent(link.hash.slice(1) || "");
let isWPlink = link.href.match(/^https:\/\/[^.]+.wikipedia.org\/wiki\//);
let isPreviewLink = Boolean(link.closest(".wikt-page-preview"));
let isGlossary = /^Appendix:.*[Gg]lossary$/.test(linkTitle);
// Start with various checks to determine whether a link should be processed.
if (link.matches(`nav a, .cancelLink a, .mw-widget-titleOptionWidget a, .new, .preview-headerlink, .external, [href$="#"]`)) {
return;
} else if (isGlossary) {
// Links to glossary *must* have an anchor.
if (!linkAnchor) return;
} else if (linkTitle.startsWith("Appendix:Bibliography/")) {
// Bibliography subpages must have an anchor starting with `bib-`.
if (!linkAnchor || !linkAnchor.startsWith("bib-")) return;
} else if (!isWPlink) {
// Other Wiktionary pages must not have a lowercase-starting anchor (filters out some unhelpful links).
if (/^[a-z]/.test(linkAnchor)) return;
}
if (link.href.startsWith("https://en.wiktionary.org/wiki/")) {
// Filter out all interwiki prefixes.
if (INTERWIKI_PREFIXES.has(titlePrefix))
return;
let {namespace, title} = new mw.Title(linkTitle);
if (namespace === 0) { // Mainspace
if (title.endsWith("/translations"))
return;
} else if (namespace === 100) { // Appendix space
if (!appendixWithSlash.some(lang => title.startsWith(lang + "/")) && !appendixNoSlash.includes(title) && !isGlossary)
return;
} else if (namespace !== 118) { // Reconstruction space
return;
}
} else if (isWPlink) {
// Filter out invalid interwiki prefixes.
if (INTERWIKI_PREFIXES.get(titlePrefix) === 0)
return;
// Get the resolved title if it's a language interwiki.
if (INTERWIKI_PREFIXES.has(titlePrefix)) {
API_domain = `https://${INTERWIKI_PREFIXES.get(titlePrefix)}.wikipedia.org`;
resolvedTitle = linkTitle.substring(linkTitle.indexOf(":") + 1);
// If the resolved title contains another interwiki, return.
let resolvedTitlePrefix = resolvedTitle.includes(":") ? resolvedTitle.toLowerCase().split(":")[0] : ""; // same as titlePrefix
if (INTERWIKI_PREFIXES.has(resolvedTitlePrefix))
return;
}
} else {
return;
}
link.addEventListener("pointerover", event => {
[mouseX, mouseY] = [event.clientX, event.clientY];
clearTimeout(popupTimer);
// Ignore links which are already open.
if (link === openLink)
return;
if (!isPreviewLink)
closePopup();
// Fetch popup text immediately on hover to reduce delay.
let responsePromise = fetch(`${API_domain}/api/rest_v1/page/html/${encodeURIComponent(resolvedTitle)}`, {
headers: {"Api-User-Agent": "Gadget developed by [[User:Ioaxxere]]"},
signal: API_controller.signal
}).then(r => r.text()).catch(() => { /* fetch was aborted */ });
popupTimer = setTimeout(() => {
popupContainer.style.display = "";
popupContent.innerHTML = "";
popupContent.append(loader);
popupContent.style.padding = "14px 16px 8px";
// Disable link hovering until animation has completed.
popupContent.style.pointerEvents = "none";
setTimeout(() => popupContent.style.pointerEvents = "", animationSpeed * 1000);
// Adapt the popup for large, standard, and small size preference in Vector 2022.
let sizeSetting = 0;
if (document.documentElement.matches(".vector-feature-custom-font-size-clientpref-1"))
sizeSetting = 1;
else if (document.documentElement.matches(".vector-feature-custom-font-size-clientpref-2"))
sizeSetting = 2;
let width = [320, 340, 360][sizeSetting];
let height = [210, 225, 240][sizeSetting];
let triangleSize = [9, 10, 11][sizeSetting];
popupContainer.style.fontSize = ["14px", "15px", "17px"][sizeSetting];
popupContainer.style.lineHeight = ["21px", "22px", "24px"][sizeSetting];
popupContainer.style.width = width + "px";
popupContainer.style.height = height + "px";
if (isPreviewLink) {
// Trigger fade-in-down animation.
popupContainer.classList.remove("popup-fade-in-up", "popup-fade-in-down");
void popupContainer.offsetLeft; // force reflow
popupContainer.classList.add("popup-fade-in-down");
} else {
openLink = link;
// Get list of rects (lines) of the target element, then find the one whose midpoint is closest to mouseY.
// This ensures that the code can correctly handle multi-line links.
let linkBlock = [...event.target.getClientRects()].reduce((prev, next) => Math.abs((next.top + next.bottom) / 2 - mouseY) < Math.abs((prev.top + prev.bottom) / 2 - mouseY) ? next : prev);
// Horizontal position. Choose left or right depending on the side of the screen the mouse is in.
let leftPosition = mouseX - 30;
if (mouseX > document.documentElement.clientWidth / 2)
leftPosition = mouseX - width + 30;
// Ensure that the popup is at least 5px away from the side of the screen.
leftPosition = Math.max(5, Math.min(leftPosition, document.documentElement.clientWidth - width - 5));
popupContainer.style.left = leftPosition + document.documentElement.scrollLeft + "px";
// Vertical position. Prioritize fade-in-down unless there would be less than 10px of room above.
if (linkBlock.top > height + 10 || linkBlock.top > document.documentElement.clientHeight - linkBlock.bottom) {
popupContainer.style.top = linkBlock.top + document.documentElement.scrollTop - height + "px";
popupContainer.classList.add("popup-fade-in-down");
popup.style.padding = `0 0 ${triangleSize}px`;
// Create a triangle on the bottom.
popup.style.clipPath = `polygon(0 0, 100% 0, 100% calc(100% - ${triangleSize}px), ${mouseX - leftPosition + triangleSize}px calc(100% - ${triangleSize}px), ${mouseX - leftPosition}px 100%, ${mouseX - leftPosition - triangleSize}px calc(100% - ${triangleSize}px), 0 calc(100% - ${triangleSize}px))`;
} else {
popupContainer.style.top = linkBlock.bottom + document.documentElement.scrollTop + "px";
popupContainer.classList.add("popup-fade-in-up");
popup.style.padding = `${triangleSize}px 0 0`;
// Create a triangle on top.
popup.style.clipPath = `polygon(0 ${triangleSize}px, ${mouseX - leftPosition - triangleSize}px ${triangleSize}px, ${mouseX - leftPosition}px 0, ${mouseX - leftPosition + triangleSize}px ${triangleSize}px, 100% ${triangleSize}px, 100% 100%, 0 100%)`;
}
}
responsePromise.then(response => {
if (!response) return; // if the fetch was aborted
popupContent.innerHTML = "";
let responseDocument = new DOMParser().parseFromString(response, "text/html");
// Convert to absolute URLs.
responseDocument.querySelectorAll("a[href]").forEach(link => link.setAttribute("href", link.href));
let anchoredElement = responseDocument.getElementById(linkAnchor);
let popupHeader = document.createElement("div");
popupHeader.style = "font-size: 90%; color: var(--wikt-palette-deepblue)";
popupContent.append(popupHeader);
let iconContainer = document.createElement("span");
iconContainer.style = "float: right; margin-left: 10px; height: 20px; color: var(--wikt-palette-black)";
popupHeader.append(iconContainer);
let titleLink = document.createElement("a");
titleLink.href = link.href;
titleLink.title = link.title;
titleLink.className = "skin-invert-image preview-headerlink";
// Scrape entry content.
if (isWPlink) {
iconContainer.innerHTML = articleIcon;
titleLink.textContent = linkTitle.replaceAll("_", " ");
if (responseDocument.title)
titleLink.innerHTML = responseDocument.title; // sometimes gives HTML text
let WikipediaName = WP_LANGUAGES.get(API_domain.substr(8).split(".")[0]);
popupHeader.append(WikipediaName + " Wikipedia article for ", titleLink);
let articleContent = document.createElement("div");
articleContent.style.margin = "5px 0 0 10px";
let firstSection = responseDocument.querySelector("section");
if (firstSection && responseDocument.querySelector(`meta[property="mw:pageNamespace"][content="0"]`)) {
firstSection.querySelectorAll("b").forEach(boldElem => boldElem.outerHTML = boldElem.innerHTML);
firstSection.querySelectorAll(":scope > p, :scope > ul, :scope > ol").forEach(elem => articleContent.append(elem));
}
if (articleContent.childElementCount)
popupContent.append(articleContent);
} else if (isGlossary) {
iconContainer.innerHTML = glossaryIcon;
titleLink.textContent = linkAnchor.replaceAll("_", " ");
popupHeader.append("Glossary definition of ", titleLink);
if (anchoredElement && anchoredElement.matches(".template-anchor")) {
// Get succeeding `dd` elements. The HTML is <dt>...<span class="template-anchor"></span>...</dt><dd>...</dd><dd>...</dd>
for (let elem = anchoredElement.parentElement.nextElementSibling; elem && elem.matches("dd"); elem = elem.nextElementSibling) {
elem.style = "margin: 5px 0 0 15px";
popupContent.append(elem.cloneNode(true));
}
}
} else if (linkTitle.startsWith("Appendix:Bibliography/")) {
iconContainer.innerHTML = bibliographyIcon;
let language = linkTitle.slice(22).replaceAll("_", " "); // trim "Appendix:Bibliography/" prefix
popupHeader.append(`${language} bibliography`);
if (anchoredElement && anchoredElement.matches(".bibliography-id")) {
let bibliographyCitation = document.createElement("div");
bibliographyCitation.style = "margin: 5px 0 0 15px";
bibliographyCitation.innerHTML = anchoredElement.innerHTML;
popupContent.append(bibliographyCitation);
}
} else {
const hasDefinitions = elem => Boolean(elem.querySelector("ol:not(.references), .ja-see, .zh-see"));
iconContainer.innerHTML = definitionIcon;
// Try to resolve the anchor element to #Chinese.
if (!anchoredElement && ["Cantonese", "Gan", "Hakka", "Hokkien", "Literary_Chinese", "Mandarin", "Middle_Chinese", "Old_Chinese", "Wu", "Xiang"].includes(linkAnchor))
anchoredElement = responseDocument.querySelector("#Chinese");
// Try to guess the anchor target in the following order: English, Chinese, Translingual, [first h2 on page]
// Always prioritize an L2 section which contains definitions.
if (!linkAnchor) {
let pageH2s = [...responseDocument.querySelectorAll("h2")];
if (pageH2s.some(h2 => hasDefinitions(h2.parentElement)))
pageH2s = pageH2s.filter(h2 => hasDefinitions(h2.parentElement));
anchoredElement = pageH2s.find(h2 => h2.id === "English");
if (!anchoredElement) anchoredElement = pageH2s.find(h2 => h2.id === "Chinese");
if (!anchoredElement) anchoredElement = pageH2s.find(h2 => h2.id === "Translingual");
if (!anchoredElement) anchoredElement = pageH2s[0];
}
let displayTitle = document.createElement("strong");
displayTitle.textContent = linkTitle.split("/").pop().replaceAll("_", " ");
if (linkTitle.startsWith("Reconstruction:"))
displayTitle.textContent = "*" + displayTitle.textContent;
// Find localest section containing an h2.
let languageSection = anchoredElement;
while (languageSection && !languageSection.querySelector(":scope > h2"))
languageSection = languageSection.parentElement.closest("section");
// Make sure that the entry is well-formed.
if (languageSection) {
let language = languageSection.querySelector(":scope > h2").textContent;
let scrapeSection = anchoredElement.closest("section");
// Find localest section which contains any definitions.
while (scrapeSection && !hasDefinitions(scrapeSection))
scrapeSection = scrapeSection.parentElement.closest("section");
if (scrapeSection) {
let headwords = [...scrapeSection.querySelectorAll(".headword-line > strong")];
if (new Set(headwords.map(h => h.textContent)).size === 1) {
// If there is a single unique headword, replace the display title with that.
displayTitle = headwords[0];
if (displayTitle.querySelector("a > img"))
displayTitle = displayTitle.querySelector("a > img").cloneNode();
} else if (new Set(headwords.map(h => h.cloneNode().outerHTML)).size === 1) {
// If there is a single unique set of element attributes, use that with the display title.
headwords[0].innerHTML = displayTitle.innerHTML;
displayTitle = headwords[0];
}
// Remove existing links in the headword.
displayTitle.querySelectorAll("a").forEach(elem => elem.replaceWith(elem.textContent));
let ols = scrapeSection.querySelectorAll(":scope > ol:not(.references), section > ol:not(.references)");
if (anchoredElement.matches(".senseid")) {
[...anchoredElement.parentElement.childNodes].forEach(child => {
if (child !== anchoredElement) child.remove();
});
ols = [anchoredElement.closest("ol")];
}
for (let ol of ols) {
let POS_container = document.createElement("div");
POS_container.style.margin = "8px 0 5px 0";
let POS = document.createElement("span");
POS.style = "font-size: 110%; font-weight: bold";
POS.textContent = ol.closest("section").firstChild.textContent;
POS_container.append(POS);
let tlb = ol.closest("section").querySelector(".headword-line ~ .usage-label-term");
if (tlb)
POS_container.append(document.createElement("br"), tlb);
popupContent.append(POS_container, ol);
}
// {{ja-see}} and {{zh-see}} don't follow the normal format, so just insert them verbatim.
for (let seeTemplate of scrapeSection.querySelectorAll(".ja-see, .zh-see"))
popupContent.append(seeTemplate);
}
titleLink.append(displayTitle);
popupHeader.append("Preview definitions of " + language + " ", titleLink);
} else {
titleLink.append(displayTitle);
popupHeader.append("Preview definitions of ", titleLink);
}
}
// Post-fetch error handling.
if (popupContent.childElementCount > 1) {
// Clean up HTML.
popupContent.querySelectorAll("link, .previewonly, .maintenance-line, .mw-empty-elt, .reference, .Inline-Template, .vsHide").forEach(elem => elem.remove());
if (!isWPlink)
popupContent.querySelectorAll("li > ul").forEach(elem => elem.remove()); // remove quotations
for (let elem of popupContent.querySelectorAll("*"))
elem.removeAttribute("id"); // avoid inadvertently repeating IDs within a page
} else if (isWPlink) {
// Display a message if the Wikipedia article was invalid or not in mainspace.
let noArticle = document.createElement("div");
noArticle.style = "margin: 5px 0 0 15px; font-size: 90%";
noArticle.textContent = "(article content could not be previewed)";
popupContent.append(noArticle);
} else if (linkAnchor && !anchoredElement) {
// Display a message if the anchor is invalid.
let noSectionFound = document.createElement("div");
noSectionFound.style = "margin: 10px 0 0 7.5px; font-size: 90%";
let strong = document.createElement("strong");
strong.textContent = linkAnchor.replaceAll("_", " ");
noSectionFound.append("The ", strong, " section was not found on this page.");
popupContent.append(noSectionFound);
} else {
// Display a message if no definitions were found in the section.
let noDefinitionsFound = document.createElement("div");
noDefinitionsFound.style = "margin: 5px 0 0 15px; font-size: 90%";
noDefinitionsFound.textContent = "(no definitions found)";
popupContent.append(noDefinitionsFound);
}
// Reduce the padding if a scrollbar is present. The intended padding is 16px on each side.
if (width - popupHeader.clientWidth > 32)
popupContent.style.paddingRight = Math.max(4, 48 + popupHeader.clientWidth - width) + "px";
});
// (Hack?) make sure that `link pointerleave` doesn't cause the popup to immediately close.
setTimeout(() => clearTimeout(popupTimer), 0);
}, isPreviewLink ? 1200 : 400);
});
link.addEventListener("pointerout", () => {
clearTimeout(popupTimer);
if (link === openLink) {
popupTimer = setTimeout(closePopup, 300);
} else {
API_controller.abort();
API_controller = new AbortController();
}
});
link.addEventListener("pointermove", event => [mouseX, mouseY] = [event.clientX, event.clientY]);
link.addEventListener("click", () => clearTimeout(popupTimer));
}
// Process all links.
document.querySelectorAll("a").forEach(processLink);
// Process links which are added to the DOM after the gadget has run.
(new MutationObserver(events => events.flatMap(event => [...event.addedNodes]).forEach(node => {
if (node instanceof HTMLAnchorElement)
processLink(node);
if (node instanceof HTMLElement)
node.querySelectorAll("a").forEach(processLink);
}))).observe(document.body, {childList: true, subtree: true});
// Dispatch hover events to support [[w:MediaWiki:Gadget-ReferenceTooltips.js]].
const linkEvent = ev => openLink && openLink.dispatchEvent(new Event(ev, {bubbles: true, cancelable: true}));
// Note: for some reason an event listener on `popup` doesn't catch everything...
popupContainer.addEventListener("pointerenter", () => setTimeout(() => linkEvent("mouseover")));
popupContainer.addEventListener("pointerleave", () => linkEvent("mouseout"));
// Custom events
popupContainer.addEventListener("pointerenter", () => linkEvent("linkPopupHover"));
popupContainer.addEventListener("pointerleave", () => linkEvent("linkPopupUnhover"));
// </nowiki>
m7wjlrwv96nlex8o1osdzwat04davci
MediaWiki:Gadget-CommentsInLocalTime.js
8
17964
88930
2024-12-11T21:21:52Z
en>Surjection
0
88930
javascript
text/javascript
// {{documentation}}
// _________________________________________________________________________________________
// | |
// | === WARNING: GLOBAL GADGET FILE === |
// | Changes to this page affect many users. |
// | Please discuss changes on the [[Wiktionary:Grease Pit]] before editing. |
// |_________________________________________________________________________________________|
//
//
// The content below are copied from https://en.wikipedia.org/w/index.php?title=User:Gary/comments_in_local_time.js&oldid=1103519446
// See [[w:User:Gary/comments_in_local_time.js]], [[w:Wikipedia:Comments in Local Time]]
// *********************************************************************************************************************************
/**
* COMMENTS IN LOCAL TIME
*
* Description:
* Changes [[Coordinated Universal Time|UTC]]-based times and dates,
* such as those used in signatures, to be relative to local time.
*
* Documentation:
* [[Wikipedia:Comments in Local Time]]
*/
$(() => {
/**
* Given a number, add a leading zero if necessary, so that the final number
* has two characters.
*
* @param {number} number Number
* @returns {string} The number with a leading zero, if necessary.
*/
function addLeadingZero(number) {
const numberArg = number;
if (numberArg < 10) {
return `0${numberArg}`;
}
return numberArg;
}
function convertMonthToNumber(month) {
return new Date(`${month} 1, 2001`).getMonth();
}
function getDates(time) {
const [, oldHour, oldMinute, oldDay, oldMonth, oldYear] = time;
// Today
const today = new Date();
// Yesterday
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
// Tomorrow
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
// Set the date entered.
const newTime = new Date();
newTime.setUTCFullYear(oldYear, convertMonthToNumber(oldMonth), oldDay);
newTime.setUTCHours(oldHour);
newTime.setUTCMinutes(oldMinute);
return { time: newTime, today, tomorrow, yesterday };
}
/**
* Determine whether to use the singular or plural word, and use that.
*
* @param {string} term Original term
* @param {number} count Count of items
* @param {string} plural Pluralized term
* @returns {string} The word to use
*/
function pluralize(term, count, plural = null) {
let pluralArg = plural;
// No unique pluralized word is found, so just use a general one.
if (!pluralArg) {
pluralArg = `${term}s`;
}
// There's only one item, so just use the singular word.
if (count === 1) {
return term;
}
// There are multiple items, so use the plural word.
return pluralArg;
}
class CommentsInLocalTime {
constructor() {
this.language = '';
this.LocalComments = {};
/**
* Settings
*/
this.settings();
this.language = this.setDefaultSetting(
'language',
this.LocalComments.language
);
// These values are also reflected in the documentation:
// https://en.wikipedia.org/wiki/Wikipedia:Comments_in_Local_Time#Default_settings
this.setDefaultSetting({
dateDifference: true,
dateFormat: 'dmy',
dayOfWeek: true,
dropDays: 0,
dropMonths: 0,
timeFirst: true,
twentyFourHours: false,
});
}
adjustTime(originalTimestamp, search) {
const { time, today, tomorrow, yesterday } = getDates(
originalTimestamp.match(search)
);
// A string matching the date pattern was found, but it cannot be
// converted to a Date object. Return it with no changes made.
if (Number.isNaN(time)) {
return [originalTimestamp, ''];
}
const date = this.determineDateText({
time,
today,
tomorrow,
yesterday,
});
const { ampm, hour } = this.getHour(time);
const minute = addLeadingZero(time.getMinutes());
const finalTime = `${hour}:${minute}${ampm}`;
// Determine the time offset.
const utcValue = (-1 * time.getTimezoneOffset()) / 60;
const utcOffset =
utcValue >= 0 ? `+${utcValue}` : `−${Math.abs(utcValue.toFixed(1))}`;
const utcPart = `(UTC${utcOffset})`;
const returnDate = this.LocalComments.timeFirst
? `${finalTime}, ${date} ${utcPart}`
: `${date}, ${finalTime} ${utcPart}`;
return returnDate;
}
convertNumberToMonth(number) {
return [
this.language.January,
this.language.February,
this.language.March,
this.language.April,
this.language.May,
this.language.June,
this.language.July,
this.language.August,
this.language.September,
this.language.October,
this.language.November,
this.language.December,
][number];
}
createDateText({ day, month, time, today, year }) {
// Calculate day of week
const dayNames = [
this.language.Sunday,
this.language.Monday,
this.language.Tuesday,
this.language.Wednesday,
this.language.Thursday,
this.language.Friday,
this.language.Saturday,
];
const dayOfTheWeek = dayNames[time.getDay()];
let descriptiveDifference = '';
let last = '';
// Create a relative descriptive difference
if (this.LocalComments.dateDifference) {
({ descriptiveDifference, last } = this.createRelativeDate(
today,
time
));
}
const monthName = this.convertNumberToMonth(time.getMonth());
// Format the date according to user preferences
let formattedDate = '';
switch (this.LocalComments.dateFormat.toLowerCase()) {
case 'dmy':
formattedDate = `${day} ${monthName} ${year}`;
break;
case 'mdy':
formattedDate = `${monthName} ${day}, ${year}`;
break;
default:
formattedDate = `${year}-${month}-${addLeadingZero(day)}`;
}
const formattedDayOfTheWeek = this.LocalComments.dayOfWeek
? `, ${last}${dayOfTheWeek}`
: '';
return `${formattedDate}${formattedDayOfTheWeek}${descriptiveDifference}`;
}
/**
* Create relative date data.
*
* @param {Date} today Today
* @param {Date} time The timestamp from a comment
* @returns {Object.<string, *>} Relative date data
*/
createRelativeDate(today, time) {
/**
* The time difference from today, in milliseconds.
*
* @type {number}
*/
const millisecondsAgo = today.getTime() - time.getTime();
/**
* The number of days ago, that we will display. It's not necessarily the
* total days ago.
*
* @type {number}
*/
let daysAgo = Math.abs(Math.round(millisecondsAgo / 1000 / 60 / 60 / 24));
const { differenceWord, last } = this.relativeText({
daysAgo,
millisecondsAgo,
});
// This method of computing the years and months is not exact. However,
// it's better than the previous method that used 1 January + delta days.
// That was usually quite off because it mapped the second delta month to
// February, which has only 28 days. This method is usually not more than
// one day off, except perhaps over very distant dates.
/**
* The number of months ago, that we will display. It's not necessarily
* the total months ago.
*
* @type {number}
*/
let monthsAgo = Math.floor((daysAgo / 365) * 12);
/**
* The total amount of time ago, in months.
*
* @type {number}
*/
const totalMonthsAgo = monthsAgo;
/**
* The number of years ago that we will display. It's not necessarily the
* total years ago.
*
* @type {number}
*/
let yearsAgo = Math.floor(totalMonthsAgo / 12);
if (totalMonthsAgo < this.LocalComments.dropMonths) {
yearsAgo = 0;
} else if (this.LocalComments.dropMonths > 0) {
monthsAgo = 0;
} else {
monthsAgo -= yearsAgo * 12;
}
if (daysAgo < this.LocalComments.dropDays) {
monthsAgo = 0;
yearsAgo = 0;
} else if (this.LocalComments.dropDays > 0 && totalMonthsAgo >= 1) {
daysAgo = 0;
} else {
daysAgo -= Math.floor((totalMonthsAgo * 365) / 12);
}
const descriptiveParts = [];
// There is years text to add.
if (yearsAgo > 0) {
descriptiveParts.push(
`${yearsAgo} ${pluralize(
this.language.year,
yearsAgo,
this.language.years
)}`
);
}
// There is months text to add.
if (monthsAgo > 0) {
descriptiveParts.push(
`${monthsAgo} ${pluralize(
this.language.month,
monthsAgo,
this.language.months
)}`
);
}
// There is days text to add.
if (daysAgo > 0) {
descriptiveParts.push(
`${daysAgo} ${pluralize(
this.language.day,
daysAgo,
this.language.days
)}`
);
}
return {
descriptiveDifference: ` (${descriptiveParts.join(
', '
)} ${differenceWord})`,
last,
};
}
determineDateText({ time, today, tomorrow, yesterday }) {
// Set the date bits to output.
const year = time.getFullYear();
const month = addLeadingZero(time.getMonth() + 1);
const day = time.getDate();
// Return 'today' or 'yesterday' if that is the case
if (
year === today.getFullYear() &&
month === addLeadingZero(today.getMonth() + 1) &&
day === today.getDate()
) {
return this.language.Today;
}
if (
year === yesterday.getFullYear() &&
month === addLeadingZero(yesterday.getMonth() + 1) &&
day === yesterday.getDate()
) {
return this.language.Yesterday;
}
if (
year === tomorrow.getFullYear() &&
month === addLeadingZero(tomorrow.getMonth() + 1) &&
day === tomorrow.getDate()
) {
return this.language.Tomorrow;
}
return this.createDateText({ day, month, time, today, year });
}
getHour(time) {
let ampm;
let hour = Number.parseInt(time.getHours(), 10);
if (this.LocalComments.twentyFourHours) {
ampm = '';
hour = addLeadingZero(hour);
} else {
// Output am or pm depending on the date.
ampm = hour <= 11 ? ' am' : ' pm';
if (hour > 12) {
hour -= 12;
} else if (hour === 0) {
hour = 12;
}
}
return { ampm, hour };
}
relativeText({ daysAgo, millisecondsAgo }) {
let differenceWord = '';
let last = '';
// The date is in the past.
if (millisecondsAgo >= 0) {
differenceWord = this.language.ago;
if (daysAgo <= 7) {
last = `${this.language.last} `;
}
// The date is in the future.
} else {
differenceWord = this.language['from now'];
if (daysAgo <= 7) {
last = `${this.language.this} `;
}
}
return { differenceWord, last };
}
replaceText(node, search) {
if (!node) {
return;
}
// Check if this is a text node.
if (node.nodeType === 3) {
// Don't continue if this text node's parent tag is one of these.
if (['CODE', 'PRE'].includes(node.parentNode.nodeName)) {
return;
}
const value = node.nodeValue;
const matches = value.match(search);
if (matches) {
// Only act on the first timestamp we found in this node. This is for
// the rare occassion that there is more than one timestamp in the
// same text node.
const [match] = matches;
const position = value.search(search);
const stringLength = match.toString().length;
// Grab the text content before and after the matching timestamp,
// which we'll then wrap in their own SPAN nodes.
const beforeMatch = value.slice(0, position);
const afterMatch = value.slice(position + stringLength);
const returnDate = this.adjustTime(match.toString(), search);
// Create the code to display the new local comments content.
const $span = $(
`<span class="localcomments" style="font-size: 95%;" title="${match}">${returnDate}</span>`
);
// Replace the existing text node in the page with our new local
// comments node.
$(node).replaceWith($span);
// Replace the text content that appears before the timestamp.
if (beforeMatch) {
$span.before(
`<span class="before-localcomments">${beforeMatch}</span>`
);
}
// Replace the text content that appears after the timestamp.
if (afterMatch) {
$span.after(
`<span class="after-localcomments">${afterMatch}</span>`
);
}
}
} else {
const children = [];
let child;
[child] = node.childNodes;
while (child) {
children.push(child);
child = child.nextSibling;
}
// Loop through children and run this func on it again, recursively.
children.forEach((child2) => {
this.replaceText(child2, search);
});
}
}
run() {
if (
['', 'MediaWiki', 'Special'].includes(
mw.config.get('wgCanonicalNamespace')
)
) {
return;
}
// Check for disabled URLs.
const isDisabledUrl = ['action=history'].some((disabledUrl) =>
document.location.href.includes(disabledUrl)
);
if (isDisabledUrl) {
return;
}
this.replaceText(
document.querySelector('.mw-body-content .mw-parser-output'),
/(\d{1,2}):(\d{2}), (\d{1,2}) ([A-Z][a-z]+) (\d{4}) \(UTC\)/
);
}
setDefaultSetting(...args) {
// There are no arguments.
if (args.length === 0) {
return false;
}
// The first arg is an object, so just set that data directly onto the
// settings object. like {setting 1: true, setting 2: false}
if (typeof args[0] === 'object') {
const [settings] = args;
// Loop through each setting.
Object.keys(settings).forEach((name) => {
const value = settings[name];
if (typeof this.LocalComments[name] === 'undefined') {
this.LocalComments[name] = value;
}
});
return settings;
}
// The first arg is a string, so use the first arg as the settings key,
// and the second arg as the value to set it to.
const [name, setting] = args;
if (typeof this.LocalComments[name] === 'undefined') {
this.LocalComments[name] = setting;
}
return this.LocalComments[name];
}
/**
* Set the script's settings.
*
* @returns {undefined}
*/
settings() {
// The user has set custom settings, so use those.
if (window.LocalComments) {
this.LocalComments = window.LocalComments;
}
/**
* Language
*
* LOCALIZING THIS SCRIPT
* To localize this script, change the terms below,
* to the RIGHT of the colons, to the correct term used in that language.
*
* For example, in the French language,
*
* 'Today' : 'Today',
*
* would be
*
* 'Today' : "Aujourd'hui",
*/
this.LocalComments.language = {
// Relative terms
Today: 'Today',
Yesterday: 'Yesterday',
Tomorrow: 'Tomorrow',
last: 'last',
this: 'this',
// Days of the week
Sunday: 'Sunday',
Monday: 'Monday',
Tuesday: 'Tuesday',
Wednesday: 'Wednesday',
Thursday: 'Thursday',
Friday: 'Friday',
Saturday: 'Saturday',
// Months of the year
January: 'January',
February: 'February',
March: 'March',
April: 'April',
May: 'May',
June: 'June',
July: 'July',
August: 'August',
September: 'September',
October: 'October',
November: 'November',
December: 'December',
// Difference words
ago: 'ago',
'from now': 'from now',
// Date phrases
year: 'year',
years: 'years',
month: 'month',
months: 'months',
day: 'day',
days: 'days',
};
}
}
// Check if we've already ran this script.
if (window.commentsInLocalTimeWasRun) {
return;
}
window.commentsInLocalTimeWasRun = true;
const commentsInLocalTime = new CommentsInLocalTime();
commentsInLocalTime.run();
});
13houlikxdhnop5vg42wvznssty7q8h
88931
88930
2026-08-06T23:15:47Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-CommentsInLocalTime.js]]
88930
javascript
text/javascript
// {{documentation}}
// _________________________________________________________________________________________
// | |
// | === WARNING: GLOBAL GADGET FILE === |
// | Changes to this page affect many users. |
// | Please discuss changes on the [[Wiktionary:Grease Pit]] before editing. |
// |_________________________________________________________________________________________|
//
//
// The content below are copied from https://en.wikipedia.org/w/index.php?title=User:Gary/comments_in_local_time.js&oldid=1103519446
// See [[w:User:Gary/comments_in_local_time.js]], [[w:Wikipedia:Comments in Local Time]]
// *********************************************************************************************************************************
/**
* COMMENTS IN LOCAL TIME
*
* Description:
* Changes [[Coordinated Universal Time|UTC]]-based times and dates,
* such as those used in signatures, to be relative to local time.
*
* Documentation:
* [[Wikipedia:Comments in Local Time]]
*/
$(() => {
/**
* Given a number, add a leading zero if necessary, so that the final number
* has two characters.
*
* @param {number} number Number
* @returns {string} The number with a leading zero, if necessary.
*/
function addLeadingZero(number) {
const numberArg = number;
if (numberArg < 10) {
return `0${numberArg}`;
}
return numberArg;
}
function convertMonthToNumber(month) {
return new Date(`${month} 1, 2001`).getMonth();
}
function getDates(time) {
const [, oldHour, oldMinute, oldDay, oldMonth, oldYear] = time;
// Today
const today = new Date();
// Yesterday
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
// Tomorrow
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
// Set the date entered.
const newTime = new Date();
newTime.setUTCFullYear(oldYear, convertMonthToNumber(oldMonth), oldDay);
newTime.setUTCHours(oldHour);
newTime.setUTCMinutes(oldMinute);
return { time: newTime, today, tomorrow, yesterday };
}
/**
* Determine whether to use the singular or plural word, and use that.
*
* @param {string} term Original term
* @param {number} count Count of items
* @param {string} plural Pluralized term
* @returns {string} The word to use
*/
function pluralize(term, count, plural = null) {
let pluralArg = plural;
// No unique pluralized word is found, so just use a general one.
if (!pluralArg) {
pluralArg = `${term}s`;
}
// There's only one item, so just use the singular word.
if (count === 1) {
return term;
}
// There are multiple items, so use the plural word.
return pluralArg;
}
class CommentsInLocalTime {
constructor() {
this.language = '';
this.LocalComments = {};
/**
* Settings
*/
this.settings();
this.language = this.setDefaultSetting(
'language',
this.LocalComments.language
);
// These values are also reflected in the documentation:
// https://en.wikipedia.org/wiki/Wikipedia:Comments_in_Local_Time#Default_settings
this.setDefaultSetting({
dateDifference: true,
dateFormat: 'dmy',
dayOfWeek: true,
dropDays: 0,
dropMonths: 0,
timeFirst: true,
twentyFourHours: false,
});
}
adjustTime(originalTimestamp, search) {
const { time, today, tomorrow, yesterday } = getDates(
originalTimestamp.match(search)
);
// A string matching the date pattern was found, but it cannot be
// converted to a Date object. Return it with no changes made.
if (Number.isNaN(time)) {
return [originalTimestamp, ''];
}
const date = this.determineDateText({
time,
today,
tomorrow,
yesterday,
});
const { ampm, hour } = this.getHour(time);
const minute = addLeadingZero(time.getMinutes());
const finalTime = `${hour}:${minute}${ampm}`;
// Determine the time offset.
const utcValue = (-1 * time.getTimezoneOffset()) / 60;
const utcOffset =
utcValue >= 0 ? `+${utcValue}` : `−${Math.abs(utcValue.toFixed(1))}`;
const utcPart = `(UTC${utcOffset})`;
const returnDate = this.LocalComments.timeFirst
? `${finalTime}, ${date} ${utcPart}`
: `${date}, ${finalTime} ${utcPart}`;
return returnDate;
}
convertNumberToMonth(number) {
return [
this.language.January,
this.language.February,
this.language.March,
this.language.April,
this.language.May,
this.language.June,
this.language.July,
this.language.August,
this.language.September,
this.language.October,
this.language.November,
this.language.December,
][number];
}
createDateText({ day, month, time, today, year }) {
// Calculate day of week
const dayNames = [
this.language.Sunday,
this.language.Monday,
this.language.Tuesday,
this.language.Wednesday,
this.language.Thursday,
this.language.Friday,
this.language.Saturday,
];
const dayOfTheWeek = dayNames[time.getDay()];
let descriptiveDifference = '';
let last = '';
// Create a relative descriptive difference
if (this.LocalComments.dateDifference) {
({ descriptiveDifference, last } = this.createRelativeDate(
today,
time
));
}
const monthName = this.convertNumberToMonth(time.getMonth());
// Format the date according to user preferences
let formattedDate = '';
switch (this.LocalComments.dateFormat.toLowerCase()) {
case 'dmy':
formattedDate = `${day} ${monthName} ${year}`;
break;
case 'mdy':
formattedDate = `${monthName} ${day}, ${year}`;
break;
default:
formattedDate = `${year}-${month}-${addLeadingZero(day)}`;
}
const formattedDayOfTheWeek = this.LocalComments.dayOfWeek
? `, ${last}${dayOfTheWeek}`
: '';
return `${formattedDate}${formattedDayOfTheWeek}${descriptiveDifference}`;
}
/**
* Create relative date data.
*
* @param {Date} today Today
* @param {Date} time The timestamp from a comment
* @returns {Object.<string, *>} Relative date data
*/
createRelativeDate(today, time) {
/**
* The time difference from today, in milliseconds.
*
* @type {number}
*/
const millisecondsAgo = today.getTime() - time.getTime();
/**
* The number of days ago, that we will display. It's not necessarily the
* total days ago.
*
* @type {number}
*/
let daysAgo = Math.abs(Math.round(millisecondsAgo / 1000 / 60 / 60 / 24));
const { differenceWord, last } = this.relativeText({
daysAgo,
millisecondsAgo,
});
// This method of computing the years and months is not exact. However,
// it's better than the previous method that used 1 January + delta days.
// That was usually quite off because it mapped the second delta month to
// February, which has only 28 days. This method is usually not more than
// one day off, except perhaps over very distant dates.
/**
* The number of months ago, that we will display. It's not necessarily
* the total months ago.
*
* @type {number}
*/
let monthsAgo = Math.floor((daysAgo / 365) * 12);
/**
* The total amount of time ago, in months.
*
* @type {number}
*/
const totalMonthsAgo = monthsAgo;
/**
* The number of years ago that we will display. It's not necessarily the
* total years ago.
*
* @type {number}
*/
let yearsAgo = Math.floor(totalMonthsAgo / 12);
if (totalMonthsAgo < this.LocalComments.dropMonths) {
yearsAgo = 0;
} else if (this.LocalComments.dropMonths > 0) {
monthsAgo = 0;
} else {
monthsAgo -= yearsAgo * 12;
}
if (daysAgo < this.LocalComments.dropDays) {
monthsAgo = 0;
yearsAgo = 0;
} else if (this.LocalComments.dropDays > 0 && totalMonthsAgo >= 1) {
daysAgo = 0;
} else {
daysAgo -= Math.floor((totalMonthsAgo * 365) / 12);
}
const descriptiveParts = [];
// There is years text to add.
if (yearsAgo > 0) {
descriptiveParts.push(
`${yearsAgo} ${pluralize(
this.language.year,
yearsAgo,
this.language.years
)}`
);
}
// There is months text to add.
if (monthsAgo > 0) {
descriptiveParts.push(
`${monthsAgo} ${pluralize(
this.language.month,
monthsAgo,
this.language.months
)}`
);
}
// There is days text to add.
if (daysAgo > 0) {
descriptiveParts.push(
`${daysAgo} ${pluralize(
this.language.day,
daysAgo,
this.language.days
)}`
);
}
return {
descriptiveDifference: ` (${descriptiveParts.join(
', '
)} ${differenceWord})`,
last,
};
}
determineDateText({ time, today, tomorrow, yesterday }) {
// Set the date bits to output.
const year = time.getFullYear();
const month = addLeadingZero(time.getMonth() + 1);
const day = time.getDate();
// Return 'today' or 'yesterday' if that is the case
if (
year === today.getFullYear() &&
month === addLeadingZero(today.getMonth() + 1) &&
day === today.getDate()
) {
return this.language.Today;
}
if (
year === yesterday.getFullYear() &&
month === addLeadingZero(yesterday.getMonth() + 1) &&
day === yesterday.getDate()
) {
return this.language.Yesterday;
}
if (
year === tomorrow.getFullYear() &&
month === addLeadingZero(tomorrow.getMonth() + 1) &&
day === tomorrow.getDate()
) {
return this.language.Tomorrow;
}
return this.createDateText({ day, month, time, today, year });
}
getHour(time) {
let ampm;
let hour = Number.parseInt(time.getHours(), 10);
if (this.LocalComments.twentyFourHours) {
ampm = '';
hour = addLeadingZero(hour);
} else {
// Output am or pm depending on the date.
ampm = hour <= 11 ? ' am' : ' pm';
if (hour > 12) {
hour -= 12;
} else if (hour === 0) {
hour = 12;
}
}
return { ampm, hour };
}
relativeText({ daysAgo, millisecondsAgo }) {
let differenceWord = '';
let last = '';
// The date is in the past.
if (millisecondsAgo >= 0) {
differenceWord = this.language.ago;
if (daysAgo <= 7) {
last = `${this.language.last} `;
}
// The date is in the future.
} else {
differenceWord = this.language['from now'];
if (daysAgo <= 7) {
last = `${this.language.this} `;
}
}
return { differenceWord, last };
}
replaceText(node, search) {
if (!node) {
return;
}
// Check if this is a text node.
if (node.nodeType === 3) {
// Don't continue if this text node's parent tag is one of these.
if (['CODE', 'PRE'].includes(node.parentNode.nodeName)) {
return;
}
const value = node.nodeValue;
const matches = value.match(search);
if (matches) {
// Only act on the first timestamp we found in this node. This is for
// the rare occassion that there is more than one timestamp in the
// same text node.
const [match] = matches;
const position = value.search(search);
const stringLength = match.toString().length;
// Grab the text content before and after the matching timestamp,
// which we'll then wrap in their own SPAN nodes.
const beforeMatch = value.slice(0, position);
const afterMatch = value.slice(position + stringLength);
const returnDate = this.adjustTime(match.toString(), search);
// Create the code to display the new local comments content.
const $span = $(
`<span class="localcomments" style="font-size: 95%;" title="${match}">${returnDate}</span>`
);
// Replace the existing text node in the page with our new local
// comments node.
$(node).replaceWith($span);
// Replace the text content that appears before the timestamp.
if (beforeMatch) {
$span.before(
`<span class="before-localcomments">${beforeMatch}</span>`
);
}
// Replace the text content that appears after the timestamp.
if (afterMatch) {
$span.after(
`<span class="after-localcomments">${afterMatch}</span>`
);
}
}
} else {
const children = [];
let child;
[child] = node.childNodes;
while (child) {
children.push(child);
child = child.nextSibling;
}
// Loop through children and run this func on it again, recursively.
children.forEach((child2) => {
this.replaceText(child2, search);
});
}
}
run() {
if (
['', 'MediaWiki', 'Special'].includes(
mw.config.get('wgCanonicalNamespace')
)
) {
return;
}
// Check for disabled URLs.
const isDisabledUrl = ['action=history'].some((disabledUrl) =>
document.location.href.includes(disabledUrl)
);
if (isDisabledUrl) {
return;
}
this.replaceText(
document.querySelector('.mw-body-content .mw-parser-output'),
/(\d{1,2}):(\d{2}), (\d{1,2}) ([A-Z][a-z]+) (\d{4}) \(UTC\)/
);
}
setDefaultSetting(...args) {
// There are no arguments.
if (args.length === 0) {
return false;
}
// The first arg is an object, so just set that data directly onto the
// settings object. like {setting 1: true, setting 2: false}
if (typeof args[0] === 'object') {
const [settings] = args;
// Loop through each setting.
Object.keys(settings).forEach((name) => {
const value = settings[name];
if (typeof this.LocalComments[name] === 'undefined') {
this.LocalComments[name] = value;
}
});
return settings;
}
// The first arg is a string, so use the first arg as the settings key,
// and the second arg as the value to set it to.
const [name, setting] = args;
if (typeof this.LocalComments[name] === 'undefined') {
this.LocalComments[name] = setting;
}
return this.LocalComments[name];
}
/**
* Set the script's settings.
*
* @returns {undefined}
*/
settings() {
// The user has set custom settings, so use those.
if (window.LocalComments) {
this.LocalComments = window.LocalComments;
}
/**
* Language
*
* LOCALIZING THIS SCRIPT
* To localize this script, change the terms below,
* to the RIGHT of the colons, to the correct term used in that language.
*
* For example, in the French language,
*
* 'Today' : 'Today',
*
* would be
*
* 'Today' : "Aujourd'hui",
*/
this.LocalComments.language = {
// Relative terms
Today: 'Today',
Yesterday: 'Yesterday',
Tomorrow: 'Tomorrow',
last: 'last',
this: 'this',
// Days of the week
Sunday: 'Sunday',
Monday: 'Monday',
Tuesday: 'Tuesday',
Wednesday: 'Wednesday',
Thursday: 'Thursday',
Friday: 'Friday',
Saturday: 'Saturday',
// Months of the year
January: 'January',
February: 'February',
March: 'March',
April: 'April',
May: 'May',
June: 'June',
July: 'July',
August: 'August',
September: 'September',
October: 'October',
November: 'November',
December: 'December',
// Difference words
ago: 'ago',
'from now': 'from now',
// Date phrases
year: 'year',
years: 'years',
month: 'month',
months: 'months',
day: 'day',
days: 'days',
};
}
}
// Check if we've already ran this script.
if (window.commentsInLocalTimeWasRun) {
return;
}
window.commentsInLocalTimeWasRun = true;
const commentsInLocalTime = new CommentsInLocalTime();
commentsInLocalTime.run();
});
13houlikxdhnop5vg42wvznssty7q8h
MediaWiki:Gadget-UTCLiveClock.js
8
17965
88932
2025-09-14T14:51:15Z
en>Surjection
0
allow override in case someone wants to
88932
javascript
text/javascript
// {{documentation}}
window.UTCLiveClockLocation = window.UTCLiveClockLocation || 'outside'; // see [[mw:MediaWiki:Gadget-UTCLiveClock]]
mw.loader.load('//www.mediawiki.org/w/index.php?title=MediaWiki:Gadget-UTCLiveClock.js&action=raw&ctype=text/javascript');
br5it9eznjp6c2wr62ib6dljeq06622
88933
88932
2026-08-06T23:15:57Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-UTCLiveClock.js]]
88932
javascript
text/javascript
// {{documentation}}
window.UTCLiveClockLocation = window.UTCLiveClockLocation || 'outside'; // see [[mw:MediaWiki:Gadget-UTCLiveClock]]
mw.loader.load('//www.mediawiki.org/w/index.php?title=MediaWiki:Gadget-UTCLiveClock.js&action=raw&ctype=text/javascript');
br5it9eznjp6c2wr62ib6dljeq06622
MediaWiki:Gadget-UTCLiveClock.css
8
17966
88934
2017-10-06T14:59:43Z
en>Gamren
0
Created page with "/** * Explicitly set width of the UTC-clock list element, so that we can use a * hidden peer gadget to add space where the clock would go before it loads. */ .skin-vector #..."
88934
css
text/css
/**
* Explicitly set width of the UTC-clock list element, so that we can use a
* hidden peer gadget to add space where the clock would go before it loads.
*/
.skin-vector #utcdate {
width: 6em;
/*
* The default margin-left is 0.75em, but set it again here explicitly, so
* we can be sure of the exact width.
*/
margin-left: 0.75em;
}
.skin-monobook #utcdate {
width: 6.279em;
display: inline-block; /* We need this for the width property to work */
/*
* The default margin-left is 1em, but set it again here explicitly, so
* we can be sure of the exact width.
*/
margin-left: 1em;
}
a9b5uwepu2o3etikfxjo5stx2yqmdz4
88935
88934
2026-08-06T23:16:06Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-UTCLiveClock.css]]
88934
css
text/css
/**
* Explicitly set width of the UTC-clock list element, so that we can use a
* hidden peer gadget to add space where the clock would go before it loads.
*/
.skin-vector #utcdate {
width: 6em;
/*
* The default margin-left is 0.75em, but set it again here explicitly, so
* we can be sure of the exact width.
*/
margin-left: 0.75em;
}
.skin-monobook #utcdate {
width: 6.279em;
display: inline-block; /* We need this for the width property to work */
/*
* The default margin-left is 1em, but set it again here explicitly, so
* we can be sure of the exact width.
*/
margin-left: 1em;
}
a9b5uwepu2o3etikfxjo5stx2yqmdz4
MediaWiki:Gadget-WiktSidebarTranslation.js
8
17967
88936
2017-08-17T06:16:09Z
en>Dixtosa
0
88936
javascript
text/javascript
$(function(){
$("#p-lang .interlanguage-link a").each(function(){
var langnameMatch = $(this).attr("title").match(/(.*) – (.*)/);
var langname = $(this).attr("title"); //some wiktionaries like afrikaans have '' as their main page
if (langnameMatch && langnameMatch.length >= 3) langname = langnameMatch[2];
$(this).text(langname)
.attr("lang", "en");//this line breaks compact languages
});
$("#p-lang .interlanguage-link").sort(function(lia, lib){
return $(lia).children().first().text() < $(lib).children().first().text() ? -1 : 1;
}).appendTo("#p-lang>div>ul");
});
0l3tsev12k2w1jgnrnjj728cjr5knji
88937
88936
2026-08-06T23:16:14Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-WiktSidebarTranslation.js]]
88936
javascript
text/javascript
$(function(){
$("#p-lang .interlanguage-link a").each(function(){
var langnameMatch = $(this).attr("title").match(/(.*) – (.*)/);
var langname = $(this).attr("title"); //some wiktionaries like afrikaans have '' as their main page
if (langnameMatch && langnameMatch.length >= 3) langname = langnameMatch[2];
$(this).text(langname)
.attr("lang", "en");//this line breaks compact languages
});
$("#p-lang .interlanguage-link").sort(function(lia, lib){
return $(lia).children().first().text() < $(lib).children().first().text() ? -1 : 1;
}).appendTo("#p-lang>div>ul");
});
0l3tsev12k2w1jgnrnjj728cjr5knji
MediaWiki:Gadget-TreeDescs-pagestyles.css
8
17968
88938
2025-11-18T19:48:01Z
en>Surjection
0
Surjection changed the content model of the page [[MediaWiki:Gadget-TreeDescs-pagestyles.css]] from "Sanitized CSS" to "CSS": gadgets cannot use sanitized CSS
88938
css
text/css
/* code forked from [[w:Template:Tree list/styles.css]] */
.treeview ul {
padding: 0;
margin: 0;
}
.treeview li li {
margin: 0;
list-style-type: none;
list-style-image: none;
background: url("https://upload.wikimedia.org/wikipedia/commons/f/f2/Treeview-grey-line.png") no-repeat 0 -2979.5px;
/* @noflip */
padding: 0 0 0 21px;
text-indent: 0.3em;
}
.treeview li li:last-child {
background-position: 0 -5969.5px
}
/* first line here deals with new situation after RemexHTML switch,
* where emptyline cause the first child to become the 2nd child. Such a mess
*/
.treeview li.emptyline > ul > .mw-empty-elt:first-child + .emptyline,
.treeview li.emptyline > ul > li:first-child {
background-position: 0 9px
}
/* additional code for Wiktionary */
.treeview li dd {
margin: 0;
}
.treeview li dl {
margin: 0;
padding-left: 21px;
}
.treeview li dl:not(:last-child) {
background: url("https://upload.wikimedia.org/wikipedia/commons/f/f2/Treeview-grey-line.png") no-repeat 0 -2px;
/* @noflip */
padding-left: 21px;
text-indent: 0.3em;
}
.treeview .desc-arr {
color: var(--wikt-palette-grey-9, grey);
}
.treeview li li .desc-arr {
vertical-align: top;
}
lz282qet5qzkmuxcslusjro6a7akd85
88939
88938
2026-08-06T23:16:52Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-TreeDescs-pagestyles.css]]
88938
css
text/css
/* code forked from [[w:Template:Tree list/styles.css]] */
.treeview ul {
padding: 0;
margin: 0;
}
.treeview li li {
margin: 0;
list-style-type: none;
list-style-image: none;
background: url("https://upload.wikimedia.org/wikipedia/commons/f/f2/Treeview-grey-line.png") no-repeat 0 -2979.5px;
/* @noflip */
padding: 0 0 0 21px;
text-indent: 0.3em;
}
.treeview li li:last-child {
background-position: 0 -5969.5px
}
/* first line here deals with new situation after RemexHTML switch,
* where emptyline cause the first child to become the 2nd child. Such a mess
*/
.treeview li.emptyline > ul > .mw-empty-elt:first-child + .emptyline,
.treeview li.emptyline > ul > li:first-child {
background-position: 0 9px
}
/* additional code for Wiktionary */
.treeview li dd {
margin: 0;
}
.treeview li dl {
margin: 0;
padding-left: 21px;
}
.treeview li dl:not(:last-child) {
background: url("https://upload.wikimedia.org/wikipedia/commons/f/f2/Treeview-grey-line.png") no-repeat 0 -2px;
/* @noflip */
padding-left: 21px;
text-indent: 0.3em;
}
.treeview .desc-arr {
color: var(--wikt-palette-grey-9, grey);
}
.treeview li li .desc-arr {
vertical-align: top;
}
lz282qet5qzkmuxcslusjro6a7akd85
MediaWiki:Gadget-ShowIDs-pagestyles.css
8
17969
88940
2026-05-15T14:39:51Z
en>Fenakhay
0
Remove duplication
88940
css
text/css
.senseid, .etymid,
.senseid-displayed, .etymid-displayed {
--id-border: var(--wikt-palette-blue-6);
--id-background: var(--wikt-palette-blue-1);
--id-background-target: var(--wikt-palette-blue-4);
}
.etymonid, .etymonid-displayed {
--id-border: var(--wikt-palette-red-6);
--id-background: var(--wikt-palette-red-1);
--id-background-target: var(--wikt-palette-red-4);
}
.etymid, .etymonid {
margin-left: 0;
padding: 0 !important;
display: inline;
}
.senseid-displayed, .etymid-displayed, .etymonid-displayed,
.senseid::before, .etymid::before, .etymonid::before {
border: 1px solid var(--id-border);
background: var(--id-background);
color: var(--wikt-palette-black);
border-radius: 5px;
font-size: 90%;
margin-right: 0.1em;
padding: 0.2em !important;
content: attr(data-id);
}
.senseid-displayed:target, .etymid-displayed:target, .etymonid-displayed:target,
.senseid:target .senseid-displayed, .etymid:target .etymid-displayed, .etymonid:target .etymonid-displayed,
.senseid:target::before, .etymid:target::before, .etymonid:target::before {
background: var(--id-background-target);
font-weight: 600;
}
.senseid:not([data-id])::before, .etymid:not([data-id])::before, .etymonid:not([data-id])::before,
.senseid[data-id=""]::before, .etymid[data-id=""]::before, .etymonid[data-id=""]::before {
content: none !important;
display: none !important;
}
9c32gfnz5l4kzwwbspg704ebxwivhou
88941
88940
2026-08-06T23:17:01Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-ShowIDs-pagestyles.css]]
88940
css
text/css
.senseid, .etymid,
.senseid-displayed, .etymid-displayed {
--id-border: var(--wikt-palette-blue-6);
--id-background: var(--wikt-palette-blue-1);
--id-background-target: var(--wikt-palette-blue-4);
}
.etymonid, .etymonid-displayed {
--id-border: var(--wikt-palette-red-6);
--id-background: var(--wikt-palette-red-1);
--id-background-target: var(--wikt-palette-red-4);
}
.etymid, .etymonid {
margin-left: 0;
padding: 0 !important;
display: inline;
}
.senseid-displayed, .etymid-displayed, .etymonid-displayed,
.senseid::before, .etymid::before, .etymonid::before {
border: 1px solid var(--id-border);
background: var(--id-background);
color: var(--wikt-palette-black);
border-radius: 5px;
font-size: 90%;
margin-right: 0.1em;
padding: 0.2em !important;
content: attr(data-id);
}
.senseid-displayed:target, .etymid-displayed:target, .etymonid-displayed:target,
.senseid:target .senseid-displayed, .etymid:target .etymid-displayed, .etymonid:target .etymonid-displayed,
.senseid:target::before, .etymid:target::before, .etymonid:target::before {
background: var(--id-background-target);
font-weight: 600;
}
.senseid:not([data-id])::before, .etymid:not([data-id])::before, .etymonid:not([data-id])::before,
.senseid[data-id=""]::before, .etymid[data-id=""]::before, .etymonid[data-id=""]::before {
content: none !important;
display: none !important;
}
9c32gfnz5l4kzwwbspg704ebxwivhou
MediaWiki:Gadget-WiktGadgetPrefs.js
8
17970
88942
2025-09-14T16:29:49Z
en>Ioaxxere
0
docs should encourage good style
88942
javascript
text/javascript
/* global mw */
// <nowiki>
/*jshint shadow:true, undef:true, latedef:true, unused:true, esversion:3 */
/**
* WiktGadgetPrefs -- developed by [[wikt:en:User:Surjection]]
*
* ***********************************************************************
* Documentation for gadget authors:
* Example usage (remember to add ext.gadget.WiktGadgetPrefs as a dependency):
const preferences = mw.wiktGadgetPrefs.get("gadgetfoobar",
{
"label": {
"en": "Foobar Gadget"
}
},
{
"frobulate": {
"type": "boolean",
"default": false,
"label": {
"en": "Whether this gadget should frobulate"
}
}
}
);
const shouldFrobulate = preferences.frobulate;
// strenum example
const preferences = mw.wiktGadgetPrefs.get("gadgetfoobar",
{
"label": {
"en": "Foobar Gadget"
}
},
{
"frobulate": {
"type": "strenum",
"default": "yes",
"label": {
"en": "Whether this gadget should frobulate"
},
"choices": [
"yes",
"no",
"maybe"
],
"choiceLabels": {
"en": {
"yes": "Yes!",
"no": "No!!",
"maybe": "Maybe?"
}
}
}
}
);
const shouldFrobulate = preferences.frobulate;
***********************************************************************
*
* Dependencies needed: mw.user, user.options
*/
(function() {
"use strict";
/**
* @typedef WiktGadgetOptions
* - Represents gadget options.
* @property {{[languageCode: string]: string}} [label]
* - The label to display on the preferences page for this
* gadget/namespace. The language code should be a MediaWiki
* language code, e.g. "en" for English.
*/
/**
* @typedef WiktGadgetPreference
* - Represents a single preference.
* @property {{"boolean"|"integer"|"string"|"strenum"|"object"}} type
* - Must be one of the following:
* - "boolean" for boolean values, either true or false.
* - "integer" for an integer value.
* - "string" for a string value.
* - "strenum" for a string value that must be one of the options.
* - "object" for an arbitrary JSON value, including null.
* No type checks are done.
* @property {any} default
* - The default value for this preference if missing, or if
* the value is invalid.
* - The developer is responsible for ensuring that this value is
* given, and that it meets the preconditions defined below.
* @property {string[]} [choices]
* - If the type is "strenum", then this must be an array of
* allowed options. If the value is not in this list, the default
* value will be returned.
* @property {number} [minimum]
* - If the type is "integer", this is the minimum allowed value.
* @property {number} [maximum]
* - If the type is "integer", this is the maximum allowed value.
* @property {number} [maximumLength]
* - If the type is "string", this is the maximum allowed length.
* @property {{[languageCode: string]: string}} [label]
* - The label to display on the preferences page for this
* preference. The language code should be a MediaWiki
* language code, e.g. "en" for English.
* @property {{[languageCode: string]: {[choice: string]: string}}} [choiceLabels]
* - The label to display on the preferences page for the choices
* of this preference (if it is an enum type). The language code
* should be a MediaWiki language code, e.g. "en" for English.
*/
var wiktGadgetPrefs__registeredPrefs = {};
var wiktGadgetPrefs__error = (Error ?
function (message) {
return new Error(message);
} :
function (message) {
return message;
});
var wiktGadgetPrefs__asciify = function (text) {
return text.replace(/[^A-Za-z0-9-]/g, function (match) {
if (match == "_") {
return "__";
} else {
return "_" + match.charCodeAt(0).toString(16) + "_";
}
});
};
var wiktGadgetPrefs__namespace_to_key = function (namespace) {
return "wiktgadgetprefs-" + wiktGadgetPrefs__asciify(namespace);
};
var wiktGadgetPrefs__is_integer = function (num) {
return num == (num | 0);
};
var wiktGadgetPrefs__parse_one = function (value, preference, key) {
var defaultValue = preference["default"];
switch (preference.type) {
case "boolean":
// data type
if (typeof value != "boolean") return defaultValue;
return value;
case "integer":
// data type
if (typeof value != "number") return defaultValue;
// integer check
if (!wiktGadgetPrefs__is_integer(value)) return defaultValue;
// constraints
if ((preference.minimum != null && value < preference.minimum) ||
(preference.maximum != null && value > preference.maximum))
return defaultValue;
return value;
case "string":
// data type
if (typeof value != "string") return defaultValue;
// constraints
if (preference.maximumLength != null && value.length > preference.maximumLength)
return defaultValue;
return value;
case "strenum":
// data type
if (typeof value != "string") return defaultValue;
// constraints
if (preference.choices.indexOf(value) < 0)
return defaultValue;
return value;
case "object":
if (typeof value == "undefined") return defaultValue;
return value;
}
throw wiktGadgetPrefs__error("Invalid preference specification '" + key + "'");
};
var wiktGadgetPrefs__parse_all = function (values, preferences) {
var parsed = {};
if (!values) values = {};
for (var key in preferences) {
if (preferences.hasOwnProperty(key)) {
parsed[key] = wiktGadgetPrefs__parse_one(values[key], preferences[key], key);
}
}
return parsed;
};
var wiktGadgetPrefs__resolve = function (namespaceKey, preferences) {
var resolved;
var mwOptions;
try {
mwOptions = mw.user.options;
} catch (e) {
}
if (!resolved && window.localStorage && window.localStorage.getItem("anon" + namespaceKey) != null) {
try {
resolved = JSON.parse(window.localStorage.getItem("anon" + namespaceKey));
} catch (e) { }
}
if (!resolved && mwOptions && mwOptions.exists("userjs-" + namespaceKey)) {
try {
resolved = JSON.parse(mwOptions.get("userjs-" + namespaceKey));
} catch (e) { }
}
return wiktGadgetPrefs__parse_all(resolved || {}, preferences);
};
/**
* Resolve preferences.
*
* Note that there is a maximum length imposed by MediaWiki
* on the total size of the preferences for each namespace.
*
* @param {string} namespace
* - An arbitrary string. This should be unique to each gadget.
* @param {WiktGadgetOptions} options
* - Options.
* @param {{[key: string]: WiktGadgetPreference}} preferences
* - Requested preferences, indexed by name.
* @returns {{[key: string]: any}}
*/
var wiktGadgetPrefs_get = function (namespace, options, preferences) {
if (wiktGadgetPrefs__registeredPrefs[namespace]) {
throw wiktGadgetPrefs__error("wiktGadgetPrefs_get has already been called with the namespace '" + namespace + "'.");
}
wiktGadgetPrefs__registeredPrefs[namespace] = {
options: options,
preferences: preferences
};
var namespaceKey = wiktGadgetPrefs__namespace_to_key(namespace);
if (namespaceKey.length >= 240) {
throw wiktGadgetPrefs__error("Namespace key '" + namespace + "' is too long. " +
"Please prefer ASCII characters A-Z, a-z, 0-9 and the hyphen, " +
"as other characters need to be converted, which increases their size.");
}
return wiktGadgetPrefs__resolve(namespaceKey, preferences);
};
mw.wiktGadgetPrefs = {};
mw.wiktGadgetPrefs.get = wiktGadgetPrefs_get;
// Only to be used by the preferences page.
mw.wiktGadgetPrefs._namespaceToKey = wiktGadgetPrefs__namespace_to_key;
mw.wiktGadgetPrefs._resolve = wiktGadgetPrefs__resolve;
mw.wiktGadgetPrefs._registeredPrefs = wiktGadgetPrefs__registeredPrefs;
})();
// </nowiki>
o4n21hnlfqc4ly11ntzob901aoo3w3p
88943
88942
2026-08-06T23:17:09Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-WiktGadgetPrefs.js]]
88942
javascript
text/javascript
/* global mw */
// <nowiki>
/*jshint shadow:true, undef:true, latedef:true, unused:true, esversion:3 */
/**
* WiktGadgetPrefs -- developed by [[wikt:en:User:Surjection]]
*
* ***********************************************************************
* Documentation for gadget authors:
* Example usage (remember to add ext.gadget.WiktGadgetPrefs as a dependency):
const preferences = mw.wiktGadgetPrefs.get("gadgetfoobar",
{
"label": {
"en": "Foobar Gadget"
}
},
{
"frobulate": {
"type": "boolean",
"default": false,
"label": {
"en": "Whether this gadget should frobulate"
}
}
}
);
const shouldFrobulate = preferences.frobulate;
// strenum example
const preferences = mw.wiktGadgetPrefs.get("gadgetfoobar",
{
"label": {
"en": "Foobar Gadget"
}
},
{
"frobulate": {
"type": "strenum",
"default": "yes",
"label": {
"en": "Whether this gadget should frobulate"
},
"choices": [
"yes",
"no",
"maybe"
],
"choiceLabels": {
"en": {
"yes": "Yes!",
"no": "No!!",
"maybe": "Maybe?"
}
}
}
}
);
const shouldFrobulate = preferences.frobulate;
***********************************************************************
*
* Dependencies needed: mw.user, user.options
*/
(function() {
"use strict";
/**
* @typedef WiktGadgetOptions
* - Represents gadget options.
* @property {{[languageCode: string]: string}} [label]
* - The label to display on the preferences page for this
* gadget/namespace. The language code should be a MediaWiki
* language code, e.g. "en" for English.
*/
/**
* @typedef WiktGadgetPreference
* - Represents a single preference.
* @property {{"boolean"|"integer"|"string"|"strenum"|"object"}} type
* - Must be one of the following:
* - "boolean" for boolean values, either true or false.
* - "integer" for an integer value.
* - "string" for a string value.
* - "strenum" for a string value that must be one of the options.
* - "object" for an arbitrary JSON value, including null.
* No type checks are done.
* @property {any} default
* - The default value for this preference if missing, or if
* the value is invalid.
* - The developer is responsible for ensuring that this value is
* given, and that it meets the preconditions defined below.
* @property {string[]} [choices]
* - If the type is "strenum", then this must be an array of
* allowed options. If the value is not in this list, the default
* value will be returned.
* @property {number} [minimum]
* - If the type is "integer", this is the minimum allowed value.
* @property {number} [maximum]
* - If the type is "integer", this is the maximum allowed value.
* @property {number} [maximumLength]
* - If the type is "string", this is the maximum allowed length.
* @property {{[languageCode: string]: string}} [label]
* - The label to display on the preferences page for this
* preference. The language code should be a MediaWiki
* language code, e.g. "en" for English.
* @property {{[languageCode: string]: {[choice: string]: string}}} [choiceLabels]
* - The label to display on the preferences page for the choices
* of this preference (if it is an enum type). The language code
* should be a MediaWiki language code, e.g. "en" for English.
*/
var wiktGadgetPrefs__registeredPrefs = {};
var wiktGadgetPrefs__error = (Error ?
function (message) {
return new Error(message);
} :
function (message) {
return message;
});
var wiktGadgetPrefs__asciify = function (text) {
return text.replace(/[^A-Za-z0-9-]/g, function (match) {
if (match == "_") {
return "__";
} else {
return "_" + match.charCodeAt(0).toString(16) + "_";
}
});
};
var wiktGadgetPrefs__namespace_to_key = function (namespace) {
return "wiktgadgetprefs-" + wiktGadgetPrefs__asciify(namespace);
};
var wiktGadgetPrefs__is_integer = function (num) {
return num == (num | 0);
};
var wiktGadgetPrefs__parse_one = function (value, preference, key) {
var defaultValue = preference["default"];
switch (preference.type) {
case "boolean":
// data type
if (typeof value != "boolean") return defaultValue;
return value;
case "integer":
// data type
if (typeof value != "number") return defaultValue;
// integer check
if (!wiktGadgetPrefs__is_integer(value)) return defaultValue;
// constraints
if ((preference.minimum != null && value < preference.minimum) ||
(preference.maximum != null && value > preference.maximum))
return defaultValue;
return value;
case "string":
// data type
if (typeof value != "string") return defaultValue;
// constraints
if (preference.maximumLength != null && value.length > preference.maximumLength)
return defaultValue;
return value;
case "strenum":
// data type
if (typeof value != "string") return defaultValue;
// constraints
if (preference.choices.indexOf(value) < 0)
return defaultValue;
return value;
case "object":
if (typeof value == "undefined") return defaultValue;
return value;
}
throw wiktGadgetPrefs__error("Invalid preference specification '" + key + "'");
};
var wiktGadgetPrefs__parse_all = function (values, preferences) {
var parsed = {};
if (!values) values = {};
for (var key in preferences) {
if (preferences.hasOwnProperty(key)) {
parsed[key] = wiktGadgetPrefs__parse_one(values[key], preferences[key], key);
}
}
return parsed;
};
var wiktGadgetPrefs__resolve = function (namespaceKey, preferences) {
var resolved;
var mwOptions;
try {
mwOptions = mw.user.options;
} catch (e) {
}
if (!resolved && window.localStorage && window.localStorage.getItem("anon" + namespaceKey) != null) {
try {
resolved = JSON.parse(window.localStorage.getItem("anon" + namespaceKey));
} catch (e) { }
}
if (!resolved && mwOptions && mwOptions.exists("userjs-" + namespaceKey)) {
try {
resolved = JSON.parse(mwOptions.get("userjs-" + namespaceKey));
} catch (e) { }
}
return wiktGadgetPrefs__parse_all(resolved || {}, preferences);
};
/**
* Resolve preferences.
*
* Note that there is a maximum length imposed by MediaWiki
* on the total size of the preferences for each namespace.
*
* @param {string} namespace
* - An arbitrary string. This should be unique to each gadget.
* @param {WiktGadgetOptions} options
* - Options.
* @param {{[key: string]: WiktGadgetPreference}} preferences
* - Requested preferences, indexed by name.
* @returns {{[key: string]: any}}
*/
var wiktGadgetPrefs_get = function (namespace, options, preferences) {
if (wiktGadgetPrefs__registeredPrefs[namespace]) {
throw wiktGadgetPrefs__error("wiktGadgetPrefs_get has already been called with the namespace '" + namespace + "'.");
}
wiktGadgetPrefs__registeredPrefs[namespace] = {
options: options,
preferences: preferences
};
var namespaceKey = wiktGadgetPrefs__namespace_to_key(namespace);
if (namespaceKey.length >= 240) {
throw wiktGadgetPrefs__error("Namespace key '" + namespace + "' is too long. " +
"Please prefer ASCII characters A-Z, a-z, 0-9 and the hyphen, " +
"as other characters need to be converted, which increases their size.");
}
return wiktGadgetPrefs__resolve(namespaceKey, preferences);
};
mw.wiktGadgetPrefs = {};
mw.wiktGadgetPrefs.get = wiktGadgetPrefs_get;
// Only to be used by the preferences page.
mw.wiktGadgetPrefs._namespaceToKey = wiktGadgetPrefs__namespace_to_key;
mw.wiktGadgetPrefs._resolve = wiktGadgetPrefs__resolve;
mw.wiktGadgetPrefs._registeredPrefs = wiktGadgetPrefs__registeredPrefs;
})();
// </nowiki>
o4n21hnlfqc4ly11ntzob901aoo3w3p
MediaWiki:Gadget-InflectionTable.css
8
17971
88944
2026-08-01T14:08:56Z
en>Fenakhay
0
Fenakhay moved page [[Template:inflection-table-top/style.css]] to [[MediaWiki:Gadget-InflectionTable.css]] without leaving a redirect
88944
sanitized-css
text/css
/**
* This CSS page provides styling for the {{inflection-table-top}} family of templates.
*
* The style rules are complex and have various dependencies on one another.
* They have been comprehensively tested on desktop and mobile.
* Take care before making any changes. If you're not confident with CSS,
* consider requesting help from an interface administrator.
*/
.inflection-table-wrapper {
padding: 3px;
margin: 0.5em 0;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-grey);
position: relative;
max-width: 100%;
box-sizing: border-box;
overflow-x: auto;
}
.inflection-table-wrapper.inflection-table-collapsed {
overflow: hidden;
}
.inflection-table-wrapper.inflection-table-no-title {
border: 0;
padding: 0;
}
.inflection-table-wrapper > table {
display: table;
width: 100%;
margin: 0;
text-align: center;
border-collapse: collapse;
border: 1px solid var(--wikt-palette-grey-6);
background: var(--wikt-palette-white);
}
.inflection-table-wrapper > table > caption {
display: table-caption;
background: var(--wikt-palette-paleblue);
text-align: left;
font-size: 95%;
padding: 0.1em 0.5em;
font-weight: bold;
}
.inflection-table-wrapper.wide > table > tbody > tr {
font-size: 90%;
}
.inflection-table-wrapper > table > tbody > tr > td,
.inflection-table-wrapper > table > tbody > tr > th {
padding: 0.15em 0.6em;
border: solid var(--wikt-palette-grey-6);
border-width: 0 1px 1px 0;
}
.inflection-table-wrapper > table > tbody > tr > td:not(.secondary):not(.separator) {
min-width: 5em;
}
/* very-narrow is for tables of unusually short words (e.g. pronouns). The :not
selectors are styling-wise unnecessary but must stay: they give this rule the
specificity it needs to beat the previous one. */
.inflection-table-wrapper.very-narrow > table > tbody > tr > td:not(.secondary):not(.separator) {
min-width: auto;
}
.inflection-table-wrapper > table > tbody > tr > td.secondary {
background: var(--wikt-palette-lightergrey);
}
.inflection-table-wrapper > table > tbody > tr > th.secondary,
.inflection-table-wrapper > table > tbody > tr > th.outer {
font-style: italic;
}
.inflection-table-wrapper > table > tbody > tr > th.secondary > sup,
.inflection-table-wrapper > table > tbody > tr > th.outer > sup {
font-style: normal;
}
/* separator and blank cells: see [[Template:la-adecl]], [[Template:ga-decl-noun]] */
.inflection-table-wrapper .separator {
height: 0.5em;
width: 0.5em;
min-height: 0.5em;
min-width: 0.5em;
padding: 0;
background: var(--wikt-palette-paleblue);
}
.inflection-table-wrapper .separator[rowspan] {
border-top: 1px solid var(--wikt-palette-paleblue);
border-bottom: 1px solid var(--wikt-palette-paleblue);
}
.inflection-table-wrapper .separator[colspan] {
border-left: 1px solid var(--wikt-palette-paleblue);
border-right: 1px solid var(--wikt-palette-paleblue);
}
/* blank cell at end of row: see [[Template:sco-conj-be]] */
.inflection-table-wrapper .blank-end-row {
background: var(--wikt-palette-paleblue);
border-right: 1px solid var(--wikt-palette-paleblue);
border-top: 1px solid var(--wikt-palette-paleblue);
border-bottom: 1px solid var(--wikt-palette-paleblue);
}
/* Notes at foot of table */
.inflection-table-notes {
text-align: left;
margin: 0.2em 0.5em 0.2em;
}
.inflection-table-notes p {
margin: 0.5em 0 0.2em;
}
.inflection-table-wrapper.wide > .inflection-table-notes {
font-size: 90%;
}
/* Collapsibility */
html.client-js .inflection-table-collapsed > table,
html.client-js .inflection-table-collapsed > table > tbody > tr > td,
html.client-js .inflection-table-collapsed > table > tbody > tr > th {
border-color: transparent;
}
html.client-js .inflection-table-collapsed > table > tbody {
visibility: collapse; /* not implemented in Safari - see the .no-vc fallback below */
}
html.client-js .inflection-table-collapsed > table > caption {
margin-bottom: -0.15em; /* hack: trims excess height from the collapsed box */
}
html.client-js .inflection-table-collapsed .inflection-table-notes {
display: none;
}
/* Fallback for browsers without visibility: collapse. JS removes .no-vc when unneeded. */
html.client-js .inflection-table-collapsible.no-vc.inflection-table-collapsed {
max-height: calc(1.5em + 8px); /* almost 1lh (= 1.6em) + wrapper padding and border */
overflow: hidden;
}
html.client-js .inflection-table-collapsible.no-vc.inflection-table-collapsed > table > tbody {
visibility: hidden;
}
html.client-js .inflection-table-collapsible.no-vc > table > caption {
white-space: nowrap; /* a second line would be occluded by the max-height above */
}
.inflection-table-wrapper .no-vc-spacer {
display: inline-block;
width: 4em;
}
/* keeps the [hide] link from scrolling away when the table is uncollapsed */
.inflection-table-wrapper .NavToggle {
position: sticky;
right: 0.5em; /* matches padding on <caption> */
background: inherit;
}
/* Single border display style - EXPERIMENTAL! */
.inflection-table-wrapper.single-border {
padding: 0;
border: 0;
}
.inflection-table-wrapper.single-border > table > caption {
border: 1px solid var(--wikt-palette-grey);
border-bottom: 0;
}
html.client-js .inflection-table-collapsed.single-border > table > caption {
border-bottom: 1px solid var(--wikt-palette-grey);
}
.inflection-table-wrapper.inflection-table-collapsible.single-border .inflection-table-notes {
border: 1px solid var(--wikt-palette-grey);
border-top: 0;
}
/* Mobile styling */
@media all and (max-width: 639px) { /* matches calc(640px - 1px) in Minerva CSS */
.inflection-table-wrapper {
overflow: auto;
max-width: 100%;
width: auto !important; /* overrides the inline style that works around a CSS sanitiser lacuna; !important is required to win */
}
.inflection-table-collapsed tbody {
display: none;
}
.inflection-table-wrapper .NavToggle {
position: sticky;
right: 0.5em;
background: inherit;
}
/* stick a single left-hand column of header cells in place */
.inflection-table-wrapper.sticky-left-column > table > tbody > tr > th:first-child {
position: sticky;
left: -4px; /* counteracts 3px wrapper padding + 1px table border */
max-width: 35vw;
}
.inflection-table-wrapper.inflection-table-no-title.sticky-left-column > table > tbody > tr > th:first-child {
left: -1px; /* counteracts 1px table border */
}
}
/* Transliterations: paler, on their own line */
.inflection-table-wrapper:not(.tr-alongside) table > tbody td span.tr {
color: var(--wikt-palette-grey-9); /* TODO slightly below AAA contrast on a -0 background */
}
.inflection-table-wrapper:not(.tr-alongside) table > tbody td span.tr:before {
content: "\a"; /* newline */
white-space: pre;
}
.inflection-table-wrapper:not(.tr-alongside) table > tbody td span.mention-gloss-paren {
display: none;
}
/* Inflection boxes - multiple tables inside one box */
.inflection-box > table {
background: none;
border: 0;
}
.inflection-box > table > tbody > tr > td {
padding: 0 0.2em;
border: 0;
vertical-align: top;
}
.inflection-box .inflection-table-wrapper {
display: inline-block;
vertical-align: top;
margin: 0.2em 0.1em;
}
/* overqualified on purpose, to beat the no-title palette rule below */
.inflection-box .inflection-table-wrapper > table.inflection-table {
background: var(--wikt-palette-white);
}
.inflection-box.flow-vertical .inflection-table-wrapper {
display: block;
width: auto !important; /* see the note on the mobile rule above */
}
.inflection-box.flow-vertical .inflection-table-wrapper + .inflection-table-wrapper {
margin-top: 0.5em;
}
/* Palettes: dark-on-light */
.inflection-table-wrapper.inflection-table-no-title > table {
background-color: var(--wikt-p-0);
}
.inflection-table tr.secondary > th,
.inflection-table th.secondary {
background-color: var(--wikt-p-1);
}
.inflection-table th {
background-color: var(--wikt-p-2);
}
.inflection-table tr.outer > th,
.inflection-table th.outer {
background-color: var(--wikt-p-4);
}
.inflection-table tr.darker > td,
.inflection-table td.darker {
background-color: var(--wikt-p-g-1) !important; /* !important beats the more specific td.secondary rule */
}
nyqjz103vuorcxh3kh6hiehkkfh889b
88945
88944
2026-08-06T23:17:29Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-InflectionTable.css]]
88944
sanitized-css
text/css
/**
* This CSS page provides styling for the {{inflection-table-top}} family of templates.
*
* The style rules are complex and have various dependencies on one another.
* They have been comprehensively tested on desktop and mobile.
* Take care before making any changes. If you're not confident with CSS,
* consider requesting help from an interface administrator.
*/
.inflection-table-wrapper {
padding: 3px;
margin: 0.5em 0;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-grey);
position: relative;
max-width: 100%;
box-sizing: border-box;
overflow-x: auto;
}
.inflection-table-wrapper.inflection-table-collapsed {
overflow: hidden;
}
.inflection-table-wrapper.inflection-table-no-title {
border: 0;
padding: 0;
}
.inflection-table-wrapper > table {
display: table;
width: 100%;
margin: 0;
text-align: center;
border-collapse: collapse;
border: 1px solid var(--wikt-palette-grey-6);
background: var(--wikt-palette-white);
}
.inflection-table-wrapper > table > caption {
display: table-caption;
background: var(--wikt-palette-paleblue);
text-align: left;
font-size: 95%;
padding: 0.1em 0.5em;
font-weight: bold;
}
.inflection-table-wrapper.wide > table > tbody > tr {
font-size: 90%;
}
.inflection-table-wrapper > table > tbody > tr > td,
.inflection-table-wrapper > table > tbody > tr > th {
padding: 0.15em 0.6em;
border: solid var(--wikt-palette-grey-6);
border-width: 0 1px 1px 0;
}
.inflection-table-wrapper > table > tbody > tr > td:not(.secondary):not(.separator) {
min-width: 5em;
}
/* very-narrow is for tables of unusually short words (e.g. pronouns). The :not
selectors are styling-wise unnecessary but must stay: they give this rule the
specificity it needs to beat the previous one. */
.inflection-table-wrapper.very-narrow > table > tbody > tr > td:not(.secondary):not(.separator) {
min-width: auto;
}
.inflection-table-wrapper > table > tbody > tr > td.secondary {
background: var(--wikt-palette-lightergrey);
}
.inflection-table-wrapper > table > tbody > tr > th.secondary,
.inflection-table-wrapper > table > tbody > tr > th.outer {
font-style: italic;
}
.inflection-table-wrapper > table > tbody > tr > th.secondary > sup,
.inflection-table-wrapper > table > tbody > tr > th.outer > sup {
font-style: normal;
}
/* separator and blank cells: see [[Template:la-adecl]], [[Template:ga-decl-noun]] */
.inflection-table-wrapper .separator {
height: 0.5em;
width: 0.5em;
min-height: 0.5em;
min-width: 0.5em;
padding: 0;
background: var(--wikt-palette-paleblue);
}
.inflection-table-wrapper .separator[rowspan] {
border-top: 1px solid var(--wikt-palette-paleblue);
border-bottom: 1px solid var(--wikt-palette-paleblue);
}
.inflection-table-wrapper .separator[colspan] {
border-left: 1px solid var(--wikt-palette-paleblue);
border-right: 1px solid var(--wikt-palette-paleblue);
}
/* blank cell at end of row: see [[Template:sco-conj-be]] */
.inflection-table-wrapper .blank-end-row {
background: var(--wikt-palette-paleblue);
border-right: 1px solid var(--wikt-palette-paleblue);
border-top: 1px solid var(--wikt-palette-paleblue);
border-bottom: 1px solid var(--wikt-palette-paleblue);
}
/* Notes at foot of table */
.inflection-table-notes {
text-align: left;
margin: 0.2em 0.5em 0.2em;
}
.inflection-table-notes p {
margin: 0.5em 0 0.2em;
}
.inflection-table-wrapper.wide > .inflection-table-notes {
font-size: 90%;
}
/* Collapsibility */
html.client-js .inflection-table-collapsed > table,
html.client-js .inflection-table-collapsed > table > tbody > tr > td,
html.client-js .inflection-table-collapsed > table > tbody > tr > th {
border-color: transparent;
}
html.client-js .inflection-table-collapsed > table > tbody {
visibility: collapse; /* not implemented in Safari - see the .no-vc fallback below */
}
html.client-js .inflection-table-collapsed > table > caption {
margin-bottom: -0.15em; /* hack: trims excess height from the collapsed box */
}
html.client-js .inflection-table-collapsed .inflection-table-notes {
display: none;
}
/* Fallback for browsers without visibility: collapse. JS removes .no-vc when unneeded. */
html.client-js .inflection-table-collapsible.no-vc.inflection-table-collapsed {
max-height: calc(1.5em + 8px); /* almost 1lh (= 1.6em) + wrapper padding and border */
overflow: hidden;
}
html.client-js .inflection-table-collapsible.no-vc.inflection-table-collapsed > table > tbody {
visibility: hidden;
}
html.client-js .inflection-table-collapsible.no-vc > table > caption {
white-space: nowrap; /* a second line would be occluded by the max-height above */
}
.inflection-table-wrapper .no-vc-spacer {
display: inline-block;
width: 4em;
}
/* keeps the [hide] link from scrolling away when the table is uncollapsed */
.inflection-table-wrapper .NavToggle {
position: sticky;
right: 0.5em; /* matches padding on <caption> */
background: inherit;
}
/* Single border display style - EXPERIMENTAL! */
.inflection-table-wrapper.single-border {
padding: 0;
border: 0;
}
.inflection-table-wrapper.single-border > table > caption {
border: 1px solid var(--wikt-palette-grey);
border-bottom: 0;
}
html.client-js .inflection-table-collapsed.single-border > table > caption {
border-bottom: 1px solid var(--wikt-palette-grey);
}
.inflection-table-wrapper.inflection-table-collapsible.single-border .inflection-table-notes {
border: 1px solid var(--wikt-palette-grey);
border-top: 0;
}
/* Mobile styling */
@media all and (max-width: 639px) { /* matches calc(640px - 1px) in Minerva CSS */
.inflection-table-wrapper {
overflow: auto;
max-width: 100%;
width: auto !important; /* overrides the inline style that works around a CSS sanitiser lacuna; !important is required to win */
}
.inflection-table-collapsed tbody {
display: none;
}
.inflection-table-wrapper .NavToggle {
position: sticky;
right: 0.5em;
background: inherit;
}
/* stick a single left-hand column of header cells in place */
.inflection-table-wrapper.sticky-left-column > table > tbody > tr > th:first-child {
position: sticky;
left: -4px; /* counteracts 3px wrapper padding + 1px table border */
max-width: 35vw;
}
.inflection-table-wrapper.inflection-table-no-title.sticky-left-column > table > tbody > tr > th:first-child {
left: -1px; /* counteracts 1px table border */
}
}
/* Transliterations: paler, on their own line */
.inflection-table-wrapper:not(.tr-alongside) table > tbody td span.tr {
color: var(--wikt-palette-grey-9); /* TODO slightly below AAA contrast on a -0 background */
}
.inflection-table-wrapper:not(.tr-alongside) table > tbody td span.tr:before {
content: "\a"; /* newline */
white-space: pre;
}
.inflection-table-wrapper:not(.tr-alongside) table > tbody td span.mention-gloss-paren {
display: none;
}
/* Inflection boxes - multiple tables inside one box */
.inflection-box > table {
background: none;
border: 0;
}
.inflection-box > table > tbody > tr > td {
padding: 0 0.2em;
border: 0;
vertical-align: top;
}
.inflection-box .inflection-table-wrapper {
display: inline-block;
vertical-align: top;
margin: 0.2em 0.1em;
}
/* overqualified on purpose, to beat the no-title palette rule below */
.inflection-box .inflection-table-wrapper > table.inflection-table {
background: var(--wikt-palette-white);
}
.inflection-box.flow-vertical .inflection-table-wrapper {
display: block;
width: auto !important; /* see the note on the mobile rule above */
}
.inflection-box.flow-vertical .inflection-table-wrapper + .inflection-table-wrapper {
margin-top: 0.5em;
}
/* Palettes: dark-on-light */
.inflection-table-wrapper.inflection-table-no-title > table {
background-color: var(--wikt-p-0);
}
.inflection-table tr.secondary > th,
.inflection-table th.secondary {
background-color: var(--wikt-p-1);
}
.inflection-table th {
background-color: var(--wikt-p-2);
}
.inflection-table tr.outer > th,
.inflection-table th.outer {
background-color: var(--wikt-p-4);
}
.inflection-table tr.darker > td,
.inflection-table td.darker {
background-color: var(--wikt-p-g-1) !important; /* !important beats the more specific td.secondary rule */
}
nyqjz103vuorcxh3kh6hiehkkfh889b
MediaWiki:Gadget-Site.js
8
17972
88946
2025-11-22T01:10:38Z
en>Fenakhay
0
88946
javascript
text/javascript
// This gadget contains code that will run unconditionally for everyone - both on mobile and desktop.
// Consider instead adding your code to [[MediaWiki:Common.js]], [[MediaWiki:Mobile.js]], or a gadget.
// <nowiki>
const pageName = mw.config.get("wgPageName");
const ns = mw.config.get("wgNamespaceNumber");
/* ***** Page-specific code ***** */
switch (pageName) {
case "Wiktionary:Main_Page": // [[Wiktionary:Main_Page]]
// Workaround for [[phab:T335552]].
document.querySelector(".mw-searchInput").autocapitalize = "off";
break;
case "Wiktionary:Gadget_preferences": // [[Wiktionary:Gadget_preferences]]
if (mw.config.get("wgAction") === "view")
importScript("MediaWiki:WiktGadgetPrefsPage.js"); // [[MediaWiki:WiktGadgetPrefsPage.js]]
break;
case "MediaWiki:Gadgets-definition": // [[MediaWiki:Gadgets-definition]]
importScript("MediaWiki:BetterGadgetsDefinition.js"); // [[MediaWiki:BetterGadgetsDefinition.js]]
break;
case "Special:Search": // [[Special:Search]]
importScript("MediaWiki:FindTrans.js"); // [[MediaWiki:FindTrans.js]]
break;
case "Wiktionary:Fonts/list": // [[Wiktionary:Fonts/list]]
if (mw.config.get("wgAction") === "view")
importScript("MediaWiki:InteractiveFontList.js"); // [[MediaWiki:InteractiveFontList.js]]
break;
case "Wiktionary:Tracking":
importScript("MediaWiki:BrowseTrackingCats.js"); // [[MediaWiki:BrowseTrackingCats.js]]
}
if (
document.querySelector(".mw-parser-output p:first-of-type") && [
"Module:languages/code_to_canonical_name",
"Module:languages/canonical_names",
"Module:etymology_languages/code_to_canonical_name",
"Module:etymology_languages/canonical_names",
"Module:families/code_to_canonical_name",
"Module:families/canonical_names",
"Module:scripts/code_to_canonical_name",
"Module:scripts/canonical_names",
"Module:Hani-sortkey/data/serialized",
].some(title => pageName.startsWith(title))
)
importScript("MediaWiki:UpdateLanguageNameAndCode.js"); // [[MediaWiki:UpdateLanguageNameAndCode.js]]
/* ***** Append #English to all links in definition sense lines ***** */
// Run this code in the main or Reconstruction namespaces,
// and on the Main Page (for WOTD and FWOTD),
// and in the Appendix namespace when there is a lemma or non-lemma form category.
if (!window.noDefinitionLineFragmentAddition &&
(ns === 0 || ns === 118 || pageName === "Wiktionary:Main_Page" ||
(ns === 100 && document.querySelector("#catlinks [href$=lemmas], #catlinks [href$=non-lemma_forms]"))
)
) {
// Look for links inside a numbered list (<ol> tag).
// Must use [lang|=en] rather than :lang(en) because, at least in Firefox,
// :lang(en) always matches wherever it is put in a selector
// because all text is within <html lang="en">.
for (const elem of document.querySelectorAll(".mw-parser-output ol a:not(.mw-parser-output [lang] a), .mw-parser-output ol [lang|=en] a")) {
// Only append to local existing main-namespace links without an existing anchor
const href = elem.getAttribute("href");
if (href && !/[#?:]/.test(href) && href[1] !== "/")
elem.href += "#English";
}
}
/* ***** New entry wizard ***** */
if (document.getElementById("necplaceholder"))
importScript("MediaWiki:NewEntryWizard.js"); // [[MediaWiki:NewEntryWizard.js]]
/* ***** Hide certain parts of the page from Google snippets ***** */
// Not sure whether this is working...
for (const elem of document.querySelectorAll(".mw-editsection, #toc, #catlinks")) {
elem.setAttribute("data-nosnippet", "");
if (navigator.userAgent.toLowerCase().includes("googlebot"))
elem.style.display = "none";
}
/* ***** Clean up category pages showing "0 c" or "0 e", e.g. [[Category:Pages with entries]] ***** */
for (const elem of document.querySelectorAll(".CategoryTreeItem bdi + span"))
elem.textContent = elem.textContent
.replace(/\b0 c, /, "")
.replace(/, 0 e\b/, "")
.replace(/\(0 c\)/, "(0 e)");
/* ***** Template gadget for [[Module:UnitTests]]: e.g. [[Module:vi/testcases]] ***** */
for (const corner of document.querySelectorAll(".unit-tests .unit-tests-img-corner"))
corner.addEventListener("click", () => corner.closest(".unit-tests").classList.toggle("unit-tests-hide-passing"));
/* ***** WikiHiero kludge: see [[phab:T210695]] ***** */
if (document.querySelector(".mw-hiero-outer"))
importScript("MediaWiki:WikiHieroTempFix.js"); // [[MediaWiki:WikiHieroTempFix.js]]
/* ***** `#new-section-redirect` changes the URL for "Add topic" (e.g. for [[Wiktionary:Beer parlour]]) ***** */
const newSectionRedirect = document.querySelector("#new-section-redirect a");
const newSectionButton = document.querySelector("#ca-addsection:is(a), #ca-addsection a");
if (newSectionRedirect && newSectionButton)
newSectionButton.href = newSectionRedirect.href;
// </nowiki>
7mjrwzkppzm15ivt1r40af7t8tqn8kc
88947
88946
2026-08-06T23:17:36Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-Site.js]]
88946
javascript
text/javascript
// This gadget contains code that will run unconditionally for everyone - both on mobile and desktop.
// Consider instead adding your code to [[MediaWiki:Common.js]], [[MediaWiki:Mobile.js]], or a gadget.
// <nowiki>
const pageName = mw.config.get("wgPageName");
const ns = mw.config.get("wgNamespaceNumber");
/* ***** Page-specific code ***** */
switch (pageName) {
case "Wiktionary:Main_Page": // [[Wiktionary:Main_Page]]
// Workaround for [[phab:T335552]].
document.querySelector(".mw-searchInput").autocapitalize = "off";
break;
case "Wiktionary:Gadget_preferences": // [[Wiktionary:Gadget_preferences]]
if (mw.config.get("wgAction") === "view")
importScript("MediaWiki:WiktGadgetPrefsPage.js"); // [[MediaWiki:WiktGadgetPrefsPage.js]]
break;
case "MediaWiki:Gadgets-definition": // [[MediaWiki:Gadgets-definition]]
importScript("MediaWiki:BetterGadgetsDefinition.js"); // [[MediaWiki:BetterGadgetsDefinition.js]]
break;
case "Special:Search": // [[Special:Search]]
importScript("MediaWiki:FindTrans.js"); // [[MediaWiki:FindTrans.js]]
break;
case "Wiktionary:Fonts/list": // [[Wiktionary:Fonts/list]]
if (mw.config.get("wgAction") === "view")
importScript("MediaWiki:InteractiveFontList.js"); // [[MediaWiki:InteractiveFontList.js]]
break;
case "Wiktionary:Tracking":
importScript("MediaWiki:BrowseTrackingCats.js"); // [[MediaWiki:BrowseTrackingCats.js]]
}
if (
document.querySelector(".mw-parser-output p:first-of-type") && [
"Module:languages/code_to_canonical_name",
"Module:languages/canonical_names",
"Module:etymology_languages/code_to_canonical_name",
"Module:etymology_languages/canonical_names",
"Module:families/code_to_canonical_name",
"Module:families/canonical_names",
"Module:scripts/code_to_canonical_name",
"Module:scripts/canonical_names",
"Module:Hani-sortkey/data/serialized",
].some(title => pageName.startsWith(title))
)
importScript("MediaWiki:UpdateLanguageNameAndCode.js"); // [[MediaWiki:UpdateLanguageNameAndCode.js]]
/* ***** Append #English to all links in definition sense lines ***** */
// Run this code in the main or Reconstruction namespaces,
// and on the Main Page (for WOTD and FWOTD),
// and in the Appendix namespace when there is a lemma or non-lemma form category.
if (!window.noDefinitionLineFragmentAddition &&
(ns === 0 || ns === 118 || pageName === "Wiktionary:Main_Page" ||
(ns === 100 && document.querySelector("#catlinks [href$=lemmas], #catlinks [href$=non-lemma_forms]"))
)
) {
// Look for links inside a numbered list (<ol> tag).
// Must use [lang|=en] rather than :lang(en) because, at least in Firefox,
// :lang(en) always matches wherever it is put in a selector
// because all text is within <html lang="en">.
for (const elem of document.querySelectorAll(".mw-parser-output ol a:not(.mw-parser-output [lang] a), .mw-parser-output ol [lang|=en] a")) {
// Only append to local existing main-namespace links without an existing anchor
const href = elem.getAttribute("href");
if (href && !/[#?:]/.test(href) && href[1] !== "/")
elem.href += "#English";
}
}
/* ***** New entry wizard ***** */
if (document.getElementById("necplaceholder"))
importScript("MediaWiki:NewEntryWizard.js"); // [[MediaWiki:NewEntryWizard.js]]
/* ***** Hide certain parts of the page from Google snippets ***** */
// Not sure whether this is working...
for (const elem of document.querySelectorAll(".mw-editsection, #toc, #catlinks")) {
elem.setAttribute("data-nosnippet", "");
if (navigator.userAgent.toLowerCase().includes("googlebot"))
elem.style.display = "none";
}
/* ***** Clean up category pages showing "0 c" or "0 e", e.g. [[Category:Pages with entries]] ***** */
for (const elem of document.querySelectorAll(".CategoryTreeItem bdi + span"))
elem.textContent = elem.textContent
.replace(/\b0 c, /, "")
.replace(/, 0 e\b/, "")
.replace(/\(0 c\)/, "(0 e)");
/* ***** Template gadget for [[Module:UnitTests]]: e.g. [[Module:vi/testcases]] ***** */
for (const corner of document.querySelectorAll(".unit-tests .unit-tests-img-corner"))
corner.addEventListener("click", () => corner.closest(".unit-tests").classList.toggle("unit-tests-hide-passing"));
/* ***** WikiHiero kludge: see [[phab:T210695]] ***** */
if (document.querySelector(".mw-hiero-outer"))
importScript("MediaWiki:WikiHieroTempFix.js"); // [[MediaWiki:WikiHieroTempFix.js]]
/* ***** `#new-section-redirect` changes the URL for "Add topic" (e.g. for [[Wiktionary:Beer parlour]]) ***** */
const newSectionRedirect = document.querySelector("#new-section-redirect a");
const newSectionButton = document.querySelector("#ca-addsection:is(a), #ca-addsection a");
if (newSectionRedirect && newSectionButton)
newSectionButton.href = newSectionRedirect.href;
// </nowiki>
7mjrwzkppzm15ivt1r40af7t8tqn8kc
MediaWiki:Gadget-UTCLiveClock-pagestyles.css
8
17973
88948
2017-10-06T15:00:19Z
en>Gamren
0
Created page with "/** * This is loaded as a hidden peer gadget of UTCLiveClock. * Before UTCLiveClock has loaded, it adds space where the clock would go, * so that the personal toolbar does..."
88948
css
text/css
/**
* This is loaded as a hidden peer gadget of UTCLiveClock.
* Before UTCLiveClock has loaded, it adds space where the clock would go,
* so that the personal toolbar does not "jump".
*/
/*
* In Vector, by default the li elements in the p-personal bar have a font-size
* of 0.75em. Duplicate that here so that we can be sure of the proper factor to
* multiply the ul element's margin-right by. And just in case, override any
* possible changes to the font size in Monobook too, although by default there
* is no change in font size between the ul and li elements.
*/
.client-js > body.skin-vector #p-personal li {
font-size: 0.75em;
}
.client-js > body.skin-monobook #p-personal li {
font-size: 1em;
}
/*
* Reserve space for the clock gadget after the end of the p-personal ul
* element.
*/
.client-js > body.skin-vector #p-personal ul {
/*
* The clock width plus its left margin, multiplied by the relative font
* size: (6em + 0.75em) * 0.75
*/
margin-right: 5.0625em;
}
.client-js > body.skin-monobook #p-personal ul {
/*
* Clock width: 6.279em
* Left margin: 1em
* Space separator after "log out" link: 0.321em (found through trial and
* error)
* Total space: 6.279em + 1em + 0.321em
*/
margin-right: 7.6em;
}
5z8u3sry5p8f5l96yrqrxf2j4uun111
88949
88948
2026-08-06T23:17:45Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-UTCLiveClock-pagestyles.css]]
88948
css
text/css
/**
* This is loaded as a hidden peer gadget of UTCLiveClock.
* Before UTCLiveClock has loaded, it adds space where the clock would go,
* so that the personal toolbar does not "jump".
*/
/*
* In Vector, by default the li elements in the p-personal bar have a font-size
* of 0.75em. Duplicate that here so that we can be sure of the proper factor to
* multiply the ul element's margin-right by. And just in case, override any
* possible changes to the font size in Monobook too, although by default there
* is no change in font size between the ul and li elements.
*/
.client-js > body.skin-vector #p-personal li {
font-size: 0.75em;
}
.client-js > body.skin-monobook #p-personal li {
font-size: 1em;
}
/*
* Reserve space for the clock gadget after the end of the p-personal ul
* element.
*/
.client-js > body.skin-vector #p-personal ul {
/*
* The clock width plus its left margin, multiplied by the relative font
* size: (6em + 0.75em) * 0.75
*/
margin-right: 5.0625em;
}
.client-js > body.skin-monobook #p-personal ul {
/*
* Clock width: 6.279em
* Left margin: 1em
* Space separator after "log out" link: 0.321em (found through trial and
* error)
* Total space: 6.279em + 1em + 0.321em
*/
margin-right: 7.6em;
}
5z8u3sry5p8f5l96yrqrxf2j4uun111
MediaWiki:Gadget-CssTest.css
8
17974
88950
2023-03-27T02:10:02Z
en>Erutuon
0
switch to Ullikummi fonts
88950
css
text/css
@font-face {
font-family: 'UllikummiC';
src: url('data:font/woff2;charset=utf-8;base64,d09GMgABAAAAAEjEABAAAAAAm7gAAEhhAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP0ZGVE0cGh4GYACGXggECYVmERAKgpZYgelqC4EIAAE2AiQDggwEIAWJRQeCfAwIG6qDZyJ3f2uJgNsB+++2+N1HBoKNYz0Y8EkUJZp0Otn///+fk3SMIZt+gGpW2X+UzMOcpFwpy2iySFD0HL0PyyFL83lErpHN0i2lbmELHNsJjl4j3WES1TmCnCY6VG2mM1dBuLm16+4gFD6oLROKCEQ2qN5Bx4KTSFTCgKvAq708SxxuFE2iuhcVfjHASVPBYqr4bNZ4S6oSZ6oYr71e1MrzAxbtsOeGZp1JVLaoiJsqJAzJ8gdbNroNppJ2m8CDikdB33YIGga2Gr36dZqeos7vctU7pv3JPIN1p6r/4zdOdv6pL/bqU47Q2Ce5AzS3rhZ5t2BsLFhQsY1oXRQxIiVlCCgY0FIGNiZlvkqIYLxSilFg1IeYGMkr+vr+o2K9gUHrmnvYiREu/b17Rqj1oee34dXFkRIMhRF1aohl5b/j+e9XzXPfb5ZNbktPI1dhKRwOCZJCUx6HsujYqNQomci9fNzMn+/VZTNJn7nrmpFlEeNs5IZgzSbY1G1vF7a1GYk9p5N3XC3c8WP/2p8nyw26bGNMexdK3U5IKyqfKfeq/i5I+Xcucpct2ynYitKV/FLLuMHIY1wzwbAxbtBjf5D7wXLChYdU46LeVCjjKYCA1aklcFJfrLLPh5Puvnj3AT54w5DHPAaLQFGFE8MRHTCE5w591/4Rgdp4CfiCJwAbNQm+rA18AK5sziq8da20qMwXsY7ZWIVsAhwqkUVEPphZl6YAgrVhlxfpwqGGKBysxkeuLbeVstg2Ht5ux223HUhbElMkDyz6u7YWBEHUAg74B/3HpfPn8IQCFpNcACO8c/aeXk9t2jBUQNr/TTNXVEZX1tfSKwpggYvsjoILDV5RKfAXzYzdp6x2XKpcWtNKNO9QXCHTmH6c9J4KFWdg2Md0LQM4mNyjhfcVDdhoIHdLbp8OIXzC/K9Z2v8nC5PLXtsUeQpuqyqJXIVnVznz52/m/z+BySS5S2YWskDZw8lRZm/7kiPMOUJbVICbPWB06E4VUVW4CltJqtYekGzdWVcp62pceeBTzWz5hRsMeA77+OCU1rRz06RQuXRVroDRUgA5ml0eL+JSXueVQ5bOCc4qt3RRumjvXVE3Kv1cGVU4PQ5KFjC6fba9s9FKsDw8QPxzrNixg7+t176Ox0mpAzZYBny9qpmpiSLalwP9CSGKs7xlaw6PXF+9CgAAMAHc7ogQr246++X+iuoHoy1qIaUyyHFDQKrRKpJJBQC4OlMPJ3qc12oAsPbZAv6g9H+5x4cwAATAPnb2DoXvhonLHCerfzseAKAv7qNgwvOhvpyhOIIwdIOI2lHgBqAnFdxeAiO3RFQAYAPbWX0asQA5eIogljPX3lc0dzKKn1/OgiXSlqn178+73cHe3Jt64/KK7wXN5EkAOnFSRcCvCyzKEspomUaoLL6HkAuAY5EDNVMettnmsVaoCj9+4MsAh0SXAxwTvQ5wSvQG4AwcSQKAC3BMEIArcFwoADfguFkAcAeOHwXwAI7fBfAEzrgCgBw4myIAKICzPgFACZz9IwB4ASeBAOANnOwEAB/g5CAA+AInlQD8gNOMB+C/ZA41GBY4AfbaK8gJJwQbMSLEAw9M8c33xdT9BX5+HdRYWN/VALhKG8DDYK0BAMC7CMBhIR/LcauEz4uDwMDe+NEfpPcferB3a7A+aLbAP+n2+oPhaDyZYkbZcv8cBbjhbTFA6usfCDIyOPmMpXP/SBbbrBMGxkKomyQwM2FQzMAQto2CY5ETbJ8PdYDCjObhaMS5uO6OhNhvpGArJ5OcCRKnb6b3eA10h/ab7YRYTRjShAPRzDMi4n604y4JWhZEcXGL7YXm0NcDTYulTXcSbQChaLCvaVDagJgwPJLSUva4VdCFhCA+FF5iA2SkiErUSDarM/EA6EPV2eYCL8fHBtePGyBfFWKNJcP2isHF/XyyGMxeirOoNbTtAmTCSHFrpJW0AZAcidSR8X0fCqQ6KJB2jz9k+kNHHwQGGRz2Z2XO7s2SzXBLIggG7Em9qn0EK+YCBgCE9wAkj8Z8bMD7c2FCwFsftKWw9kEZCEHQBy4hdP9gPnT00pEQESDIcMjI8DBUMBQeEQJBg4OHICFA1jFWZ5FGmsGeQ6VRxWQOjeqOCt0wCZvApHmq2FQlx+hhxxBhMiXfzlMR6cxWEjxQJZVPXUah40gkJgzDHBIKp1DfTBt473KBtnApjCMLSbi1MApPOuENSdxsaCsMo/Tr8LyByxPBOyMpXNgGRX3hdkWUVTSeu6fACJlEQYcVpsL7YBzMQWA+icTALYLhfBh+DONEh2pTBFx8BNbQ5dQnCaWRnFBHtKhIg++CJ1GlQJUVlXh4P34f7JbCBcYumPdeaQuXi/WhIIbrUBR2TEtlAWyIluMOwGwkzYXoHg+OmiODpAQ6eSpNxCB4h/0I6h7ka6QotHd72HunMJwTWe6nQXCPsQ38qvhObAe4+imSVCNam+ohj5J2hCLdQQRT+Kn5DVmq0zmbSWIvPj4f1T7WjmA4L7neaaARsW12hX1y1vDt+7r2CDobi6MAdCxCvqNHwTBKoawC7d26rH8KPMS+D2lrs4yqbnzub0P6t7B7N4CeVs8NXHzPeqe+zVSao6ztc7lC+t4r3ftILuT0EfbZh4CtD34FgY5ZbJVHoA2KA+x+V+EACmJT09eQG3t1NlTRQAlYQFqEXNnDPrsMCiKwUyCdMChqr5ZYcHR9+NkMJSQh8K7Un8IVpML8IdnNyx+nENQEcAlK/rflWvMd2AbOPsQ+zU25XlzyCf321/2PjwRTX0M7s5biT3SKRzwHjEL0oN2pcirsa7CTls+kCY6QmYECe+QbFuQ/emQzNDznn0Pi6Usb2Fc8SNJqbjgP36cwu/rHS6QHI+EycHUfaRMV+wDnh70b8KJuyMc8Jvo720I87EtVnIprIsMkA7thoKL9WKEu2sRv5KlVtbc5JkEhYaKGYL14ZPxSBHvWAxwZAguef2vdw5uZJnYDu1JOBq5TCbb7I71+19N97QICg6yyzqLcGawhiHibbX0IpAdDAMOtvXPUHPCaPZMOYaKmwx1OMMAXEaiIR1KTlfhyBcbIGQVj6KPq5tGeFlD+0CINBLjC3WcdbPc2yvAgyM1axjdXysyf9lOLWcRjPDnVGSHE61k0UyDdu4Gt3pG+x+j66BWRPIMZNIjOgRYubjMReOxu44xxATggn7xibpQvIL4rgHQP6QHwkFsfRU223gJ6e3diGWAjXXoQQohnY7LT/PL1z9t2Iae3ZLJbsSsF0FPp1YjXFyR1pRJkQkQn7uk5UBbwhHXDAcWxRSZM4QA/pSTsNPb0xWTG0kgZlgCsteVJr16oiKoWQBSEajoHDEPcEyYdWmk7lv49mNo+sIZ0vI8tTZVDU1QaHawLXkJ3/TL5iSZoDU5YViHJ4Szo7juwIamm2DDaQC5gWIL29fBlYrA6KzEpiAxpozK9Yz1dIogQq0F9xdR+bYVSMsCjYL1lwG4vkMJtrQQ+curvq9qSPFFAAp4VxQj7ERw1hp/GwVD/MXgP51Slk7WZzpCGuT7CFq/iFlk68GUBBFvOp7BsVAyG09C9i0/twtTB/qbDbl7P7kJW2cweANuMzaizo8dzeUDQdrTgHd6cBYpI1PZthm0XPEGqiWXyPVtpf72maIg6z+D5+CWVr0xDWsf9U8foZI8K2EQ4sQ7f0iNj9fLT6fTM2nBm07tDGBe+mfaZ5YrL1mmYa1OuX2I7oY7OEfdeFmzCKhVXdWp475z5edh2xUI62bsTJVoraNtSsV9/sWo/MB0VUlvaQx261cnTd/svM5nCLwf/XBc36aPsidgxfR9+NMb+WEDhB9Afq9TWDtuusCiJ3p3UEB96EQh7uewHQSdLNOgRKFYwMDasxPnUf3WM9imyx4bbKZbFSgZ1trC+vdtZ9CrL9k+rkbuwFZqG1al2uFZDFyT6L+zL++fQifF8UYD19W0lnbFtcDovY6nkbaOsWYYzLxAYBrq46GezFbtLLwhQLFoClEVuVipikNXKaaExDzUnj7utas5Pi/6ogPlVCU5OLkvm0HHwPoeFXFwI8ybB+0ofhWQxuUjtrp/qC21Uq2TV63bNSJIf0y+/3Rc2AqB2WlbTXdeAJYgLqr9NCeaYG0CYl6uUgY/M7rMcS/JKOXQwtcGHG/YAk/O89pkU1vp2w7ZrRcFbB+DAndLhQ1/DrrqIV4/iOSIBotmcRI57udJohUZMaikGq4MbeCWX/LfyZMPxeAe7myPuL7fZ6B2T9r1eoCSKyjqdq4tenVfjcus0+DWiIOhgGhwBD54wWcWQlT1ypJKW8Ot4Pm1YQwG/+YfGgtBak8ROyOxIYW03bH+wvCu21rOUKs266JoiP1a5IOonCkv4SrN/zJt23A1IKkQzZ9B3+POxjDdZB6xPvV797+XgOwWzbAraEoAgwap2OfjnauM1tkjJblBNAcuyKGTRyx0qXe3UPa+3fbCwYAlxi3yzGfcCVDZe8RIIfjkmggzqKm68kOMnTdS8r5IJzEbj9TA2fOEn+FSzrDFKlXpR0Km6nzsTuK+G6278/VC65LM3Sk3CTVYILkryHPukJHusEygKYrmThEnBEHIb8sTvkZX3Ydp1Ip2QRMM1GaqJm6Rrx6Tf5cyyiRwEiHh+uJABJ3AkkMpl4j4qw6CyexHPZQKo5rRaoSmjjxFNSWCYJrXhGMvClgkPQlMSaChpc+07Q4GmznUSDEPv7hXHNPafQhk9vfrv4fiXiRyySazjHeXaIgDabCKG8k+UqJAuXZ7LFFVTDlCIK5x1iZ/KDXkiZmMqllBznoOKKFYqlQhubBx2x3Mi1+uRoZrs8PpCQA/lQDQ+1tei26H/3Iesqo5zMovQeF70fnoBJmZmwOGAKBGzQYxh1WjuJktXoxptpl9qmjcbJOO3H4Qt7FHLor7ItXpT9zDvBaIVUQbyBDJLdOzPgogwnlqZaifUn3QjOpeXMA2ukGrKigC+6nAXUK1WcZkjEenKlKnUg3egtEupsPsbxBxF3CH2RsRtRbiOnR/TOlNDfKQjrnXEWf8ptuEkzGGIM5A5WGxi3IDiOzmJCly5x0xnmMLgG6TRNM83ETmd24HoynAAzFWlkh3/RljBlwS187MJhlAu2WBD+jUh/lpgUwDDNGGYNQHWKRjEu8qE3bxOCQDJzmpB2aQ05pbMgmRelVkks5+dqoQx104vG2pVlKLqCQkbJCjp+3TfLpimAjkIym1kaQNEHQlZhpsM16jusfB4kuUbMh6hDHGbYgpFAbTKB1QdZDNIYBgHkiZ38FUjcgMHyDjsnNotLCo4hgzjQrK6SPOWLKBmKtnhQvOt6bw0mX5ZvJBD8AXA9beURpAdG8soK5hwCAUauFLP4PzGDYLLy9Dw2NFka8tDnNTRPSaUhab4ykJ3UtuuKfLB8Sp96MRflJOvN6FS9DwrKT7Z6OPE5EIHiwLZbA3ZC7C9bPv6levjVWBBKM7/x9ZtYQbJlEZ83m+34gLc0nnWdFQaBYcPkPxS9vHUY7sZoz7rTFC01zSMmv2ckRKPk+5UEt7GqikXWiWRPzQELnZjHDwWj1R/53H86HTauQBIojeLVSrRW85KjpmQpNIlQbjX+IHoQIsCtpK24CKM60wcfBOgQDOOzIGtf6KXmrUpd+5E1cuVzn4psugkBGYAssidSxgypH+re+1PxzvHbrOR2+wdO01bqTZXR0/EGcnA/ohvBBm9ZFeNNPWtaqSKtdoClkbNZG8Wm4+ThLGjcDFlsdobsneaVB15Dqi1XzjSNAx8dtN8R+PjC2VGsAC+ZLC1yMlBhBmvMBEIqcsJhlrbLrWKFh6kt9w1pHVBh3P6nmnb7iMSN4wHUl8rUgfxZNIz7wRh2Seu6294dexDsCEdAislmnQzs8HAptaRqYC3c2P22dHgSWWE98T0hkfBHmsEx/k5k/515RS3okhu8GArEskfGWiTPEV8br0FEz8V1MEkl1t4L+7FiwFtqZVCEeTmncS7lsNzLgl7qrck48CSFqmk4SGJwmeZ97rYRFPZAoZK51wAS9WQKAgx5ZS8wcsnk32UkFOA7y9ZkdpHvONwrG5U/8ZfG77OxvP5BDLxRDm2L5IzwxY66Zh7kRJqTuzjIXN41CzEHUU23T77cZgsAbYdD7KDlf59mXfsJEmxby7hzs5HoVUnpmtEzObd4+1s6GUtbQMDgnSfXhF9XleylFsOKF4eniM4motqCjItR5WoNsmkCURsg0yqBVQ3NrAPsesE04YxfdP0Y3pd8TNCxskHW5YcUaKahg5GyRKW0iWSuSTUGPLcIe1sdkJdGd2LdRLOQXhsX8fEkq9Fadkt5iiyiyxR3+92tzFDeN2tdS9JroQQQ1gRuXTV48095DsU5aRm5uFuM5y7yc4SWwIsoP4Gm3JtclyjsdsVA0GnSNuFJknUcaUdvF0Qa/kqiab8UYbacKkdLbfHxKBaSf0YZ/Gh0Vw+fzNi1Ywi1tEOcFQ6LFInn2tyWcKNw0NijhhXdC4K+sOoOyYgEfDZTY+zDhjhStgsK5UKSjLfUsKWjENl30wJHu6NgVj9dvKEfkfGAzkABYSVIRcpRsXxjnEbzqNxnOBMtaUS3cB+CxxDZI5P7X5agZXoYpiQlnW5x7Qrl4isRRLdhLLN0yBNxy1xyJY5pMJN+37RDvz1j6ff1jWuBUcKkLKwQlaP8zdiwXR0NnbBnei4KSsIbHx6zxMFdT55GxvA5lZYJQ4nkCKIr+m2eViXQwXRU8khhYuKH5JxBnP3QpX1KnCwhnP0O7/SpTGBqcmKquEhOhMpch/BTSNWNN5A5M151Z27r1Jfu6AZe6rHzZn9CQrDxKRrQZhtVUJH2sDGT6x3n9mihovczb2Mrbe1TB3oo9zj8NMJFL1MxzlykYHV9d1wft7oiuc9JSFK63Ezl7nU0tu2hQQXAnibgA9pFksf1VMZsUhhfpcqvtkVx7xgSz9XpaPTAtJW5tX3cNO+E6umaMgNR/FutliOwciOoByJyvjHItu0BLdEry9hZfh+xKs2TH0FoFcY2uN4Op5a/2ykEfw7f+1ubPb/F86lE83rXr90/eTZkA9z4lZki5dieLLUZtoXgNSQlMOF8OOoh4yUHaliFb7nuqNY8UDzfkJ/gGTHMUwreUlVFNdbqiDt6/KveUPwJMCoTLX0LMxmJNSl6r68gZUyshFK8HQHUlUyPQ09FQHhNm+NJB62A3X7XiOot/RrtlTva4n+4aZfRh1WFIOjtFXtJCgzbYPABMJx2oygaBhpGVeVB83sfn2hIFIZf/FWL8SOJdKSTPeHvVjg1Mz6TG5sAPSJz2/41mtDb9QPceX1Xq7XFSJqg0m1gI2P0IctIQxorQle7vQiMQQDpfQyHIa1g2SrVk4eY17+JH0CBkr6Db+OQFYaJgZvhyMbjn7WYCPW5Mc7/fR7rxciASVMhS7S72OnD5QN7gUy+vcuInoXrshzsUcpialW5eKh/KzRPzq0N9pgkhqj1EKM7GpjpKvN2+zP1hNFzEq4cxgfN9XxzA2wchbskyvalevD14M66BeQXy/4oCjYJaPIChAgLujr71weMw1jFTo+v2ICnRmn1fjeGuJkGIbjM6FQ4onoY3/5alVW01NT/jEvEM37p47JCT5L95l3IdnyBAaob6G3s82pIHNK/aqjUFRWi4oV9yfjlqwga3VjmQDAgexEB5zsYuZSv/PfI+8RPUR8pP9x9IZNPrYKJaM4aOpKSVbTHQvXRGdyCgtrSoHh72N8B6qpf7/T1GHwT5dNH+IrncPffpca/u7bXz3jsbuBs1ynaGyYOzDs1Pnf23e3fnr8aW8v8f/A5z/530+7PIn7Kmd4HbwOocJwH/vpDGLNspTMOM94T5ed11xv8ldX+BkNfhHJGaVVXbw3m79a8qOJpXYxkiMSrV1ou7Rwpvs7N3+Vs4rBDDafSIqOdOLx6RpHlnvLXA+7vVy7oVCKu7HMt+yC+wNfWz+/f8HOt3ELwfuVVktqKhNcwjeEm5Nd+AIrlQvmsadYEWFwSA8FcaSI3Y3svuyD9rS5fiabKGrRbFrkdMcprp+nBpxYEBvtpCclliZGxoZzr7klPPKe4rbD11yy/mWAP6Xpisfqti1i+pboQ0LTAbRJu2Cqyxdn2TaZ3WpWt84ldXSa6/MIR8XO2Uqldb7clY51+qRwFX7dqYE+aS1+4QFJPiq3YNlembjztlOHMJ8RnIS2ARf6ArVRgcaMYn1ot8Fcvb112k9BX3p4tS9zrkL2uatJVNXQ5pMIid1tbRRhF7aJbdo8ghFTjGmq+1V3w/9VDQEhcWZe+DprQiieuYfJrGP6tHwNsBDFhnOCoBgWp79d/d167n3+0v7wZolWaz9kFedBEBrXibBoLMl6qdoDSnTu9kqQG05PS/I2aJJr3CUd+7/8FnL6jD124JQ19uBKUmLK13sC/+nTY0O3bTN5x+zZeXPk4Z/jgarI8P37ZuVMNamMOXE5YxGJbmq0wRh1yU5vsl3Ysx3FGr2UIVB3YuCHcP+l6X6m4KiUAPL2dv9zKMQSrC6o/SQVp4Y27/r0rkZlTE1XaE9k1hi1G8Pa6AN+9Hv1KR+//ll2Idc72dOS7NRe7TVVauB52/ISNGAoWMu8TfJpUAXvCwjNdPPk6/3sY0Pwca7y0C3DfiRv0rM/UGszvBinIAUrzemKSOe4QJnWRulj0YXysYPcQ2lOyWGWtDC7i3sF5snslOw//vC7RfdyqOFD0ZAYhsXM9k0TEwkx/8JuZxdv94rmqyf8Lnn1nNHSb9EPHzjle0H+/aG7Q2Agadz/919300fmYoJedqDAaZ1ycL7VH1achxwrroeAKwElbPrmNCpJWSyiv8PcZwoVYJs2WBM4g85S0n9HPZ98SYm/umXIR/scvY+yrDBceN0R6LA8S46ardB2vvOjow51chGWxlGzFQkutPu9qDAh/odtUDYX+8AwsrRcS7hpR5kswSouNC6pvO9P//YG9AHavOWBx02fZR/kpjnfvVT3VehTtHgP1eNent61+hPRRhJ2EUMfo9FHp9yMSv4C6H8NYlXCCGV/tkS9Sfd1Dv53k+qF3WolOoxCJzDv6xaz0x6eXA2jt9CYoYPpYUtu2QYssmEtxowcIz8mlBEQkRjtpq+Jn+ETGhSTGXB8hyLkmzfzPaPxWNyXT/xshtD1X7TYV2QbZxaOsq7N3DM9yytB/M8eI/0fxru17sdxNNw5+3hXY1BKdrBxaniWl0vEnzMyDTLVObOx31PNPd+avzs87v9gGcbCInk6vjoZCkoJ8vV1c2C0k6zecJ7J5/39DB1HL+35xxgYgNpUxduqikQee2ZP3YtUuwYmGIKszAwGvZq2HXtsA6rsLsjcQurrK+y8/NsKAlkqFpFURfn2c0icI0rxsr1x1fabLG2Ru48Ch7/nts/Lx0yglXo+VtCDE20Zcfv2TfGfYgr47hTS9YzeaJBsk7jt+u7wxvFTPWNnknTTzDuHB1nq3RjBEQrg5HxPeuIY7aglsLiHGNgarHZ7t3htBtueAeFxdkwH1sYcovpKJnbBPnCoGk30qEuWEwnklDcF/3EIJMqKhEFjvwU9hN4UsaXJ+v6UizYxE0Mw+huKlBDYVGPU7eRHJA73qM9YH/b8HEr6JZ/cejOyKptMa+rA6Dvpg7kdc9ri5i8+1/k3n0O/Sn+KW6ooI5E9jIfW6fuqqm8aIyvzF7K8CaTi4iX0BvrSu5ciizrS11/d1KWkX3O9P4TCr7ZuFid2eZqmCEMK+W5yGoMotTVssUFClhd9PJZD/xNttKFvLP5ufsRRwQI5ym9bQVtRiZKaVysJB6OZUFkqSm1eHT5rnKZhUWTnOTyu6ZxS3SmtyK3MnaQao6O30JKT4z4X881+Ge98xk+WoHfRC+01ihGz8rOIGHubIdR4QP4xSTUtl+RzGhIChYpZdpo4rZmnF6KZ0+Xv85D4us+r4aAc7lOuNWjNsZ3EcUo2XL3f2cOGUOgVcoBEv8fPkvW5aJEL0E4gihRL4/xGxOmXhu7AXVVmRtBRWr4GwKRgPdunvHdYqbEmRxT5n3HHyLYkakIyj56kkiW1cnc3UpB7RHRHrzTLVeN6Ad4JRpPsadZjFe9ZuSk2f9lR+AZ+W/URskezXDxTTHXx4T335pN5VDGnSZuvkXpxyQalJZ4Yw6xNda50hcBXBQ4xAEwedfN/IBnbxtnGJNyUVbu22WAKgiR/dbymsgFDSKMZGfHaigZgyzEnXQrUs2OV+290PP9FgOpo3Or4+lrFJ1jF34A+GTdIeW99tUw89G7Ox31MgpBKL89Jn4kBa9lLkka4+x9ze1gYTH6vKNFuIVdpEy5cbORsY4Le3npK24nOIwLVJFBBt5CdBPQyJVPS6KxGzkBbzcQYmtVVIvEBDqiNfvZOA1L1wHZW9mF3VWwWhzSn1f+xG8oTkShxs7j0HBUz54zToV4q3JxNwg4MOmSGhro8gHvBFI+Mjd6QZqZKIrMm6eRZsYRIxpLS4d+5RJEjKvTzt7MD+lbNTm3AcNR/JKFonAH4hVWcdWX0yDOEaEB/5creiGkD456wif8Du2vkv/W78bYuwxZmSb14JMaVY5vgYe5aMiudspXVV/7JL9TlPtwj2nx6ctY05sskQ9drQVqh+/lvBCNoduADFRRKsFAQNo92IBsvRUysMEUtrDAhdnpgHaBHaiPx2covG/JHJuiLe3c7pnE+XRIh8lTgfZEZX6r8e0PVyIefi4d2+6XhPx0RTXENVoJ6TdCL0KkhAJQLLJ9RvfPsBcKNkGsGohmyt7hb42yQsBd5nmZG5mKsPBt+hsbt59BbGcLEq20LomTdr9LfKmzSSi5kFtQ865+GN0HnQCjR9x1fzW85NpVvmM9YS7W/wOLCYRNYbCJbkQ68L4rEZ19fv6144hyoDYiAnkEb1riIrFYlEECztRhRmGCZaS1gRGC2XV+4Onbihrlnn4RqeP6TSjqf4bcJxL5CeoiMryEF9i8y9gfgSkWTdGkOKp4TjZ8yzZEG7Zp9nXM/Nrd8g2aPDOwvfmX9eEZhLWrLwUYEbt8WQNJeMB2OIZWaSzRZPBwrdGSeEyWdmcKbS0U2ZhMZ3efs5kZ+Ot7RcZ3L4iPp5Mik+aF2vjY4iVQnMyNqnBkncLNuWBsWsfIyPjVI5qeVRCMIR8wvC+iI3Z/824H00o8Vd/nkCwADRtIoUZT1A/HzfgkNoHM/c2Ido6T62gAMoeAVnQlX4u/dSPrUOpkWMhR+h3bPHi814WQGsA7QQfCGhd14RY1yYfpcdzakjLEhetu7LKF+xgk3dY72/qMhnv/+oCkv6b+yeu8G6ded+XcmfixuO2RK/Yi/JIox4+Ilfx/MH/uLWth+2JiK/3hJGA4nZstdMTiBUaqWc/3Q4bFzrQksGYZTh8ndMDgRKsuuihwLP/WF9BhIEbB9cBkYTDk71OslB1uqYy6LpxTtEDvQ7H1xo0NtapuxluqZEr3wCQJD1++/PvTx0yhQ6JMQ26CmwIwsEmIhDFItUOZsYEbP3eF900oX9jAuJa9dsTJwaguQVcltaLAQoZXY+Rfrhl6+dse3xnAzsXyyMg7ZRYlDhsKgkSNzlfeW/37yg4tule7qypGRi+/o8+FTQgyaaDr5W/b7GCc1NWTNnD+8rk5xqqPccODwsuFtn6ebjnEcoJGBd9LRuG6rFWsvXJ1jyG1gbP0yMZEhpM7+OTxY/ILxsuX+8bmhK+sZFaTdNVdE6a96eLo18mvVeCYLnGj6HQAxUAZOzBPiuGyvsHdlHddl83XF21y3Stt6m8NsmUzcrx+9hKacGbkLTdf8lKyEUUUGNPGPnQ9+V1PxhL1OjKkzsOffzxAX+2u8csf5TLJXbPSFePJj+1714VIfXAC97nOeceWapyOHe4bRXTEIBLVl6oPviBqLVYm3j9I7RzpeuJ73b94rtR/MQhKsbwErCRO6ujavcCELGmA08Fd71wBrySnT3DZRG/L8oPYHCyOcMY2qkC+WZy3APj+29+X0tdWCjmQR1DI5OVnQxNLfrs5/uZ5BMwApkq3/ZTLBD81Xf11x59XFp7T3RbiiD1xwOgDM6tqva2fXa4aKFvmWtDhAkobsxstFrZq2XwxOQE/vErIC0kUIbtNBb0z3wXTnhMgSRe8ledsLLghBHRD/wTS7au3B9ffXfVbz4qMhvCKeSLglGWn97ehcQ24bWEmaGPXnXbqqHOm+f9Wca2gFK74iE2bfwYBZ6R/Zs+ZiFDGq7pOB7GKS9xwbfv6Ptw8+qXhiru799h20zootJ66sOx6Dt7EB4tzIzlTv/LtUN/1Q7f/pgu8ZvBPyDM6HsVGXRB412F0QPo92oJTAgkLthIHUC6vPH6KTjzQc3Dz0fnPZ3muDWemOjGkcBtMT6oDPnVxQWz7rxCVblx8CaxseIXKm3c7/OxdEPSXaBDcdzizY/qwjBW9EzgKMXIbJ4chrt/SikPM/iCYwEoen4itXFP3JNsPqUJsXFMgLi4dM+atWn1yXdPzFKGlc+g4YkM6jCQyBIruBe2wBYcRyp2Rs366hwQERyySVJCC9GRTIy0fhqvPqeh98R6D+Aw5JOkg0G789PZLtuJasYuCPLLxEQIlCkrBNSh4sOfQ0pir/Ni5mc/hmlI6BgEVqYwzUvu2o6iLKvwgyL+ZwSpGuDe9ixQQsEmpCkOcj4qv1x2M4JBIY70KgEftyprc2u/8ccwnSi5d3bdn7+72dp7KJAqEQPbOMqkvYxU5hBxA9CTO/zX8zEwCv11xRbtYg0rD6ykgcoWNxb8/sG2weK4ycn47jOdhgtujT8UK9lp2d0+IRKA1OANtdO/dknj9fO1hwrNq29eOOU12PCdavYpvJZTEICJQtErlJoz9uS7tze3TY3rGpadvMqN1f78aES0LZZzOokFfPMUyQkYoNHGfbIQ21RUN9hIFf2FbshsWFgwzMbW5VZhTEgu4NW3otIMbAYiVd68+v6Mh08JEqkyvPK/bt0QYyk7x4bKIwJUJk3wViYW3Juy4HeHaJC2JU6nqVIwiCIGD91/1dcBCHOwrvzndvcwHjbITTzOSe8vP7792kqNig3cR/dO+E9l13Qz273tyvAMDrCy6vDOkgyG1wGGFzmrRrakbthaifTd7MB0ZO15V/aBi1/yreAIYj/DI+1VVICcigrY3DyyCDTA+sgXUcfIRedGfrp8my/36dG23KydSgO4dmUNbOwDPPlOkdh+rV+ThVHngNzIPLwWLS2a//7Z+WvW6Q8TFp7YqVzh5rcam7nbrPbCBBKxSHmL/+x+ZUeD1OZswiFcP+KX1gMsBo7234KMYxgsH5QVZ4s14298oms+gakW9T0iGfmtbTqGkcPXsz+2fujcanYHyU5EOc5LG66KqBq7rk57BhoV/vaX0yig3ZTIdVlW56CpQNlZbM33blBPXRqrwL7YOZSsvgqHWMNbeQ2BAJNSnAX55gvskvSycX48GBYjLp080qyb9VOQOhQ85ZoYlc8mOuyyK2EyXNPdL0gExrcgCpzwEAWck/5REwIUZonf8/e8R3NYNFpphidQftRnyjv+9FKdkzVBk9+EQoghhnylbGcBCmqS/FDiR6QclQGCHedyo028L/bJgdOQaL5CaRka9EbLp0euCNuqXZgp7Fzf7buR1tVOSVDu7okWdpo5VL5+IFQwmvqggeA+ofke6/rpKxYnPw9W32WlX3K5UH8e+S4hchDSEeEdO9PIikJSWsDYoIfCi9yJ9IgfXP5nydxt8wtERxxzJOEht63O+a+ZHV8bxY6F6IKQ2XbZt3PmBwjvBUTANnWYT5eiGJdPqKdonYRI2wxkEucLGjM5dCC8NYtneWus6y0yiWyTaGsV3dnvhpjhXHD0c04KObcbX46GkuGERKVHxkIdD5deuaTqnngCBIGL72QsSKnXUVWhIxG4CJXqo/NMqmH3+oZsQCMClRCXDjQBD30LG3b/mBGP0Bq5exth9ZMHxoFwBlSEa3RN94RhW9xPug3V2/wlM7ILjxk1TBrFlTq6hw4P/IRRw7XxtLxakncfpYOkBQBYnAUHsXlLiV6hBnz44UZJdxDh501PHCH++xp/L9Y8wiUHtffnv2WXgJRoW8Xst/oVP2tmIfNIMUOI9xy2/eaG1zgHMi1dqPfufClGdSLkURzp0FxlHYUwoYUi5ZHu5ISkaTiF1LaUiZjkjf0CjKE/o3IJDxzO55SATUtgoydvP4VCQ3Dk9b1CjI+75on8fiaQAN1jT+FTAlbFqMlRMLYTQ5drA3sSWbi3n1cOhV7dtY55AGiXKRjySbb5rjxZhxpua9XXJxcEb+6aOPoGftCgI3C2/RyMjb4HjYBIUWKsAFofPOLvrfxjU6AaBkehM9yizVhym1xcX1XXAq3FoUgcvJ3hD1AwQR8gUJztu5KhCn4y2BEPLtMp1j2Aw4kxBFnb5CBxBUbh6gyQHElpqkny/pf77xUdBfexCzMpeFs3Ij0M38CDIuGkYzAWqwOz9s3vCObJwUMdnL9IwPh95l5j6u02tSydTKpLXZ8X5RDykWEvicAS3DOmbOK5qVur2LsSOIQ8MH4CMTZd2vBtZFDRP54SUXMhdtf9oxjZD0/hYA5QKz/C797Z5W5H7zO8EERuKgcR1FTa7lqiln30EXOVsF6q9tkkVs1Z9xlogbrmvJ6i9vPv9baXuDrOP8wKVNLae/6HjC5W9amSQwWJt/pJeaQQPDoN5y3NV9HtW4+LOWRvzy5GVzglPf8fgrNy8d/l92thjUljnzvk8j70h2dQnE1FjRFRuha7BIvMHjd5c1thrb8f6cvsZVq5btzxvYKPzQktu0yA1KdCWyrdjGfOrqDAT1QBUkiRaR6IFVgA6MiUBiFbe3D79gtNbbJ0Pvb/K1iHFeCEEIshlhz8KKeVZW4Wx6nlDo4m69jguCIMDQ9PrsXHlz00tvC8t2UEyZ6CzBZzfBTDXTBBkgeZFXz16fQIM5guAgGralsDsBCFSFReNAvt4GVDIualyIfH4IPkw48+Wpl0JnoTD3TuAKgbVjUfKcBpjI4FH9wkU5I81hrr/heI4lM+c0ZkfDyLuDdGgGqJfvvvOZG8QNZcSJxvsbbxzAIB2ml3eMfOG6cP/8f6mfbnvyAttl3owDyfMKDZAJkvNnCo/oHCwzzRFEh2qipt46yCoi2U1SK2aq8cnCYWIxbZC0wH3Q/LOysJLPA7iyILbJhBm8llkobnn7V5MsQrvuw3GcqRzs3bi9EZCwSxSt3412s7368n45cXr8mstBPSEMsgsuAYLlvjqGdG2RvIHFgogMa7ydHgtHNRtVhJFSCsX8KUww8/Mf0XsXjgi/X8r96TN0m3AxObypuH6wjfQsDZ6wzNxfyZYE1Y+eNeZrvV4IEmFzoTlPCC/Hdu+uPPn4qfTdtoe+zOHe1nHSD3Ko8pm1r3R5iHj5+gOJFD4tTQ4UZIaRKqfgnB0FfkC+jh4UKT1Z/Hr5+p0JFIEtkOzJzCwII82KhKkONn7AHHtBRDClLbc/fM/6A4lkoShdSLK3wvmFk/KmIm72Al8g3yh0jRQSd78ecyddyhrJvrO9TNBTLmSYpKAP0j6lHvJK9SiTNDqWK5baLxNPbuOw18IgAtFn4imWGQah4Z7r0lKOrqBBN/eLfSnthf9iEibtfWAg0nV32RFoGeVcfGpxAxtmGtHNMkQrk7g46PBErDwry7qfDHec1P/rmjA/3uEryAdTfsbPsXOxMtQ2ph8vTITjyHv0SBeO+IKTwz5+By7EwW+pzzFhMIHg8xGVWMJAejsA5vLDZzjlZ+eDA9TnsWnngeefxlgbbbTnjfymTD4AES6L6SH2G37zeHsaXEV9f8i0Im8Jvrp5QbmEZi0kWgmf3yZxrN6qSzCE7KB+kcCbDBNA9xMxcGhO157pHV2xRHstKz5nh9BG0mW7t27zzeeP6V669rYBo8cKGSZIzwcO72fbsRsKLYPal68NttD11K6uzF3JjQQfLTs+Z4cgUOLuXbn5r1v/0nW6ttoRg8cKFuRmzgcPOXiQ2lrLsOxjv5qMx33JfFoLEnOgJeMrlI7Gtug5Dlav+mzJOBdnlYDMi1d7EsC+hPtW92IJtzeiIpT8hOj0rITW5Hr9I+u9fYS/03wuQqf8yXV+WnJcHS3+Zp0bL5SQeRr8hnq2rVVGGhHWXq2dw9C2ZxSJHFpJ+NP5GExE5e3Rl1yoEEyiK9qiL2/v+mQKzy/+gRwkYvdcc+z63fS43whA2GoyXH8OjCHF0zkPiaRHZAGfSsMJ8KWAY916r/oKfxbWBG4kx9E5v5OIi+ZQ+AI+HbDsJe/Bx0J6op12DkNpjWMGdjp7qJbecaW7m3fOkYLnWnCO35cUDj89egmlQufqEcfvxYWfj6ASNvoiyJW3xl11UtxP7Mku60dXN+4aQ6JvtCTaOVSmcpe+mBfOW22U+VELETa42BSsi5GD6i5e59LpvG24vk2cK/hx1lsF5IB0MBo6xqyOn6uVefPIJq/iJGIcq2FTDzKLiYBsOL2LAHN7ZzCSMRyFr7487X/4XwyhuiquJL25FXw56VsfhKPyu1yL1t75Ja6ljGencud1Q9koX3EvsaMlCRXlV6R7Nj0IsGVZ1tkuFaq/Hs7A8LQszfbZf7hmrfSJ8tBXt2QFadB7/By7c+T8q5JMiN7qtH2VIPRNeS/YT5u/atViF2wbf+Q4BIIg/JNxt7Itu6noVfz07ru/WKlsamPriUq1eEYzccUteQOLK88EwYbsDum1wE1Vd7fh5DYNcS7jhz81kzY9lxdCPEUm8KE3rvl6TIrMZDf0TBRSDgVT+4fbdo/RrhjwCgNObgDXGEBLJJKt+H9n/p0P9MWtHY6XUyLC4KRScTADLGOUxtXs7D3/+az7IiTpTOBef6pglZCVzWDGKVxukUEdpvtgzFu56vjae9twqnqXGMAFG/jUTNx8CylicpXTgTif2jXDq5JvyjYln8cVLjNOPrgEnC9Sj/ng32lwUjVYC6hBtQnWQXd2nLsKtjY6pE5MHBYaoNhCDz4dygFLtVXrWtue924wY4cgPCSDjZC0hNAwo02VB8QMtT9/NqPG2HfmRGbhBoeAobNNFAVoTOe/pEPeQDYz9N7DqNd/GBuF7smg5/gRgh8419ceb070Ovgl7YlYENh8vqCg5ulf/aN2ethuV0EzchaAchhG366/QyIK3W9+IxhAM8IHyilWK/QqFytKX2uDz9Mn+zILLkYsv5/+6mL2ynrAxg/Pggx2RnA1YuAQzLnCl3twBe7njzt4IxBKBR8pM2DD6/Rf7/x0mjoUyBskkR+39pztp26D0yESLmGWa5xd41/PffJ98WRDEb5msyEW9dZE1FvI5B2VsuJTh2NhPUmnr1IqbXBw4G8qXjGYRj3zUs0OYAUekzuTs7DpNlE0ZEU8Ea0/JCn13N6CgyKpevJ0OvLmdAeLS6ducKndoqXl+WmAPBAjIVEbibrZouUk3We9Sb05cnQsyeHS6+D0Qveb34mDtQkqqJQgd0FKYr+f8USQXMrGWSJxlghgM2iG2uLxzRZ+9xdM2TtgSnfk3rPNgDPnuxwOVyxVxvC7q7/iPOuHOgPSnJz+tjVH10WtYoLljOXLFnRfuEt+vnbD15vD8sWKgWNBkdDzcV0CAE8lYuXeaRt277l/WKR5rYMlvnlANN0bQHQknVf3190LI58SbQIbLxQUrn16PoVghG4BUKKutvkoPtWprp7VuPrJ5NvV5CZhShpE+UgngXE2z24NP6sBt9TPptQdPyCbvvFGS5/LlqtuYfA03ZQs6ERh1fEqe4/mGYX1DcRCRu28tn6kd66GVSqggwyQjWc7NNjXx+QBsUNbn1UuSn9T9WKqphZQA9QP70aj3MZEqknh4NH/TA5HPD9w6w2MoBknACqpQabFIHtOlBmnnI/sNaABsUQSs6Wvd95+xGzb5bRsuDTLRHSGuZR4ks1u58RelRsAJTDKsqt2Dt3G3Qi+ZiCYQVVPOdjD6BQL3YSy7r87F0SNE20ill0oyNz+dNc0nBE5B0K5NY+MgNsg5PRGrAw7ui34zGE7vLcc6LGaFc9AASFFKDFCqFq4Hn2mFq7zvrez2t6utf9s7y6rcJzKgkiNiMwAAphvFBJBOsSJGm+NOGMkRIDRYmf89fllgXoSHPyZ5Hvpdcjc2pBbP4lhYDReCFTTaCEegrB5w9M+B1KYfRjYgITyCFEmr94vXz7sa+o08dl7J96iXpJdV/AoeDala/FyveLqa1oVA7p+8PKxxkPP+4aJiaWcstzuqHDpkdh9q9Tn4kZiX0RA4f6ILis7MyVVejRu374/PhGUdNHxBbujMiqHz6ci05C/QDh3U43YY3o1ducE2x7X2FD0TLznDGrASYi87ui1RyN8TQNwE98GuJ4E8TwXc7KHgWcr0EM1H8ADEMAhHge+DBLlKrfCJp0GXPwRWroaB9xehxcYoU8+9z7AA68BfsUHSHiDMwQIlSxPpTpb7XXMlfQdBrj4ja2J+gfnqVajapPAsZ8r9HLC+p8PwPuUVeJObf0KOu6TjUIH+9n94o44CJQH2OjONVYzDMMwDMMwDMMw7HFp9ivBp6zQTUfXmqZpmqZpmqZpmn6LyI6NIUX1yrOV1LvvHfWTy2txz74tNd4s+eYEfJklgM4HOrpg763Yb2PoU5YIMnH8PYgLoAYAAAAAAMA1/OqN0s567e2DgbXjs/ES0fACx92Ra1SLiIiIiIio0ltrYnUycbSSyHBsd+0MWmuttdZaa1231knY46tk2ke1GgzQzdbCAF6bXUV46WWQJzo32fmzx+9l6VfDzMzMzMxcmytnq78ZdfPslja7jSiljoiIiIiISBXxdiwbb72/udDU14ZOjNfDNeNqpm1fDSxZ9XB1w+nB45m1U5L/jpJARERERESsifg6N1FpmIMkwzJESQ6diN+zB3Acx3Ecx3Ecx2uOVxyIxeD1ajUfs9/Ufp6ITmjZ7OXYOBJSMK30aXN0jssWjaFIrrtmNduJNVhCS+EQ4qHOSRDWCCGEEEIIIYQVltDGUiBQ5hKqM0q47UmeCOurSynlTvXZk/bgngYZ2QmhkiRJkiRJkiRJ1pIkf4WN0ohwoykmZhh3OLxY553VNXU7MhmEEEIIIYQQqgmhqwGiALIOGe1HbS2DCLonmg429s4XS/v41aWrbSGWaYijIT1n35emSPNMRCn5EhuJ7kejSDE9D3PoF1qAs6daSm9rAgAAAAAAAIAaAOgNCNWEEEIIIYQQQuh7enAW3JqHdolhUrWiKIqiKIqiKIraqimPv1Paj+nCQ5p8MedXSzeLrO+c2bM3fnneljtqn/WmD0PUrvlTbix4vuDVoBi7pdl+1iwr37obuR3L9c7fl3YZ+Ama66zvuR7fdqCNrtuuF7vDjqw71x9qDMe+Y6Pxphlmh8Nl1nQ8tE98rzZTakorZMSBH2qqHSsjA08V+loyWd3P2EO08DOnp5jtWWjkjrJXDt1nr3s1AYSbzUucNPRGARYCoaBjvnmqgX+gYZuZpdxzft4dvNn+HYyf9nV+1/qr/l/9ScWd/ReiuY80fb9bTecaqmad2Ne8Xlzsq253EWV8Fqjayk3OpD0fyyln/DHeH+12rMbhgL9jthVNopVJUWWUsyn303g2tjMdabuRHbBfyYGKO7hQPeSv9bC1ZVmWZVmWZVmWZZP6pWuBSXPlyYZ0GQ+tpZ0ZTqEduuNOIhCaPVKcmAT38MstBKIciY/K6pnBqEAK0AzDMAzDMAzDMLVhKiOwHJeavHeKTdtmA0/NZI7QKkCdO1uif+pIssT9E5+zFXwh9vFl1E8JsZ7n3VzUrskwwQ5ZgBBCCCGEEMIaIbxvstqz4KiZzLLYXe3z1aKLLG1fvFpgV6O/GxxiJ5cX1qMTXssI7FqpWHV5b7B72AbOYzSvtFq1bXLtttLMK6My4vHGFIoN2xzJwQA+nnNpjK5Hkt+yVcKupPyUp/1gh7YOAqCS3a5oQuDlSg+HxWZSqXfN9sowpkb6Wd6H/dRgqFM5WCka46y+cyGdLkBknhPlk1X+bz4xn+Rsdv9i15S+2sNmcXM2SnXY8cQFdEFRg6ik1GpiaF1o8ygffWR+1n+7jNpy76GdTu1Rtppw0951O2mtzsxcO4k57gPR3yWs+0AN/QSsJUmSJEn+Rz683A6fFPZncfyuol2svQ3v2CRqAAAAAABdQXQjoHz7aU8B5q7AkuqK1aCUt1kshm23UaQ6SZIkSao6a2CaOn+WfiL9eEqk4z8Z/cL7NuB9/OWyTgBT7KLVKVMqA3h29i9FmrTB1BgcBu+Z2n6MN5xGrB3WIs6d23Udrt0Ot7cu7u1Koak84PUTk9P4PL5O3oPyvowPX9/7Sv+dTjndOsjmcOeZAaxRNoQQQgghhLDCOZobxJmJ7qGbv3sB5sUmrWI03kCr5dpWGGvjFHPlyJErZ87iJAwJTgVBKFb/q/WpUbG4GUtTs24xpiK2rtu50obOx0Pb0dPPtRvCtIscRH+kx+G2QVMfqP7qP38AgJRSVn/zZFsajdJqlTRZt75+bwtB/oDTip74dBh1JTElw2glkU4yEsEe2MncTOZLbge3ABAANKCJisuqh0meuTTg2BSb160krGi/w3x1HDq669YNM1gw9+nMP0oECZidvNPdft6fxuvyWlJtBijjEeKTVvp7ZKskkuTAGoIgCIIgCIIgCFYQbGzvYL3avbVdb4nDTyXLKOTnGbIEFSvObW9HaDoh9oNjaHIVJMXJkhS6eIqN/SikSJBgyXqwdly5D+43B58bW8vZR997j6Nf9I9xJblzKV5EkKbcxNUbR4ZTvAeCnT/IEKsHCRHOwm2qJZ6qRMScxdSsUz0QnymsrNnMsWB7379Y9eduW9Fc6FDfol5Jt52t2+uR6Ji1p3fOU4AUHkUngjNwkmr8JtshAd8LSBWTnDSh+tScViit1//9cAXfd/bqAu+36q5juj5vUadOnTp16tSpg0Kh0LpOXVUHxXxgjmALbI3dNQK7FGgxhIIQ0SB6qiUF9X69Axirx4wZM2bMmDFjxly5cuWqGitXSTARbb/altZEqBNk2WhsVsyy1c7boFtL83rHq9l14ixAqGR5KtXZaq9jrnQHUeudV6/swrK3ZLfd7RdQDmA5fFMwEI/icThJLHqZX7K7eHO+syx+n9Au4t/iX+NHMzjB9jNHDQBApVKpeParGiyz3QUVCjPjMLa8fZm3nwR9uNMk8NS9OyNrPi/LOWWoRIkgExC0gRZX4OoWGCQfEH+BLD8PJ9zJ9rx2au46ceDLIFGucits0mnAxQSat+Xb5jRu+XyUnPj6Qu4gF6EYFKkEFZFjJGObdJZuMmU61xbaTtOTE+N5vCs8PGk+zNsMm7bz9g0WMN/CN79++Vf67eRNCd1kTWhCf5NInGtCQwdtGZZOwokrV6THxeMSpaennsJ/9IdfWWZ7xRjHkFgXR+Nm3Irb9e+jOp7Eq/gQH8PT6YGtk/8OhqvEOYE+I0A6Zyi4vpDBiQgRO2TXT0FBQUFBQUEBgUAgtYKC4o56OiP+OEfo70EyQuYXZZKsxZDvQ3oiKAe9FZpDoayQpUYNRK1aDOrVI9AQGsbTArH1pV/NudXN2eqVUaZAgTK1uW9dNYZ26kGp6m7O4n6xcPnMLVekeONAWt6UQm2exwMkL4vvnQdGF5ToNo+YGfDVULNg5kwXpv0QfvzWQZJVlLnBty7g0SCGMboVD3vn5POmTNaVDBQJYl5aEHUcxVncKcdTxR/xV/V7k2d8sHenV3d29evOxbA77rn3Z4+UKUrtHh9hO12fdtppp5122mmnnTZixIiR6rSR8y9LN+Lfnvqq02kCtWIyJ+67bCqKdbMRX9YtRp73l+13Y8aMV2nXsO6+7Hk/vM99/D98/x12hybQdJrO541exx+yDdIL8/n5sbP/zchP6/xPBFx3RR/8y1/X89Wo20Tr70bBpbxECAdhokRd06ULx4iRIc6coeJDvKSQJGWf0i6rvXbyuANe3RucOsCbHCf5dD3rXHpiOk+D87n9R7WZpKwxMV9PY3E9EXv7vXiehiR2HDt2jelZfgb9Ff7yYnhxy+RdJxYrOke9dxUopZRSCh0dHb0uVdWc/k0/07/oERHhv12yTEz4wwbUdDQ94zP4dBfhxDwTSYnAMSpGh5hTRt4D3jEAAAAAAAAAAEANgAoAvBfe9zzv+XsioorYlAs9gAUMa8FaAvyz/zOLO2RpXto29KtD+f57AthFfoLT0C5afPAdk9Tia9PZWTg4L9szbgwu0S0eZ9f2s5E0tIzaTus2h2DPxJnePK0XQ44CC3xzygmd5I5Glih8LbrrtJxoWtcoGoqqY2mcXypgrByrlupBrI41F4tIesem1+aXwy/d7Qw7dehwzMDZQCY+Ko9heHJHJVktNp7iQLOSoXTuJXoXXfaAGsQCAAAAAAAAKsjgdzoou8mukc150VJaANWkBCGEEEIIIVTRnI6QvpB0atSoc9eSYnDN1mOMMcYYY4wrxno6pP+sP6z39Isr+/fxYHgPymG7W/VWBsoxLKdvpjMAkiLGi1JpFc76aoRnLEOUP/eS5ySsvrcq5bWUQjiKDqneV3cwuVolUieLFq0yZiO0XMT5WP+l60L5Wg0lzx55TCpiPrPMmJnF9wTct2ABg5ULVmw6xE4o563z1cwUapYuKsmS5StUOFepuAifmYhy5fNZdRd2/tvVQVCdw27wqFusB8GQhhowQBidz/KpcH22yTBZyr4IYylhrKzEm9eA79qZLyg3NwjEtLg0LquWb/Ylyy8P8LGmGhEREfHx8fErbq1+8tHS5fRfmfAJ8dlubdubY63lFycWJ65c3d7CCQHViYSE5JDnju2PFfvz32LzE4cIVuRNmT06wT87We+Lz4bbGrfled2DFcbXEspZ3AuGO//8Kx44WmJL3B3/jB/qz0cpBjGOSUxjFmXM4ySexLN4E2/D8+bwsT3+2lKmWh1NKyBoRUB49avDKRGc7kDRospUBAMGAnjxkoQLODJkaFMNqjRosGAaTFnfrOVLVYeq1qF12xokg/b3elkwB8R3S5Kf4vUwWth6RIfaUAMGT/fp7naZp7sK7im4PYr8woJYkMibXHuobgJ41wAOceDLIFGucits0mlgpP6saLi47sByyTXXvvcNPwXfCBSY838z57aiaVrufPvSkFuMdu2zaydHRwpOuAEtaWBzdA9Hg044Ua7tmXuwudz5y9uDjBjRMt67tzcASpvngAIHh4KI6MapCgQLECDY1C6My93mfml45iCvAtAe2u0edmfDr91Orb0ByK92o9QN+kO/gdlAaBYxRNLl9Nvj8mVolg8AoMLDFyr9v+fqmGt2JfxLfkaTgFlhVlL+5eOKZOTTmkvxHdd5xu+2yiPjiRAGoYEy7kET8Pb2t54mA8es5N9SgPELuWXaBx8CyKZ+E6hd7uSFkCzIZ3OZ+5NxKMvTZiJWWeWkzqZ/mt5d9rYRd/9uE+IvBoEJ/hu/OAuctByPlxbsQXm7tAnvvDOx+NxApAlegaAnt77EC3bJuqweieM4juM4EolEqjaXeB6lRBiCQbfuTUjr2duN79/hii3dfNAUvzbXP+/W2my/ZHzuF1/lyspZRIEC5QQx1+7RS/lB3GUNKddPw8WITVocR8fxpXpQJhe1admyKafzU05/qU/ulx8PbUD07I57W89oCJb34hEXYtX/SOUbgAAAtPQviUino8EfH7TCr/6/d8oPyc5Gq9qvihKhAp29wP/kFmNihl/4o1bmn2u0mZAF/kdnSeB3jMQppIdVDhClCzG7cbIbN/3oGeKYR6xKZVoZsjnJulK0yxbgVaJOJrySrFAG8eQqtwKFSlCoLNGK0MggXP7mlXq0SpbMhNJ4gFUmvCtDrjwAfv7iWFMAUkZ5ZSPasAOA7Ey2srz/aQIAwP/6j2KLfWUsV4Pj5J+KT/5uQ4sHcVX/N3wLbOkAzbsz29n/tSMgxpIrgp/u+MOgOsmqPa9Z+KuT/4d3/Xv7pv/Iek8BCsP9FHYJAIB9sQEQ6CdYwY2D+MpC1BABRDYsQAjeCDBpAgQEOcUCDi1bKT6Wf0og8CwngYhRNZSE/D6BLKSpCFNf2ZrBzoFUVgVAALEdFyB0DwSYzl9AsJMm4NhkAcXz/C6BICOXBSJpTaMk5K8XyKrq/MYxsdtCdyqNz8BjcSwDTmY4SExJPlA/o2b9pnFTTOJUUcITid6UajDIIWjEFMtYTE1CzZCDxlQzo5L9VGNLf2YI4G9gbHydxX9Gmg8YVyk5PBkkWpRf+jpClP+vgCT85r/PxgSAhC5SeSKbTN7id0goW21etGWZvwOxCJZN8mbArwUyFPrQ/w3c9RfwkDt8eAEY01ZZO3G76q4nA4scTFSNqYLmKsWAgkfTQFnyg4q8SMVyvd6C6X8pMmuI7NRiaKmhb2USMi6o6E+OApSh6e0MOfgLGss/EgH6aQf7Q6+RBRocNg4ZHKs/LOKL7YIBTH7YXBz6d8NrHXNA38bApDJSrqCUKrWeewARJpRxIRVV0w3Tsh3X88NkRdV0w7Rsx/V8sUQqI+UKSqlS6+kbGGb+DeOICILtnidXjPZ8rUKv1LT39arS4W3kJ8WQV659d83btWbR48NpL2nt6+q361Sqd7HI8KLoszmGa6uu0Yvmz3G5jgDXqSm6/HDG98w57SW9jnDrsZqOCgAAAA==') format('woff2');
font-weight: normal;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'UllikummiB';
src: url('data:font/woff2;charset=utf-8;base64,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') format('woff2');
font-weight: normal;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'UllikummiA';
src: url('data:font/woff2;charset=utf-8;base64,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') format('woff2');
font-weight: normal;
font-style: normal;
font-display: swap;
}
hzn7lgylehppz0sd8jb0y50h62svr55
88951
88950
2026-08-06T23:17:54Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-CssTest.css]]
88950
css
text/css
@font-face {
font-family: 'UllikummiC';
src: url('data:font/woff2;charset=utf-8;base64,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') format('woff2');
font-weight: normal;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'UllikummiB';
src: url('data:font/woff2;charset=utf-8;base64,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') format('woff2');
font-weight: normal;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'UllikummiA';
src: url('data:font/woff2;charset=utf-8;base64,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') format('woff2');
font-weight: normal;
font-style: normal;
font-display: swap;
}
hzn7lgylehppz0sd8jb0y50h62svr55
MediaWiki:Gadget-UnsupportedTitles.json
8
17975
88952
2024-12-25T15:39:44Z
en>J3133
0
Added text display (see [[Wiktionary:Grease pit/2022/August#Preventing emoji display in titles|Wiktionary:Grease pit/2022/August § Preventing emoji display in titles]]). (Most are symbols, but I do not oppose changing any if needed.)
88952
json
application/json
{
"UnsupportedCharacterEscapes": {
"grave": "`",
"num": "#",
"repl": "�",
"percnt": "%",
"tilde": "~",
"rcub": "}",
"amp": "\u0026",
"vert": "|",
"lcub": "{",
"lsqb": "[",
"gt": "\u003E",
"rsqb": "]",
"lt": "\u003C",
"lowbar": "_",
"period": "."
},
"UnsupportedTitles": {
"": "",
"Space": "[space]",
"Cifrão": "[Cifrão]",
"Ogham space": "[Ogham space]",
"Old Israeli shekel": "[Old Israeli shekel]",
"Ancient Greek dish": "λοπαδοτεμαχοσελαχογαλεοκρανιολειψανοδριμυποτριμματοσιλφιοκαραβομελιτοκατακεχυμενοκιχλεπικοσσυφοφαττοπεριστεραλεκτρυονοπτοκεφαλλιοκιγκλοπελειολαγῳοσιραιοβαφητραγανοπτερύγων",
"Thai name of Bangkok": "กรุงเทพมหานคร อมรรัตนโกสินทร์ มหินทรายุธยา มหาดิลกภพ นพรัตนราชธานีบูรีรมย์ อุดมราชนิเวศน์มหาสถาน อมรพิมานอวตารสถิต สักกะทัตติยวิษณุกรรมประสิทธิ์"
},
"TitleDisplay": {
"⌛": "⌛︎",
"⏩": "⏩︎",
"⏪": "⏪︎",
"☕": "☕︎",
"♀": "♀︎",
"♂": "♂︎",
"♈": "♈︎",
"♉": "♉︎",
"♊": "♊︎",
"♋": "♋︎",
"♌": "♌︎",
"♍": "♍︎",
"♎": "♎︎",
"♏": "♏︎",
"♐": "♐︎",
"♑": "♑︎",
"♒": "♒︎",
"♓": "♓︎",
"♿": "♿︎",
"⚓": "⚓︎",
"⚡": "⚡︎",
"⚪": "⚪︎",
"⚫": "⚫︎",
"⚽": "⚽︎",
"⛎": "⛎︎",
"⛔": "⛔︎",
"⛳": "⛳︎",
"⛺": "⛺︎",
"⛽": "⛽︎",
"✊": "✊︎",
"✋": "✋︎",
"✍": "✍︎",
"✨": "✨︎",
"➿": "➿︎",
"🆎": "🆎︎",
"🆑": "🆑︎",
"🆒": "🆒︎",
"🆓": "🆓︎",
"🆔": "🆔︎",
"🆕": "🆕︎",
"🆖": "🆖︎",
"🆗": "🆗︎",
"🆘": "🆘︎",
"🆙": "🆙︎",
"🈁": "🈁︎",
"🈚": "🈚︎",
"🈯": "🈯︎",
"🈲": "🈲︎",
"🈳": "🈳︎",
"🈴": "🈴︎",
"🈵": "🈵︎",
"🈶": "🈶︎",
"🉐": "🉐︎",
"🌀": "🌀︎",
"🌈": "🌈︎",
"🌐": "🌐︎",
"🌑": "🌑︎",
"🌒": "🌒︎",
"🌓": "🌓︎",
"🌔": "🌔︎",
"🌕": "🌕︎",
"🌖": "🌖︎",
"🌗": "🌗︎",
"🌘": "🌘︎",
"🌙": "🌙︎",
"🌞": "🌞︎",
"🌠": "🌠︎",
"🌮": "🌮︎",
"🌲": "🌲︎",
"🌹": "🌹︎",
"🌽": "🌽︎",
"🍀": "🍀︎",
"🍁": "🍁︎",
"🍅": "🍅︎",
"🍆": "🍆︎",
"🍇": "🍇︎",
"🍉": "🍉︎",
"🍋": "🍋︎",
"🍌": "🍌︎",
"🍑": "🍑︎",
"🍒": "🍒︎",
"🍔": "🍔︎",
"🍟": "🍟︎",
"🍤": "🍤︎",
"🍪": "🍪︎",
"🍴": "🍴︎",
"🍼": "🍼︎",
"🍿": "🍿︎",
"🎂": "🎂︎",
"🎃": "🎃︎",
"🎄": "🎄︎",
"🎅": "🎅︎",
"🎉": "🎉︎",
"🎓": "🎓︎",
"🎠": "🎠︎",
"🎡": "🎡︎",
"🎢": "🎢︎",
"🎤": "🎤︎",
"🎥": "🎥︎",
"🎨": "🎨︎",
"🎩": "🎩︎",
"🎭": "🎭︎",
"🎲": "🎲︎",
"🎵": "🎵︎",
"🏁": "🏁︎",
"🏠": "🏠︎",
"🐀": "🐀︎",
"🐁": "🐁︎",
"🐎": "🐎︎",
"🐐": "🐐︎",
"🐘": "🐘︎",
"🐙": "🐙︎",
"🐦": "🐦︎",
"🐱": "🐱︎",
"🐾": "🐾︎",
"👀": "👀︎",
"👂": "👂︎",
"👃": "👃︎",
"👆": "👆︎",
"👇": "👇︎",
"👊": "👊︎",
"👋": "👋︎",
"👌": "👌︎",
"👍": "👍︎",
"👎": "👎︎",
"👏": "👏︎",
"👑": "👑︎",
"👤": "👤︎",
"👥": "👥︎",
"👴": "👴︎",
"👵": "👵︎",
"👹": "👹︎",
"👼": "👼︎",
"👽": "👽︎",
"👾": "👾︎︎",
"👿": "👿︎︎",
"💀": "💀︎",
"💅": "💅︎",
"💉": "💉︎",
"💊": "💊︎",
"💍": "💍︎",
"💎": "💎︎",
"💓": "💓︎",
"💔": "💔︎",
"💕": "💕︎",
"💖": "💖︎",
"💙": "💙︎",
"💚": "💚︎",
"💛": "💛︎",
"💜": "💜︎",
"💝": "💝︎",
"💞": "💞︎",
"💠": "💠︎",
"💡": "💡︎",
"💢": "💢︎",
"💣": "💣︎",
"💤": "💤︎",
"💥": "💥︎",
"💦": "💦︎",
"💧": "💧︎",
"💨": "💨︎",
"💩": "💩︎",
"💪": "💪︎",
"💫": "💫︎",
"💬": "💬︎",
"💭": "💭︎",
"💮": "💮︎",
"💯": "💯︎",
"💱": "💱︎",
"💳": "💳︎",
"💸": "💸︎",
"💼": "💼︎",
"💾": "💾︎",
"📅": "📅︎",
"📈": "📈︎",
"📉": "📉︎",
"📋": "📋︎",
"📌": "📌︎",
"📍": "📍︎",
"📎": "📎︎",
"📖": "📖︎",
"📚": "📚︎",
"📝": "📝︎",
"📞": "📞︎",
"📠": "📠︎",
"📢": "📢︎",
"📣": "📣︎",
"📰": "📰︎",
"📲": "📲︎",
"🔀": "🔀︎",
"🔁": "🔁︎",
"🔂": "🔂︎",
"🔃": "🔃︎",
"🔄": "🔄︎",
"🔅": "🔅︎",
"🔆": "🔆︎",
"🔇": "🔇︎",
"🔈": "🔈︎",
"🔋": "🔋︎",
"🔍": "🔍︎",
"🔎": "🔎︎",
"🔑": "🔑︎",
"🔒": "🔒︎",
"🔓": "🔓︎",
"🔔": "🔔︎",
"🔕": "🔕︎",
"🔗": "🔗︎",
"🔘": "🔘︎",
"🔙": "🔙︎",
"🔚": "🔚︎",
"🔛": "🔛︎",
"🔜": "🔜︎",
"🔝": "🔝︎",
"🔞": "🔞︎",
"🔥": "🔥︎",
"🔧": "🔧︎",
"🔪": "🔪︎",
"🔫": "🔫︎",
"🔬": "🔬︎",
"🔭": "🔭︎",
"🔰": "🔰︎",
"🔴": "🔴︎",
"🔵": "🔵︎",
"🔻": "🔻︎",
"🕎": "🕎︎",
"🖕": "🖕︎",
"🖤": "🖤︎",
"🗽": "🗽︎",
"🗿": "🗿︎",
"😀": "😀︎",
"😁": "😁︎",
"😂": "😂︎",
"😃": "😃︎",
"😄": "😄︎",
"😅": "😅︎",
"😆": "😆︎",
"😇": "😇︎",
"😈": "😈︎",
"😉": "😉︎",
"😊": "😊︎",
"😋": "😋︎",
"😌": "😌︎",
"😍": "😍︎",
"😎": "😎︎",
"😏": "😏︎",
"😐": "😐︎",
"😔": "😔︎",
"😕": "😕︎",
"😗": "😗︎",
"😙": "😙︎",
"😚": "😚︎",
"😛": "😛︎",
"😜": "😜︎",
"😝": "😝︎",
"😠": "😠︎",
"😡": "😡︎",
"😤": "😤︎",
"😩": "😩︎",
"😫": "😫︎",
"😬": "😬︎",
"😭": "😭︎",
"😮": "😮︎",
"😱": "😱︎",
"😳": "😳︎",
"😶": "😶︎",
"😸": "😸︎",
"😺": "😺︎",
"🙃": "🙃︎",
"🙄": "🙄︎",
"🙈🙉🙊": "🙈︎🙉︎🙊︎",
"🙌": "🙌︎",
"🙏": "🙏︎",
"🚀": "🚀︎",
"🚂": "🚂︎",
"🚥": "🚥︎",
"🚦": "🚦︎",
"🚧": "🚧︎",
"🚨": "🚨︎",
"🚩": "🚩︎",
"🚬": "🚬︎",
"🚮": "🚮︎",
"🚰": "🚰︎",
"🚲": "🚲︎",
"🚴": "🚴︎",
"🚶": "🚶︎",
"🚹": "🚹︎",
"🚺": "🚺︎",
"🚻": "🚻︎",
"🚼": "🚼︎",
"🚽": "🚽︎",
"🚾": "🚾︎",
"🚿": "🚿︎",
"🛂": "🛂︎",
"🛃": "🛃︎",
"🛄": "🛄︎",
"🛅": "🛅︎",
"🛌": "🛌︎",
"🛑": "🛑︎",
"🛒": "🛒︎",
"🛗": "🛗︎",
"🛜": "🛜︎",
"🛝": "🛝︎",
"🛫": "🛫︎",
"🛬": "🛬︎",
"🟠": "🟠︎",
"🟡": "🟡︎",
"🟢": "🟢︎",
"🟣": "🟣︎",
"🟤": "🟤︎",
"🤏": "🤏︎",
"🤑": "🤑︎",
"🤒": "🤒︎",
"🤓": "🤓︎",
"🤔": "🤔︎",
"🤕": "🤕︎",
"🤖": "🤖︎",
"🤗": "🤗︎",
"🤝": "🤝︎",
"🤟": "🤟︎",
"🤡": "🤡︎",
"🤢": "🤢︎",
"🤣": "🤣︎",
"🤦": "🤦︎",
"🤧": "🤧︎",
"🤩": "🤩︎",
"🤫": "🤫︎",
"🤬": "🤬︎",
"🤮": "🤮︎",
"🤯": "🤯︎",
"🥀": "🥀︎",
"🥇": "🥇︎",
"🥚": "🥚︎",
"🥛": "🥛︎",
"🥜": "🥜︎",
"🥱": "🥱︎",
"🥲": "🥲︎",
"🥴": "🥴︎",
"🥵": "🥵︎",
"🥶": "🥶︎",
"🥷": "🥷︎",
"🥹": "🥹︎",
"🥺": "🥺︎",
"🥽": "🥽︎",
"🦀": "🦀︎",
"🦅": "🦅︎",
"🦎": "🦎︎",
"🦴": "🦴︎",
"🦽": "🦽︎",
"🧏": "🧏︎",
"🧠": "🧠︎",
"🧢": "🧢︎",
"🧩": "🧩︎",
"🧵": "🧵︎",
"🧷": "🧷︎",
"🧸": "🧸︎",
"🧹": "🧹︎",
"🩵": "🩵︎",
"🩶": "🩶︎",
"🩸": "🩸︎",
"🪐": "🪐︎",
"🪑": "🪑︎",
"🪢": "🪢︎",
"🪦": "🪦︎",
"🪩": "🪩︎",
"🪫": "🪫︎",
"": "︎",
"🫂": "🫂︎",
"🫡": "🫡︎",
"🫣": "🫣︎",
"🫰": "🫰︎",
"🫵": "🫵︎",
"🫶": "🫶︎"
}
}
aqsryl8nqgv36ixkm84l85nnsl1j9sy
88953
88952
2026-08-06T23:18:02Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-UnsupportedTitles.json]]
88952
json
application/json
{
"UnsupportedCharacterEscapes": {
"grave": "`",
"num": "#",
"repl": "�",
"percnt": "%",
"tilde": "~",
"rcub": "}",
"amp": "\u0026",
"vert": "|",
"lcub": "{",
"lsqb": "[",
"gt": "\u003E",
"rsqb": "]",
"lt": "\u003C",
"lowbar": "_",
"period": "."
},
"UnsupportedTitles": {
"": "",
"Space": "[space]",
"Cifrão": "[Cifrão]",
"Ogham space": "[Ogham space]",
"Old Israeli shekel": "[Old Israeli shekel]",
"Ancient Greek dish": "λοπαδοτεμαχοσελαχογαλεοκρανιολειψανοδριμυποτριμματοσιλφιοκαραβομελιτοκατακεχυμενοκιχλεπικοσσυφοφαττοπεριστεραλεκτρυονοπτοκεφαλλιοκιγκλοπελειολαγῳοσιραιοβαφητραγανοπτερύγων",
"Thai name of Bangkok": "กรุงเทพมหานคร อมรรัตนโกสินทร์ มหินทรายุธยา มหาดิลกภพ นพรัตนราชธานีบูรีรมย์ อุดมราชนิเวศน์มหาสถาน อมรพิมานอวตารสถิต สักกะทัตติยวิษณุกรรมประสิทธิ์"
},
"TitleDisplay": {
"⌛": "⌛︎",
"⏩": "⏩︎",
"⏪": "⏪︎",
"☕": "☕︎",
"♀": "♀︎",
"♂": "♂︎",
"♈": "♈︎",
"♉": "♉︎",
"♊": "♊︎",
"♋": "♋︎",
"♌": "♌︎",
"♍": "♍︎",
"♎": "♎︎",
"♏": "♏︎",
"♐": "♐︎",
"♑": "♑︎",
"♒": "♒︎",
"♓": "♓︎",
"♿": "♿︎",
"⚓": "⚓︎",
"⚡": "⚡︎",
"⚪": "⚪︎",
"⚫": "⚫︎",
"⚽": "⚽︎",
"⛎": "⛎︎",
"⛔": "⛔︎",
"⛳": "⛳︎",
"⛺": "⛺︎",
"⛽": "⛽︎",
"✊": "✊︎",
"✋": "✋︎",
"✍": "✍︎",
"✨": "✨︎",
"➿": "➿︎",
"🆎": "🆎︎",
"🆑": "🆑︎",
"🆒": "🆒︎",
"🆓": "🆓︎",
"🆔": "🆔︎",
"🆕": "🆕︎",
"🆖": "🆖︎",
"🆗": "🆗︎",
"🆘": "🆘︎",
"🆙": "🆙︎",
"🈁": "🈁︎",
"🈚": "🈚︎",
"🈯": "🈯︎",
"🈲": "🈲︎",
"🈳": "🈳︎",
"🈴": "🈴︎",
"🈵": "🈵︎",
"🈶": "🈶︎",
"🉐": "🉐︎",
"🌀": "🌀︎",
"🌈": "🌈︎",
"🌐": "🌐︎",
"🌑": "🌑︎",
"🌒": "🌒︎",
"🌓": "🌓︎",
"🌔": "🌔︎",
"🌕": "🌕︎",
"🌖": "🌖︎",
"🌗": "🌗︎",
"🌘": "🌘︎",
"🌙": "🌙︎",
"🌞": "🌞︎",
"🌠": "🌠︎",
"🌮": "🌮︎",
"🌲": "🌲︎",
"🌹": "🌹︎",
"🌽": "🌽︎",
"🍀": "🍀︎",
"🍁": "🍁︎",
"🍅": "🍅︎",
"🍆": "🍆︎",
"🍇": "🍇︎",
"🍉": "🍉︎",
"🍋": "🍋︎",
"🍌": "🍌︎",
"🍑": "🍑︎",
"🍒": "🍒︎",
"🍔": "🍔︎",
"🍟": "🍟︎",
"🍤": "🍤︎",
"🍪": "🍪︎",
"🍴": "🍴︎",
"🍼": "🍼︎",
"🍿": "🍿︎",
"🎂": "🎂︎",
"🎃": "🎃︎",
"🎄": "🎄︎",
"🎅": "🎅︎",
"🎉": "🎉︎",
"🎓": "🎓︎",
"🎠": "🎠︎",
"🎡": "🎡︎",
"🎢": "🎢︎",
"🎤": "🎤︎",
"🎥": "🎥︎",
"🎨": "🎨︎",
"🎩": "🎩︎",
"🎭": "🎭︎",
"🎲": "🎲︎",
"🎵": "🎵︎",
"🏁": "🏁︎",
"🏠": "🏠︎",
"🐀": "🐀︎",
"🐁": "🐁︎",
"🐎": "🐎︎",
"🐐": "🐐︎",
"🐘": "🐘︎",
"🐙": "🐙︎",
"🐦": "🐦︎",
"🐱": "🐱︎",
"🐾": "🐾︎",
"👀": "👀︎",
"👂": "👂︎",
"👃": "👃︎",
"👆": "👆︎",
"👇": "👇︎",
"👊": "👊︎",
"👋": "👋︎",
"👌": "👌︎",
"👍": "👍︎",
"👎": "👎︎",
"👏": "👏︎",
"👑": "👑︎",
"👤": "👤︎",
"👥": "👥︎",
"👴": "👴︎",
"👵": "👵︎",
"👹": "👹︎",
"👼": "👼︎",
"👽": "👽︎",
"👾": "👾︎︎",
"👿": "👿︎︎",
"💀": "💀︎",
"💅": "💅︎",
"💉": "💉︎",
"💊": "💊︎",
"💍": "💍︎",
"💎": "💎︎",
"💓": "💓︎",
"💔": "💔︎",
"💕": "💕︎",
"💖": "💖︎",
"💙": "💙︎",
"💚": "💚︎",
"💛": "💛︎",
"💜": "💜︎",
"💝": "💝︎",
"💞": "💞︎",
"💠": "💠︎",
"💡": "💡︎",
"💢": "💢︎",
"💣": "💣︎",
"💤": "💤︎",
"💥": "💥︎",
"💦": "💦︎",
"💧": "💧︎",
"💨": "💨︎",
"💩": "💩︎",
"💪": "💪︎",
"💫": "💫︎",
"💬": "💬︎",
"💭": "💭︎",
"💮": "💮︎",
"💯": "💯︎",
"💱": "💱︎",
"💳": "💳︎",
"💸": "💸︎",
"💼": "💼︎",
"💾": "💾︎",
"📅": "📅︎",
"📈": "📈︎",
"📉": "📉︎",
"📋": "📋︎",
"📌": "📌︎",
"📍": "📍︎",
"📎": "📎︎",
"📖": "📖︎",
"📚": "📚︎",
"📝": "📝︎",
"📞": "📞︎",
"📠": "📠︎",
"📢": "📢︎",
"📣": "📣︎",
"📰": "📰︎",
"📲": "📲︎",
"🔀": "🔀︎",
"🔁": "🔁︎",
"🔂": "🔂︎",
"🔃": "🔃︎",
"🔄": "🔄︎",
"🔅": "🔅︎",
"🔆": "🔆︎",
"🔇": "🔇︎",
"🔈": "🔈︎",
"🔋": "🔋︎",
"🔍": "🔍︎",
"🔎": "🔎︎",
"🔑": "🔑︎",
"🔒": "🔒︎",
"🔓": "🔓︎",
"🔔": "🔔︎",
"🔕": "🔕︎",
"🔗": "🔗︎",
"🔘": "🔘︎",
"🔙": "🔙︎",
"🔚": "🔚︎",
"🔛": "🔛︎",
"🔜": "🔜︎",
"🔝": "🔝︎",
"🔞": "🔞︎",
"🔥": "🔥︎",
"🔧": "🔧︎",
"🔪": "🔪︎",
"🔫": "🔫︎",
"🔬": "🔬︎",
"🔭": "🔭︎",
"🔰": "🔰︎",
"🔴": "🔴︎",
"🔵": "🔵︎",
"🔻": "🔻︎",
"🕎": "🕎︎",
"🖕": "🖕︎",
"🖤": "🖤︎",
"🗽": "🗽︎",
"🗿": "🗿︎",
"😀": "😀︎",
"😁": "😁︎",
"😂": "😂︎",
"😃": "😃︎",
"😄": "😄︎",
"😅": "😅︎",
"😆": "😆︎",
"😇": "😇︎",
"😈": "😈︎",
"😉": "😉︎",
"😊": "😊︎",
"😋": "😋︎",
"😌": "😌︎",
"😍": "😍︎",
"😎": "😎︎",
"😏": "😏︎",
"😐": "😐︎",
"😔": "😔︎",
"😕": "😕︎",
"😗": "😗︎",
"😙": "😙︎",
"😚": "😚︎",
"😛": "😛︎",
"😜": "😜︎",
"😝": "😝︎",
"😠": "😠︎",
"😡": "😡︎",
"😤": "😤︎",
"😩": "😩︎",
"😫": "😫︎",
"😬": "😬︎",
"😭": "😭︎",
"😮": "😮︎",
"😱": "😱︎",
"😳": "😳︎",
"😶": "😶︎",
"😸": "😸︎",
"😺": "😺︎",
"🙃": "🙃︎",
"🙄": "🙄︎",
"🙈🙉🙊": "🙈︎🙉︎🙊︎",
"🙌": "🙌︎",
"🙏": "🙏︎",
"🚀": "🚀︎",
"🚂": "🚂︎",
"🚥": "🚥︎",
"🚦": "🚦︎",
"🚧": "🚧︎",
"🚨": "🚨︎",
"🚩": "🚩︎",
"🚬": "🚬︎",
"🚮": "🚮︎",
"🚰": "🚰︎",
"🚲": "🚲︎",
"🚴": "🚴︎",
"🚶": "🚶︎",
"🚹": "🚹︎",
"🚺": "🚺︎",
"🚻": "🚻︎",
"🚼": "🚼︎",
"🚽": "🚽︎",
"🚾": "🚾︎",
"🚿": "🚿︎",
"🛂": "🛂︎",
"🛃": "🛃︎",
"🛄": "🛄︎",
"🛅": "🛅︎",
"🛌": "🛌︎",
"🛑": "🛑︎",
"🛒": "🛒︎",
"🛗": "🛗︎",
"🛜": "🛜︎",
"🛝": "🛝︎",
"🛫": "🛫︎",
"🛬": "🛬︎",
"🟠": "🟠︎",
"🟡": "🟡︎",
"🟢": "🟢︎",
"🟣": "🟣︎",
"🟤": "🟤︎",
"🤏": "🤏︎",
"🤑": "🤑︎",
"🤒": "🤒︎",
"🤓": "🤓︎",
"🤔": "🤔︎",
"🤕": "🤕︎",
"🤖": "🤖︎",
"🤗": "🤗︎",
"🤝": "🤝︎",
"🤟": "🤟︎",
"🤡": "🤡︎",
"🤢": "🤢︎",
"🤣": "🤣︎",
"🤦": "🤦︎",
"🤧": "🤧︎",
"🤩": "🤩︎",
"🤫": "🤫︎",
"🤬": "🤬︎",
"🤮": "🤮︎",
"🤯": "🤯︎",
"🥀": "🥀︎",
"🥇": "🥇︎",
"🥚": "🥚︎",
"🥛": "🥛︎",
"🥜": "🥜︎",
"🥱": "🥱︎",
"🥲": "🥲︎",
"🥴": "🥴︎",
"🥵": "🥵︎",
"🥶": "🥶︎",
"🥷": "🥷︎",
"🥹": "🥹︎",
"🥺": "🥺︎",
"🥽": "🥽︎",
"🦀": "🦀︎",
"🦅": "🦅︎",
"🦎": "🦎︎",
"🦴": "🦴︎",
"🦽": "🦽︎",
"🧏": "🧏︎",
"🧠": "🧠︎",
"🧢": "🧢︎",
"🧩": "🧩︎",
"🧵": "🧵︎",
"🧷": "🧷︎",
"🧸": "🧸︎",
"🧹": "🧹︎",
"🩵": "🩵︎",
"🩶": "🩶︎",
"🩸": "🩸︎",
"🪐": "🪐︎",
"🪑": "🪑︎",
"🪢": "🪢︎",
"🪦": "🪦︎",
"🪩": "🪩︎",
"🪫": "🪫︎",
"": "︎",
"🫂": "🫂︎",
"🫡": "🫡︎",
"🫣": "🫣︎",
"🫰": "🫰︎",
"🫵": "🫵︎",
"🫶": "🫶︎"
}
}
aqsryl8nqgv36ixkm84l85nnsl1j9sy
MediaWiki:Gadget-UnsupportedTitles.js
8
17976
88954
2024-07-24T16:04:52Z
en>Ioaxxere
0
try moving logic into a helper function
88954
javascript
text/javascript
/*
To get template for titleTag.textContent, process MediaWiki:pagetitle,
open your browser's JavaScript console on a Wiktionary page
and run this code:
((msg) => new mw.Api().getMessages(msg).then((messages) => {
mw.messages.set(messages);
const titleTemplate = new mw.Message(mw.messages, msg, ["$1"]).text();
const {
before,
after
} = /^(?<before>.*)\$1(?<after>.*)/.exec(titleTemplate).groups;
console.log(JSON.stringify(before), JSON.stringify(after))
})
.fail(console.error))("pagetitle")
We can do this for action=edit and action=submit by replacing
"pagetitle" with "editing" in this snippet.
*/
function setTitle(newTitle) {
// Set title tag, which determines the title shown in the browser.
var titleTag = document.getElementsByTagName('title')[0];
titleTag.textContent = newTitle + " - Wiktionary, the free dictionary";
// Set the text of the first heading above the page content.
document.getElementById('firstHeading').textContent = newTitle;
}
var UnsupportedTitlesJson = require('./UnsupportedTitles.json');
var prettyPageName = mw.config.get('wgPageName').replace(/_/g, ' ');
function generateCharacterToEscapeMap() {
var map = {};
Object.entries(UnsupportedTitlesJson.UnsupportedCharacterEscapes).forEach(function(entry) {
map[entry[1]] = entry[0];
});
return map;
}
// Helper function accessible from other scripts
function convertUnsupportedTitle(originalTitle) {
originalTitle = originalTitle.replaceAll("_", " ");
var match = /^(?:Talk:)?Unsupported titles\/(.+)$/.exec(originalTitle);
var newTitle;
if (match) {
var subpage = match && match[1];
newTitle = UnsupportedTitlesJson.UnsupportedTitles[subpage]
|| subpage.replace(/`([^`]+)`/g, function(wholeMatch, key) {
const character = UnsupportedTitlesJson.UnsupportedCharacterEscapes[key];
return character != null ? character : key;
});
} else {
newTitle = UnsupportedTitlesJson.TitleDisplay[originalTitle];
}
if (mw.config.get('wgCanonicalNamespace') == 'Talk')
newTitle = 'Talk:' + newTitle;
return newTitle
}
window.convertUnsupportedTitle = convertUnsupportedTitle;
/*
* On subpages of Unsupported titles and Talk:Unsupported titles, show the
* correct title in the header at the top of the page. For instance, on the page
* [[Unsupported titles/Left curly bracket]], show the title {.
*
* This is not enabled in the mobile version of the site.
*
* For all such pages, see [[Special:PrefixIndex/Unsupported titles]].
*/
if (mw.config.get('wgAction') === 'view'
&& (/^(?:Talk:)?Unsupported titles\//.test(prettyPageName)
|| UnsupportedTitlesJson.TitleDisplay.hasOwnProperty(prettyPageName))) {
$(function() {
try {
setTitle(convertUnsupportedTitle(prettyPageName));
} catch (e) {
console.log('Error while changing title: ' + e.message + '.');
}
});
}
/*
* This redirects a user to the correct Unsupported titles page if they attempt
* to access an invalid title. For instance, if you attempt to access the page
* for <
* https://en.wiktionary.org/wiki/%3C
* you will be redirected to
* https://en.wiktionary.org/wiki/Unsupported_titles/Less_than
*/
if (mw.config.get('wgCanonicalSpecialPageName') == 'Badtitle') {
// Avoid redirecting twice or more. Set this parameter after redirecting
// and don't redirect if it is already set.
const alreadyRedirectedParam = "UnsupportedTitleRedirected";
/*
* Generate the name of the page that the user attempted to access using
* either the portion of the URL after "/wiki/" or the value of the "title"
* parameter in the query. That is, the user is accessing the page using
* either the path /wiki/<invalid title> or a path similar to
* /w/index.php?title=<invalid title>.
*
* The actual page displayed if a user attempts to access an invalid title
* is the special page with the canonical title [[Special:Badtitle]].
*/
if (!mw.util.getParamValue(alreadyRedirectedParam)) {
var rxArticlePath = new RegExp('^' + mw.config.get('wgArticlePath').replace('$1', '(.*)') + '$');
var m = rxArticlePath.exec(location.pathname);
var title = m ? decodeURIComponent(m[1]) : mw.util.getParamValue('title');
const characterToEscapeMap = generateCharacterToEscapeMap();
var badTitleSubpage = title.replace(/./g, function (fullMatch) {
var escapeName = characterToEscapeMap[fullMatch];
return escapeName != null ? '`' + escapeName + '`' : fullMatch;
});
if (badTitleSubpage != title) {
const url = new URL(location.href);
const newTitle = 'Unsupported titles/' + badTitleSubpage;
if (url.searchParams.get("title")) {
url.searchParams.set("title", newTitle);
} else {
url.pathname = mw.util.getUrl(newTitle);
}
url.searchParams.set(alreadyRedirectedParam, "1");
location.href = url;
}
}
}
bry7wrzns91exdq5miv8mhq40r1pkgh
88955
88954
2026-08-06T23:18:11Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-UnsupportedTitles.js]]
88954
javascript
text/javascript
/*
To get template for titleTag.textContent, process MediaWiki:pagetitle,
open your browser's JavaScript console on a Wiktionary page
and run this code:
((msg) => new mw.Api().getMessages(msg).then((messages) => {
mw.messages.set(messages);
const titleTemplate = new mw.Message(mw.messages, msg, ["$1"]).text();
const {
before,
after
} = /^(?<before>.*)\$1(?<after>.*)/.exec(titleTemplate).groups;
console.log(JSON.stringify(before), JSON.stringify(after))
})
.fail(console.error))("pagetitle")
We can do this for action=edit and action=submit by replacing
"pagetitle" with "editing" in this snippet.
*/
function setTitle(newTitle) {
// Set title tag, which determines the title shown in the browser.
var titleTag = document.getElementsByTagName('title')[0];
titleTag.textContent = newTitle + " - Wiktionary, the free dictionary";
// Set the text of the first heading above the page content.
document.getElementById('firstHeading').textContent = newTitle;
}
var UnsupportedTitlesJson = require('./UnsupportedTitles.json');
var prettyPageName = mw.config.get('wgPageName').replace(/_/g, ' ');
function generateCharacterToEscapeMap() {
var map = {};
Object.entries(UnsupportedTitlesJson.UnsupportedCharacterEscapes).forEach(function(entry) {
map[entry[1]] = entry[0];
});
return map;
}
// Helper function accessible from other scripts
function convertUnsupportedTitle(originalTitle) {
originalTitle = originalTitle.replaceAll("_", " ");
var match = /^(?:Talk:)?Unsupported titles\/(.+)$/.exec(originalTitle);
var newTitle;
if (match) {
var subpage = match && match[1];
newTitle = UnsupportedTitlesJson.UnsupportedTitles[subpage]
|| subpage.replace(/`([^`]+)`/g, function(wholeMatch, key) {
const character = UnsupportedTitlesJson.UnsupportedCharacterEscapes[key];
return character != null ? character : key;
});
} else {
newTitle = UnsupportedTitlesJson.TitleDisplay[originalTitle];
}
if (mw.config.get('wgCanonicalNamespace') == 'Talk')
newTitle = 'Talk:' + newTitle;
return newTitle
}
window.convertUnsupportedTitle = convertUnsupportedTitle;
/*
* On subpages of Unsupported titles and Talk:Unsupported titles, show the
* correct title in the header at the top of the page. For instance, on the page
* [[Unsupported titles/Left curly bracket]], show the title {.
*
* This is not enabled in the mobile version of the site.
*
* For all such pages, see [[Special:PrefixIndex/Unsupported titles]].
*/
if (mw.config.get('wgAction') === 'view'
&& (/^(?:Talk:)?Unsupported titles\//.test(prettyPageName)
|| UnsupportedTitlesJson.TitleDisplay.hasOwnProperty(prettyPageName))) {
$(function() {
try {
setTitle(convertUnsupportedTitle(prettyPageName));
} catch (e) {
console.log('Error while changing title: ' + e.message + '.');
}
});
}
/*
* This redirects a user to the correct Unsupported titles page if they attempt
* to access an invalid title. For instance, if you attempt to access the page
* for <
* https://en.wiktionary.org/wiki/%3C
* you will be redirected to
* https://en.wiktionary.org/wiki/Unsupported_titles/Less_than
*/
if (mw.config.get('wgCanonicalSpecialPageName') == 'Badtitle') {
// Avoid redirecting twice or more. Set this parameter after redirecting
// and don't redirect if it is already set.
const alreadyRedirectedParam = "UnsupportedTitleRedirected";
/*
* Generate the name of the page that the user attempted to access using
* either the portion of the URL after "/wiki/" or the value of the "title"
* parameter in the query. That is, the user is accessing the page using
* either the path /wiki/<invalid title> or a path similar to
* /w/index.php?title=<invalid title>.
*
* The actual page displayed if a user attempts to access an invalid title
* is the special page with the canonical title [[Special:Badtitle]].
*/
if (!mw.util.getParamValue(alreadyRedirectedParam)) {
var rxArticlePath = new RegExp('^' + mw.config.get('wgArticlePath').replace('$1', '(.*)') + '$');
var m = rxArticlePath.exec(location.pathname);
var title = m ? decodeURIComponent(m[1]) : mw.util.getParamValue('title');
const characterToEscapeMap = generateCharacterToEscapeMap();
var badTitleSubpage = title.replace(/./g, function (fullMatch) {
var escapeName = characterToEscapeMap[fullMatch];
return escapeName != null ? '`' + escapeName + '`' : fullMatch;
});
if (badTitleSubpage != title) {
const url = new URL(location.href);
const newTitle = 'Unsupported titles/' + badTitleSubpage;
if (url.searchParams.get("title")) {
url.searchParams.set("title", newTitle);
} else {
url.pathname = mw.util.getUrl(newTitle);
}
url.searchParams.set(alreadyRedirectedParam, "1");
location.href = url;
}
}
}
bry7wrzns91exdq5miv8mhq40r1pkgh
MediaWiki:Gadget-catfix.js
8
17977
88956
2025-05-29T19:58:16Z
en>Surjection
0
88956
javascript
text/javascript
// <nowiki>
// Makes tweaks to links in language-specific categories, where we would normally
// be wrapping them in {{l}}.
for (let catfix of document.getElementsByClassName("catfix")) {
// Catfix looks something like this:
// <ul class="catfix" data-anchor="Russian"><span class="Cyrl" lang="ru"></span></ul>
// The outer element records the anchor that we need to use, while the child element is used as the wrapper.
// Applies a language-specific wrapper to the link based on the information from `catfix`.
// If the link is something like `Appendix:Toki Pona/nasin`,
// we need to wrap it like this: `Appendix:Toki Pona/<span prop="blah blah">nasin</span>`.
// Otherwise, put the span around the link and add an anchor.
function processLink(link) {
if (link.catfixed) return;
let wrapper = catfix.firstElementChild.cloneNode();
let pageName = link.textContent;
let ns = (new mw.Title(pageName)).getNamespaceId();
if (ns === 0) { // Mainspace
link.parentElement.append(wrapper);
wrapper.append(link);
link.hash = catfix.dataset.anchor;
} else if (ns === 100 && pageName.includes("/")) { // Appendix
let [prefix, ...title] = pageName.split("/");
// only do Appendix:ABC/def if ABC matches the catfix language
if (prefix.replace('Appendix:', '') === catfix.dataset.anchor.replaceAll('_', ' ')) {
title = title.join('/');
wrapper.textContent = title;
link.innerHTML = "";
link.append(prefix + "/", wrapper);
}
} else if (ns === 118) { // Reconstruction
// experiment: try replacing Reconstruction:XYZ/ with * for a cleaner layout
// (but only if XYZ matches the catfix's language!)
let [prefix, ...title] = pageName.split("/");
if (prefix.replace('Reconstruction:', '') === catfix.dataset.anchor.replaceAll('_', ' ')) {
title = title.join('/');
link.textContent = "*" + title;
link.parentElement.append(wrapper);
wrapper.append(link);
}
}
link.catfixed = true;
}
// There are currently two types of catfixes: those which apply to an entire category page,
// And those applying to a single category tree.
// This is a bit of a hack to tell the two kinds apart.
let categoryTree = catfix.nextElementSibling;
if (
categoryTree &&
(categoryTree.matches(".CategoryTreeTag") || categoryTree.querySelector(".CategoryTreeTag"))
) {
// Skip known empty CategoryTrees
var children = categoryTree.querySelector(".CategoryTreeChildren");
if (children) {
// Process category tree elements already present on the page.
categoryTree.querySelectorAll(".CategoryTreeChildren a").forEach(processLink);
// Also deal with any category tree data loaded dynamically using a MutationObserver.
(new MutationObserver(events => events
.flatMap(event => [...event.addedNodes])
.flatMap(node => [...node.querySelectorAll("a")])
.forEach(processLink)
)).observe(children, {
childList: true,
subtree: true
});
}
} else {
document.querySelectorAll(".mw-category li > a").forEach(processLink);
}
}
// </nowiki>
dfngdf1wzoq0uw62b2i6b7eflruxmjb
88957
88956
2026-08-06T23:18:18Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-catfix.js]]
88956
javascript
text/javascript
// <nowiki>
// Makes tweaks to links in language-specific categories, where we would normally
// be wrapping them in {{l}}.
for (let catfix of document.getElementsByClassName("catfix")) {
// Catfix looks something like this:
// <ul class="catfix" data-anchor="Russian"><span class="Cyrl" lang="ru"></span></ul>
// The outer element records the anchor that we need to use, while the child element is used as the wrapper.
// Applies a language-specific wrapper to the link based on the information from `catfix`.
// If the link is something like `Appendix:Toki Pona/nasin`,
// we need to wrap it like this: `Appendix:Toki Pona/<span prop="blah blah">nasin</span>`.
// Otherwise, put the span around the link and add an anchor.
function processLink(link) {
if (link.catfixed) return;
let wrapper = catfix.firstElementChild.cloneNode();
let pageName = link.textContent;
let ns = (new mw.Title(pageName)).getNamespaceId();
if (ns === 0) { // Mainspace
link.parentElement.append(wrapper);
wrapper.append(link);
link.hash = catfix.dataset.anchor;
} else if (ns === 100 && pageName.includes("/")) { // Appendix
let [prefix, ...title] = pageName.split("/");
// only do Appendix:ABC/def if ABC matches the catfix language
if (prefix.replace('Appendix:', '') === catfix.dataset.anchor.replaceAll('_', ' ')) {
title = title.join('/');
wrapper.textContent = title;
link.innerHTML = "";
link.append(prefix + "/", wrapper);
}
} else if (ns === 118) { // Reconstruction
// experiment: try replacing Reconstruction:XYZ/ with * for a cleaner layout
// (but only if XYZ matches the catfix's language!)
let [prefix, ...title] = pageName.split("/");
if (prefix.replace('Reconstruction:', '') === catfix.dataset.anchor.replaceAll('_', ' ')) {
title = title.join('/');
link.textContent = "*" + title;
link.parentElement.append(wrapper);
wrapper.append(link);
}
}
link.catfixed = true;
}
// There are currently two types of catfixes: those which apply to an entire category page,
// And those applying to a single category tree.
// This is a bit of a hack to tell the two kinds apart.
let categoryTree = catfix.nextElementSibling;
if (
categoryTree &&
(categoryTree.matches(".CategoryTreeTag") || categoryTree.querySelector(".CategoryTreeTag"))
) {
// Skip known empty CategoryTrees
var children = categoryTree.querySelector(".CategoryTreeChildren");
if (children) {
// Process category tree elements already present on the page.
categoryTree.querySelectorAll(".CategoryTreeChildren a").forEach(processLink);
// Also deal with any category tree data loaded dynamically using a MutationObserver.
(new MutationObserver(events => events
.flatMap(event => [...event.addedNodes])
.flatMap(node => [...node.querySelectorAll("a")])
.forEach(processLink)
)).observe(children, {
childList: true,
subtree: true
});
}
} else {
document.querySelectorAll(".mw-category li > a").forEach(processLink);
}
}
// </nowiki>
dfngdf1wzoq0uw62b2i6b7eflruxmjb
MediaWiki:Gadget-catfixRegrouper-Data.js
8
17978
88958
2026-06-01T00:27:48Z
en>Fenakhay
0
88958
javascript
text/javascript
// {{documentation}}
// <nowiki>
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, es5:true */
/* global mw */
// Configuration and data for [[MediaWiki:Gadget-catfixRegrouper.js]].
/** REGROUPER_DATA_LANG Data format:
* IMPORTANT! The regrouper already assumes that the language sort keys
* are set up correctly. It does not protect against the same group
* heading being repeated *AT ALL*. Do not enable the regrouper for
* a language until sort keys are set up for it first (so that
* each would-be group is contiguous).
* The data is organized by language code.
* If the code is missing or the value is undefined, the
* regrouper is disabled. This is the default.
* If it is ``true``, default values are used.
* Otherwise it is an object, with the following fields:
* group
* A function which is given the following parameters,
* in this order:
* - page title (minus namespace and language prefixes),
* - language code,
* - script code,
* - namespace number.
* ``this`` will be the language data object (so that you
* can add your own variables, functions, etc.; but if you
* do so, please prefix their names with x_).
* It should return the group, i.e. the heading that the
* page should be categorized under. If the value returned
* is undefined, null, ``false`` or an empty string,
* its existing group will be kept.
* If ``true``, the group falls back to the
* default grouping function.
* The default is the default grouping function, either of the
* script, or of ``defaultGroup``;
* see below for a description thereof.
* detectScript
* A function which is given the following parameters,
* in this order:
* - page title (minus namespace and language prefixes),
* - language code,
* - (default) script code,
* - namespace number.
* ``this`` will be the language data object (so that you
* can add your own variables, functions, etc.; but if you
* do so, please prefix their names with x_).
* It should return a script code, or undefined to use
* the default script.
* initials
* If the default grouping function is used, this can be a RegExp
* of initial letters/digraphs/etc. which are automatically mapped
* to that respective group (with titlecase conversion).
* initialFallback
* Only applies for the default grouping function and if initials
* is defined. If ``true``, the default grouping function falls
* back to the default logic when the initial doesn't match any
* specified in initials; if ``false`` (default), it just returns
* ``undefined`` (i.e. keeps the existing group).
* ignore
* Preceding characters to ignore instead of the default ones.
* This is in RegExp character class syntax.
* ignoreAdd
* Preceding characters to ignore in addition to the default ones.
* This is in RegExp character class syntax.
* removeDiacritics
* If specified, decomposition is applied, diacritics matching
* this pattern are removed, and then the remaining text is
* recomposed. Corresponds to remove_diacritics in language data.
* This only applies with the default grouping function.
* removeExceptions
* If specified, these precomposed characters are retained,
* even if the diacritic would otherwise be subject to removal
* as per removeDiacritics. Corresponds to remove_exceptions
* in language data. This must be a string containing characters,
* not a regex pattern.
* This only applies with the default grouping function.
* unsupported
* If ``true``, unsupported titles are passed directly to
* ``group``. If ``false`` (default), they are ignored, and their
* existing groups are kept.
* dottedDotlessI
* Used in case conversion; ``true`` means the language has both
* dotted and dotless I as separate letters (like in Turkish),
* and ``false`` (default) means it doesn't.
*
* REGROUPER_DATA_SC data format:
* This data is organized by script. Note that these configurations are
* still only considered for languages that have regrouping enabled.
*
* If missing, defaults are used. Else, an object may override:
* group
* A function that works just like group in REGROUPER_DATA_LANG.
* If missing, the default grouping function is used.
* The priority of grouping functions is:
* - group in language data,
* - group in script data,
* - default grouping.
*
* The default grouping function:
* Checks initials and initialFallback.
* If there are initials, it matches them first.
* Initial matching ignores certain preceding characters,
* e.g. hyphens.
* If an initial is found, the matching portion is converted
* to title case with ``titleCase`` and returned.
* Otherwise, we fall back to 'fallback' logic only if
* `initialFallback` is `true`, and else return `undefined`
* to keep the existing group.
* We may fall into the fallback logic:
* remove preceding characters (e.g. hyphens),
* take the first remaining Unicode character
* (or the first character in general if none would remain
* from the previous step),
* convert it to title case with ``titleCase`` and return it.
*/
window.RegrouperMetadata = function() {
const REGROUPER_COMMON_DIACRITICS = /[\u0300-\u034E\u0350-\u036F\u1AB0-\u1ACE\u1DC0-\u1DFF\u20D0-\u20F0\uFE20-\uFE2F]/g;
const REGROUPER_DATA_LANG = {
"da": {
initials: /[ÆØÅ]/i,
removeDiacritics: /[\u0300\u0301\u0302\u0303\u0304\u030B\u030C\u0327]/g,
removeExceptions: "Åå",
},
"et": {
initials: /[ŠZŽÕÄÖÜ]/i,
},
"fi": {
initials: /[Å]/i,
removeDiacritics: REGROUPER_COMMON_DIACRITICS,
removeExceptions: "ÅÄÖåäö",
},
// TODO check with editors.
// "hu": {
// initials: /(?:[ÁÉÍÓÖŐÚÜŰ]|C[sz]|Dzs?|[GLNT]y|Sz)/i,
// },
"liv": {
initials: /[ĀÄǞḐĒĪĻŅŌȮȰÕȬÖȪŖŠȚŪÜṺȲŽ]/i,
},
"lud": {
initials: /[ČŠZŽÜÄÖʹ]/i,
},
"mt": {
initials: /(?:Għ|Ie|[ĊĠĦŻCGHIZ])/i,
},
"sv": {
initials: /[Å]/i,
removeDiacritics: /[\u0300\u0301\u0302\u0303\u0304\u030B\u030C\u0327]/g,
removeExceptions: "Åå",
},
"vot": {
initials: /(?:[ČŠZŽÕÄÖÜ]|Tš)/i,
},
};
const REGROUPER_DATA_SC = {
};
this.getByLang = lang => REGROUPER_DATA_LANG[lang];
this.getBySc = sc => REGROUPER_DATA_SC[sc];
}
//</nowiki>
23l97ycefmpgfemndxsnvstxjvvb4j4
88959
88958
2026-08-06T23:18:26Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-catfixRegrouper-Data.js]]
88958
javascript
text/javascript
// {{documentation}}
// <nowiki>
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, es5:true */
/* global mw */
// Configuration and data for [[MediaWiki:Gadget-catfixRegrouper.js]].
/** REGROUPER_DATA_LANG Data format:
* IMPORTANT! The regrouper already assumes that the language sort keys
* are set up correctly. It does not protect against the same group
* heading being repeated *AT ALL*. Do not enable the regrouper for
* a language until sort keys are set up for it first (so that
* each would-be group is contiguous).
* The data is organized by language code.
* If the code is missing or the value is undefined, the
* regrouper is disabled. This is the default.
* If it is ``true``, default values are used.
* Otherwise it is an object, with the following fields:
* group
* A function which is given the following parameters,
* in this order:
* - page title (minus namespace and language prefixes),
* - language code,
* - script code,
* - namespace number.
* ``this`` will be the language data object (so that you
* can add your own variables, functions, etc.; but if you
* do so, please prefix their names with x_).
* It should return the group, i.e. the heading that the
* page should be categorized under. If the value returned
* is undefined, null, ``false`` or an empty string,
* its existing group will be kept.
* If ``true``, the group falls back to the
* default grouping function.
* The default is the default grouping function, either of the
* script, or of ``defaultGroup``;
* see below for a description thereof.
* detectScript
* A function which is given the following parameters,
* in this order:
* - page title (minus namespace and language prefixes),
* - language code,
* - (default) script code,
* - namespace number.
* ``this`` will be the language data object (so that you
* can add your own variables, functions, etc.; but if you
* do so, please prefix their names with x_).
* It should return a script code, or undefined to use
* the default script.
* initials
* If the default grouping function is used, this can be a RegExp
* of initial letters/digraphs/etc. which are automatically mapped
* to that respective group (with titlecase conversion).
* initialFallback
* Only applies for the default grouping function and if initials
* is defined. If ``true``, the default grouping function falls
* back to the default logic when the initial doesn't match any
* specified in initials; if ``false`` (default), it just returns
* ``undefined`` (i.e. keeps the existing group).
* ignore
* Preceding characters to ignore instead of the default ones.
* This is in RegExp character class syntax.
* ignoreAdd
* Preceding characters to ignore in addition to the default ones.
* This is in RegExp character class syntax.
* removeDiacritics
* If specified, decomposition is applied, diacritics matching
* this pattern are removed, and then the remaining text is
* recomposed. Corresponds to remove_diacritics in language data.
* This only applies with the default grouping function.
* removeExceptions
* If specified, these precomposed characters are retained,
* even if the diacritic would otherwise be subject to removal
* as per removeDiacritics. Corresponds to remove_exceptions
* in language data. This must be a string containing characters,
* not a regex pattern.
* This only applies with the default grouping function.
* unsupported
* If ``true``, unsupported titles are passed directly to
* ``group``. If ``false`` (default), they are ignored, and their
* existing groups are kept.
* dottedDotlessI
* Used in case conversion; ``true`` means the language has both
* dotted and dotless I as separate letters (like in Turkish),
* and ``false`` (default) means it doesn't.
*
* REGROUPER_DATA_SC data format:
* This data is organized by script. Note that these configurations are
* still only considered for languages that have regrouping enabled.
*
* If missing, defaults are used. Else, an object may override:
* group
* A function that works just like group in REGROUPER_DATA_LANG.
* If missing, the default grouping function is used.
* The priority of grouping functions is:
* - group in language data,
* - group in script data,
* - default grouping.
*
* The default grouping function:
* Checks initials and initialFallback.
* If there are initials, it matches them first.
* Initial matching ignores certain preceding characters,
* e.g. hyphens.
* If an initial is found, the matching portion is converted
* to title case with ``titleCase`` and returned.
* Otherwise, we fall back to 'fallback' logic only if
* `initialFallback` is `true`, and else return `undefined`
* to keep the existing group.
* We may fall into the fallback logic:
* remove preceding characters (e.g. hyphens),
* take the first remaining Unicode character
* (or the first character in general if none would remain
* from the previous step),
* convert it to title case with ``titleCase`` and return it.
*/
window.RegrouperMetadata = function() {
const REGROUPER_COMMON_DIACRITICS = /[\u0300-\u034E\u0350-\u036F\u1AB0-\u1ACE\u1DC0-\u1DFF\u20D0-\u20F0\uFE20-\uFE2F]/g;
const REGROUPER_DATA_LANG = {
"da": {
initials: /[ÆØÅ]/i,
removeDiacritics: /[\u0300\u0301\u0302\u0303\u0304\u030B\u030C\u0327]/g,
removeExceptions: "Åå",
},
"et": {
initials: /[ŠZŽÕÄÖÜ]/i,
},
"fi": {
initials: /[Å]/i,
removeDiacritics: REGROUPER_COMMON_DIACRITICS,
removeExceptions: "ÅÄÖåäö",
},
// TODO check with editors.
// "hu": {
// initials: /(?:[ÁÉÍÓÖŐÚÜŰ]|C[sz]|Dzs?|[GLNT]y|Sz)/i,
// },
"liv": {
initials: /[ĀÄǞḐĒĪĻŅŌȮȰÕȬÖȪŖŠȚŪÜṺȲŽ]/i,
},
"lud": {
initials: /[ČŠZŽÜÄÖʹ]/i,
},
"mt": {
initials: /(?:Għ|Ie|[ĊĠĦŻCGHIZ])/i,
},
"sv": {
initials: /[Å]/i,
removeDiacritics: /[\u0300\u0301\u0302\u0303\u0304\u030B\u030C\u0327]/g,
removeExceptions: "Åå",
},
"vot": {
initials: /(?:[ČŠZŽÕÄÖÜ]|Tš)/i,
},
};
const REGROUPER_DATA_SC = {
};
this.getByLang = lang => REGROUPER_DATA_LANG[lang];
this.getBySc = sc => REGROUPER_DATA_SC[sc];
}
//</nowiki>
23l97ycefmpgfemndxsnvstxjvvb4j4
MediaWiki:Gadget-catfixRegrouper.js
8
17979
88960
2026-05-13T23:39:38Z
en>Surjection
0
88960
javascript
text/javascript
// {{documentation}}
// <nowiki>
// implicit dependencies : mediawiki.Title, ext.gadget.catfixRegrouper-Data
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, esversion:6 */
/* global $, mw, RegrouperMetadata */
/* data is in [[MediaWiki:Gadget-catfixRegrouper-Data.js]] */
// characters to ignore when looking for the initial in a title.
// this is embedded inside a RegExp character class
const REGROUPER_INITIALS = "-";
function safeUpperCase(text, dottedDotlessI) {
if (dottedDotlessI)
return text.replace(/i/g, "İ").toUpperCase();
else
return text.toUpperCase();
}
function safeLowerCase(text, dottedDotlessI) {
if (dottedDotlessI)
return text.replace(/I/g, "ı").toLowerCase();
else
return text.toLowerCase();
}
function titleCase(text, lang, sc) {
return safeUpperCase(text.charAt(0), this.dottedDotlessI)
+ safeLowerCase(text.substring(1), this.dottedDotlessI);
}
function makeRemoveExceptionsMap(chars) {
const pairs = [];
let code = 0x100000;
for (let char of chars) {
pairs.push([char, String.fromCodePoint(code++)]);
}
return pairs;
}
function escapeRegexp(text) {
return text.replace(/[/\-\\^$*+?.()|[\]{}]/g, "\\$&");
}
function replaceAll(text, source, destination) {
return text.replace(new RegExp(escapeRegexp(source), "g"), destination);
}
function removeDiacritics(text, diacritics, exceptions) {
if (exceptions != null) {
for (let pair of exceptions) {
text = replaceAll(text, pair[0], pair[1]);
}
}
text = text.normalize("NFD");
text = text.replace(diacritics, "");
if (exceptions != null) {
for (let pair of exceptions) {
text = replaceAll(text, pair[1], pair[0]);
}
}
return text.normalize("NFC");
}
function defaultGroup(title, lang, sc) {
if (title.length < 1) return undefined;
let cleaned = title.replace(this._clean_regex, "");
if (this.removeDiacritics) {
cleaned = removeDiacritics(cleaned, this.removeDiacritics, this._removeExceptions_map);
}
if (this.initials) {
const initialMatch = cleaned.match(this._initials_regex);
if (initialMatch) {
return titleCase(initialMatch[0], lang, sc);
}
if (!this.initialFallback) return undefined;
}
title = cleaned || title;
return titleCase(title.charAt(0), lang, sc);
}
function makeGroup(groupText) {
const groupDiv = document.createElement("div");
groupDiv.className = "mw-category-group";
const groupH3 = document.createElement("h3");
groupH3.textContent = groupText;
groupDiv.append(groupH3);
const groupUl = document.createElement("ul");
groupDiv.append(groupUl);
return [groupDiv, groupUl];
}
function getLiText(el) {
const child = $(el).find("a, span").first();
const rawText = el.textContent || el.innerText;
return (child.length > 0 && child.text()) || rawText;
}
jQuery(() => {
'use strict';
let catfix;
// Apply only to pages in the Category namespace
// containing an element with the id "catfix".
// Set window.disableCatfixRegrouper to true to prevent this script from running.
if (!(!window.disableCatfixRegrouper
&& mw.config.get('wgNamespaceNumber') === 14
&& (catfix = $(".catfix")).length))
return;
catfix = catfix[0];
const catfixSpan = catfix.querySelector("span");
if (!catfixSpan) return;
const regrouperMeta = new RegrouperMetadata();
// Get the language name and script catfix.
const langName = decodeURIComponent(catfix.getAttribute("data-anchor").replace(/_/g, " "));
catfix = catfix.getElementsByTagName("*")[0] || document.createElement("span");
const lang = catfixSpan.getAttribute("lang");
const defaultSc = catfixSpan.classList[0] || "None";
const cachedScriptData = {};
if (!lang)
return;
const UNPREFIXED_NAMESPACES = ["", "Talk", "Citations"];
const PREFIXED_NAMESPACES = ["Appendix", "Appendix talk", "Reconstruction", "Reconstruction talk"];
function isEntry(namespaceName, pageName) {
// main, Talk, Citations,
// Reconstruction/Appendix (Talk) if it starts with language name and "/"
return UNPREFIXED_NAMESPACES.indexOf(namespaceName) !== -1
|| (PREFIXED_NAMESPACES.indexOf(namespaceName) !== -1
&& pageName.slice(0, langName.length + 1) === langName + "/");
}
const formattedNamespaces = mw.config.get("wgFormattedNamespaces");
const regrouperData = regrouperMeta.getByLang(lang);
if (!regrouperData) return;
// set up stuff for the default regrouper
regrouperData._clean_regex = new RegExp("^[" + ((regrouperData.ignoreAdd || "") + (regrouperData.ignore || REGROUPER_INITIALS)) + "]+");
if (regrouperData.initials)
regrouperData._initials_regex = new RegExp("^" + regrouperData.initials.source, regrouperData.initials.flags);
if (regrouperData.removeExceptions)
regrouperData._removeExceptions_map = makeRemoveExceptionsMap(regrouperData.removeExceptions);
const groupFunction = regrouperData.group;
const detectScriptFunction = regrouperData.detectScript;
function getGroup(pageTitle, oldGroup) {
const titleobj = new mw.Title(pageTitle);
const namespaceId = titleobj.getNamespaceId();
const namespaceName = formattedNamespaces[namespaceId];
const pageName = titleobj.getMainText();
if (!isEntry(namespaceName, pageName))
return oldGroup;
// verify language prefix if the namespace should have one
let formattedTitle = pageName;
const langPrefix = langName + "/";
if (PREFIXED_NAMESPACES.indexOf(namespaceName) !== -1) {
if (formattedTitle.startsWith(langPrefix)) {
formattedTitle = formattedTitle.substring(langPrefix.length);
} else {
return oldGroup;
}
}
// ignore unsupported titles unless the language data requests otherwise
if (formattedTitle.startsWith("Unsupported titles/") && !regrouperData.unsupported)
return oldGroup;
// script detection
let sc = defaultSc;
if (detectScriptFunction)
sc = detectScriptFunction.call(regrouperData, formattedTitle, lang, sc, namespaceId) || sc;
let scData = cachedScriptData[sc];
if (!scData)
scData = cachedScriptData[sc] = regrouperMeta.getBySc(sc) || {};
let newGroup = true;
// the language group function may return true to fall back
if (groupFunction)
newGroup = groupFunction.call(regrouperData, formattedTitle, lang, sc, namespaceId);
if (newGroup === true && scData.group)
newGroup = scData.group.call(regrouperData, formattedTitle, lang, sc, namespaceId);
if (newGroup === true)
newGroup = defaultGroup.call(regrouperData, formattedTitle, lang, sc, namespaceId);
return newGroup || oldGroup;
}
const GROUP_QUERY = "#mw-pages > .mw-content-ltr .mw-category-group";
let regroupOk = true;
const regroupData = new Map();
// Process each group in the category listing.
jQuery(GROUP_QUERY)
.each(function () {
// Get the existing group.
const group = $(this).find("h3").first().text();
if (!group) {
// Failed to get group -- something has gone wrong.
regroupOk = false;
return;
}
$(this).find("li")
.each(function () {
try {
const liText = getLiText(this);
const newGroup = getGroup(liText, group);
regroupData.set(liText, newGroup);
} catch (e) {
console.error(e);
regroupOk = false;
}
});
});
// Find the existing groups, which we will delete.
const groups = jQuery(GROUP_QUERY);
// Cannot regroup if there are no groups.
if (!groups.length) return;
const parent = groups.first().parent()[0];
if (!parent) return;
const fragment = document.createDocumentFragment();
if (regroupOk) {
let lastGroup, groupUl;
jQuery(GROUP_QUERY + " li")
.each(function () {
const liText = getLiText(this);
const newGroup = regroupData.get(liText) || "";
if (lastGroup !== newGroup) {
const elements = makeGroup(newGroup);
const groupDiv = elements[0];
fragment.appendChild(groupDiv);
groupUl = elements[1];
lastGroup = newGroup;
}
groupUl.appendChild(this);
});
groups.remove();
parent.appendChild(fragment);
}
});
// </nowiki>
4pdvkpkfjbwt3g1mhmh3mkt22id34z1
88961
88960
2026-08-06T23:18:40Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-catfixRegrouper.js]]
88960
javascript
text/javascript
// {{documentation}}
// <nowiki>
// implicit dependencies : mediawiki.Title, ext.gadget.catfixRegrouper-Data
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, esversion:6 */
/* global $, mw, RegrouperMetadata */
/* data is in [[MediaWiki:Gadget-catfixRegrouper-Data.js]] */
// characters to ignore when looking for the initial in a title.
// this is embedded inside a RegExp character class
const REGROUPER_INITIALS = "-";
function safeUpperCase(text, dottedDotlessI) {
if (dottedDotlessI)
return text.replace(/i/g, "İ").toUpperCase();
else
return text.toUpperCase();
}
function safeLowerCase(text, dottedDotlessI) {
if (dottedDotlessI)
return text.replace(/I/g, "ı").toLowerCase();
else
return text.toLowerCase();
}
function titleCase(text, lang, sc) {
return safeUpperCase(text.charAt(0), this.dottedDotlessI)
+ safeLowerCase(text.substring(1), this.dottedDotlessI);
}
function makeRemoveExceptionsMap(chars) {
const pairs = [];
let code = 0x100000;
for (let char of chars) {
pairs.push([char, String.fromCodePoint(code++)]);
}
return pairs;
}
function escapeRegexp(text) {
return text.replace(/[/\-\\^$*+?.()|[\]{}]/g, "\\$&");
}
function replaceAll(text, source, destination) {
return text.replace(new RegExp(escapeRegexp(source), "g"), destination);
}
function removeDiacritics(text, diacritics, exceptions) {
if (exceptions != null) {
for (let pair of exceptions) {
text = replaceAll(text, pair[0], pair[1]);
}
}
text = text.normalize("NFD");
text = text.replace(diacritics, "");
if (exceptions != null) {
for (let pair of exceptions) {
text = replaceAll(text, pair[1], pair[0]);
}
}
return text.normalize("NFC");
}
function defaultGroup(title, lang, sc) {
if (title.length < 1) return undefined;
let cleaned = title.replace(this._clean_regex, "");
if (this.removeDiacritics) {
cleaned = removeDiacritics(cleaned, this.removeDiacritics, this._removeExceptions_map);
}
if (this.initials) {
const initialMatch = cleaned.match(this._initials_regex);
if (initialMatch) {
return titleCase(initialMatch[0], lang, sc);
}
if (!this.initialFallback) return undefined;
}
title = cleaned || title;
return titleCase(title.charAt(0), lang, sc);
}
function makeGroup(groupText) {
const groupDiv = document.createElement("div");
groupDiv.className = "mw-category-group";
const groupH3 = document.createElement("h3");
groupH3.textContent = groupText;
groupDiv.append(groupH3);
const groupUl = document.createElement("ul");
groupDiv.append(groupUl);
return [groupDiv, groupUl];
}
function getLiText(el) {
const child = $(el).find("a, span").first();
const rawText = el.textContent || el.innerText;
return (child.length > 0 && child.text()) || rawText;
}
jQuery(() => {
'use strict';
let catfix;
// Apply only to pages in the Category namespace
// containing an element with the id "catfix".
// Set window.disableCatfixRegrouper to true to prevent this script from running.
if (!(!window.disableCatfixRegrouper
&& mw.config.get('wgNamespaceNumber') === 14
&& (catfix = $(".catfix")).length))
return;
catfix = catfix[0];
const catfixSpan = catfix.querySelector("span");
if (!catfixSpan) return;
const regrouperMeta = new RegrouperMetadata();
// Get the language name and script catfix.
const langName = decodeURIComponent(catfix.getAttribute("data-anchor").replace(/_/g, " "));
catfix = catfix.getElementsByTagName("*")[0] || document.createElement("span");
const lang = catfixSpan.getAttribute("lang");
const defaultSc = catfixSpan.classList[0] || "None";
const cachedScriptData = {};
if (!lang)
return;
const UNPREFIXED_NAMESPACES = ["", "Talk", "Citations"];
const PREFIXED_NAMESPACES = ["Appendix", "Appendix talk", "Reconstruction", "Reconstruction talk"];
function isEntry(namespaceName, pageName) {
// main, Talk, Citations,
// Reconstruction/Appendix (Talk) if it starts with language name and "/"
return UNPREFIXED_NAMESPACES.indexOf(namespaceName) !== -1
|| (PREFIXED_NAMESPACES.indexOf(namespaceName) !== -1
&& pageName.slice(0, langName.length + 1) === langName + "/");
}
const formattedNamespaces = mw.config.get("wgFormattedNamespaces");
const regrouperData = regrouperMeta.getByLang(lang);
if (!regrouperData) return;
// set up stuff for the default regrouper
regrouperData._clean_regex = new RegExp("^[" + ((regrouperData.ignoreAdd || "") + (regrouperData.ignore || REGROUPER_INITIALS)) + "]+");
if (regrouperData.initials)
regrouperData._initials_regex = new RegExp("^" + regrouperData.initials.source, regrouperData.initials.flags);
if (regrouperData.removeExceptions)
regrouperData._removeExceptions_map = makeRemoveExceptionsMap(regrouperData.removeExceptions);
const groupFunction = regrouperData.group;
const detectScriptFunction = regrouperData.detectScript;
function getGroup(pageTitle, oldGroup) {
const titleobj = new mw.Title(pageTitle);
const namespaceId = titleobj.getNamespaceId();
const namespaceName = formattedNamespaces[namespaceId];
const pageName = titleobj.getMainText();
if (!isEntry(namespaceName, pageName))
return oldGroup;
// verify language prefix if the namespace should have one
let formattedTitle = pageName;
const langPrefix = langName + "/";
if (PREFIXED_NAMESPACES.indexOf(namespaceName) !== -1) {
if (formattedTitle.startsWith(langPrefix)) {
formattedTitle = formattedTitle.substring(langPrefix.length);
} else {
return oldGroup;
}
}
// ignore unsupported titles unless the language data requests otherwise
if (formattedTitle.startsWith("Unsupported titles/") && !regrouperData.unsupported)
return oldGroup;
// script detection
let sc = defaultSc;
if (detectScriptFunction)
sc = detectScriptFunction.call(regrouperData, formattedTitle, lang, sc, namespaceId) || sc;
let scData = cachedScriptData[sc];
if (!scData)
scData = cachedScriptData[sc] = regrouperMeta.getBySc(sc) || {};
let newGroup = true;
// the language group function may return true to fall back
if (groupFunction)
newGroup = groupFunction.call(regrouperData, formattedTitle, lang, sc, namespaceId);
if (newGroup === true && scData.group)
newGroup = scData.group.call(regrouperData, formattedTitle, lang, sc, namespaceId);
if (newGroup === true)
newGroup = defaultGroup.call(regrouperData, formattedTitle, lang, sc, namespaceId);
return newGroup || oldGroup;
}
const GROUP_QUERY = "#mw-pages > .mw-content-ltr .mw-category-group";
let regroupOk = true;
const regroupData = new Map();
// Process each group in the category listing.
jQuery(GROUP_QUERY)
.each(function () {
// Get the existing group.
const group = $(this).find("h3").first().text();
if (!group) {
// Failed to get group -- something has gone wrong.
regroupOk = false;
return;
}
$(this).find("li")
.each(function () {
try {
const liText = getLiText(this);
const newGroup = getGroup(liText, group);
regroupData.set(liText, newGroup);
} catch (e) {
console.error(e);
regroupOk = false;
}
});
});
// Find the existing groups, which we will delete.
const groups = jQuery(GROUP_QUERY);
// Cannot regroup if there are no groups.
if (!groups.length) return;
const parent = groups.first().parent()[0];
if (!parent) return;
const fragment = document.createDocumentFragment();
if (regroupOk) {
let lastGroup, groupUl;
jQuery(GROUP_QUERY + " li")
.each(function () {
const liText = getLiText(this);
const newGroup = regroupData.get(liText) || "";
if (lastGroup !== newGroup) {
const elements = makeGroup(newGroup);
const groupDiv = elements[0];
fragment.appendChild(groupDiv);
groupUl = elements[1];
lastGroup = newGroup;
}
groupUl.appendChild(this);
});
groups.remove();
parent.appendChild(fragment);
}
});
// </nowiki>
4pdvkpkfjbwt3g1mhmh3mkt22id34z1
MediaWiki:Gadget-nearby.js
8
17980
88962
2025-12-13T23:46:16Z
en>Ioaxxere
0
adjust display text for mammoth pages
88962
javascript
text/javascript
// <nowiki>
(async () => {
// Do a preliminary check to avoid running the gadget unnecessarily.
if ([...document.querySelectorAll("#catlinks li")].some(cat => cat.textContent.endsWith(" lemmas"))) {
mw.util.addCSS(`
.nearby-entries-box {
padding: 6px;
margin-top: 10px;
background: var(--wikt-palette-palergreen);
width: fit-content;
max-width: 100%;
border: 1.5px solid var(--wikt-palette-forestgreen);
border-radius: 2px;
overflow-wrap: break-word;
}
`);
let actionAPI = new mw.Api({ajax: {headers: {"Api-User-Agent": "Gadget developed by [[User:Ioaxxere]]"}}});
let pageName = mw.config.values.wgPageName;
const N_ENTRIES = 3; // the number of preview entries on each side of the current page
// Given a page title (no underscores), return the prettified title.
const makeDisplayTitle = rawTitle => {
rawTitle = rawTitle.replaceAll("_", " ").replace(/\/languages [A-Z] to [A-Z]$/g, "");
if (rawTitle.startsWith("Reconstruction:"))
return `*${rawTitle.split("/").pop()}`;
if (rawTitle.startsWith("Appendix:"))
return rawTitle.split("/").pop();
if (rawTitle.startsWith("Unsupported titles/"))
return convertUnsupportedTitle(rawTitle); // from [[MediaWiki:Gadget-UnsupportedTitles.js]]
return rawTitle;
};
// Build pageCategorySortkeys, a map from each page category to its associated sortkey.
let pageCategorySortkeys = new Map();
let continueParam;
while (true) {
let params = {
action: "query",
format: "json",
cllimit: "max",
clprop: "sortkey",
prop: "categories",
titles: pageName
};
if (continueParam)
params.clcontinue = continueParam;
let response = await actionAPI.get(params);
for (let category of Object.values(response.query.pages)[0].categories)
pageCategorySortkeys.set(category.title, category.sortkeyprefix);
if (!response.continue) break;
continueParam = response.continue.clcontinue;
}
document.querySelectorAll(".mw-heading2 h2").forEach(async h2 => {
let language = h2.id.replaceAll("_", " ");
let category = `Category:${language} lemmas`;
if (!pageCategorySortkeys.has(category)) return; // skip non-lemmas and malformed entries
let params = {
action: "query",
cmlimit: 40, // arbitrary; we don't know how many we need ahead of time
format: "json",
list: "categorymembers",
cmsort: "sortkey",
cmtitle: category,
cmstartsortkeyprefix: pageCategorySortkeys.get(category)
};
let titles = (await actionAPI.get(params)).query.categorymembers
.filter(page => page.ns !== 14) // Filter out namespace 14 (Category:)
.map(page => page.title);
// pageName is guaranteed to be in this list unless there are over `cmlimit` pages with the same sortkey.
let currPagePosition = titles.indexOf(pageName.replaceAll("_", " "));
let entriesAhead = titles.slice(currPagePosition + 1, currPagePosition + N_ENTRIES + 1);
let entriesBehind = titles.slice(Math.max(currPagePosition - N_ENTRIES, 0), currPagePosition);
if (entriesBehind.length < N_ENTRIES) {
// If entriesBehind isn't yet long enough, switch direction and get the entries coming before.
// There are no duplicates between the first and second queries.
params.cmdir = "desc";
entriesBehind = (await actionAPI.get(params)).query.categorymembers
.filter(page => page.ns !== 14)
.map(page => page.title)
.slice(0, N_ENTRIES - entriesBehind.length)
.reverse()
.concat(entriesBehind);
}
// Display nearby entries on the page.
let nearbyEntriesDiv = document.createElement("div");
nearbyEntriesDiv.className = "nearby-entries-box";
nearbyEntriesDiv.innerText = "» ";
// Need to account for both HTML formats.
if (h2.parentElement.nextElementSibling.tagName === "SECTION")
h2.parentElement.nextElementSibling.prepend(nearbyEntriesDiv);
else
h2.parentElement.parentElement.insertBefore(nearbyEntriesDiv, h2.parentElement.nextElementSibling);
for (let entry of entriesBehind) {
let entryLink = document.createElement("a");
entryLink.href = `/wiki/${mw.util.wikiUrlencode(entry)}#${h2.id}`;
entryLink.title = entry;
entryLink.textContent = makeDisplayTitle(entry);
nearbyEntriesDiv.append(entryLink, " • ");
}
let selflink = document.createElement("a");
selflink.className = "mw-selflink selflink";
selflink.textContent = makeDisplayTitle(pageName);
nearbyEntriesDiv.append(selflink);
for (let entry of entriesAhead) {
let entryLink = document.createElement("a");
entryLink.href = `/wiki/${mw.util.wikiUrlencode(entry)}#${h2.id}`;
entryLink.title = entry;
entryLink.textContent = makeDisplayTitle(entry);
nearbyEntriesDiv.append(" • ", entryLink);
}
});
}
})();
// </nowiki>
qrnvvsk960y6rne2zimvyueuhlrbfh9
88963
88962
2026-08-06T23:21:08Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-nearby.js]]
88962
javascript
text/javascript
// <nowiki>
(async () => {
// Do a preliminary check to avoid running the gadget unnecessarily.
if ([...document.querySelectorAll("#catlinks li")].some(cat => cat.textContent.endsWith(" lemmas"))) {
mw.util.addCSS(`
.nearby-entries-box {
padding: 6px;
margin-top: 10px;
background: var(--wikt-palette-palergreen);
width: fit-content;
max-width: 100%;
border: 1.5px solid var(--wikt-palette-forestgreen);
border-radius: 2px;
overflow-wrap: break-word;
}
`);
let actionAPI = new mw.Api({ajax: {headers: {"Api-User-Agent": "Gadget developed by [[User:Ioaxxere]]"}}});
let pageName = mw.config.values.wgPageName;
const N_ENTRIES = 3; // the number of preview entries on each side of the current page
// Given a page title (no underscores), return the prettified title.
const makeDisplayTitle = rawTitle => {
rawTitle = rawTitle.replaceAll("_", " ").replace(/\/languages [A-Z] to [A-Z]$/g, "");
if (rawTitle.startsWith("Reconstruction:"))
return `*${rawTitle.split("/").pop()}`;
if (rawTitle.startsWith("Appendix:"))
return rawTitle.split("/").pop();
if (rawTitle.startsWith("Unsupported titles/"))
return convertUnsupportedTitle(rawTitle); // from [[MediaWiki:Gadget-UnsupportedTitles.js]]
return rawTitle;
};
// Build pageCategorySortkeys, a map from each page category to its associated sortkey.
let pageCategorySortkeys = new Map();
let continueParam;
while (true) {
let params = {
action: "query",
format: "json",
cllimit: "max",
clprop: "sortkey",
prop: "categories",
titles: pageName
};
if (continueParam)
params.clcontinue = continueParam;
let response = await actionAPI.get(params);
for (let category of Object.values(response.query.pages)[0].categories)
pageCategorySortkeys.set(category.title, category.sortkeyprefix);
if (!response.continue) break;
continueParam = response.continue.clcontinue;
}
document.querySelectorAll(".mw-heading2 h2").forEach(async h2 => {
let language = h2.id.replaceAll("_", " ");
let category = `Category:${language} lemmas`;
if (!pageCategorySortkeys.has(category)) return; // skip non-lemmas and malformed entries
let params = {
action: "query",
cmlimit: 40, // arbitrary; we don't know how many we need ahead of time
format: "json",
list: "categorymembers",
cmsort: "sortkey",
cmtitle: category,
cmstartsortkeyprefix: pageCategorySortkeys.get(category)
};
let titles = (await actionAPI.get(params)).query.categorymembers
.filter(page => page.ns !== 14) // Filter out namespace 14 (Category:)
.map(page => page.title);
// pageName is guaranteed to be in this list unless there are over `cmlimit` pages with the same sortkey.
let currPagePosition = titles.indexOf(pageName.replaceAll("_", " "));
let entriesAhead = titles.slice(currPagePosition + 1, currPagePosition + N_ENTRIES + 1);
let entriesBehind = titles.slice(Math.max(currPagePosition - N_ENTRIES, 0), currPagePosition);
if (entriesBehind.length < N_ENTRIES) {
// If entriesBehind isn't yet long enough, switch direction and get the entries coming before.
// There are no duplicates between the first and second queries.
params.cmdir = "desc";
entriesBehind = (await actionAPI.get(params)).query.categorymembers
.filter(page => page.ns !== 14)
.map(page => page.title)
.slice(0, N_ENTRIES - entriesBehind.length)
.reverse()
.concat(entriesBehind);
}
// Display nearby entries on the page.
let nearbyEntriesDiv = document.createElement("div");
nearbyEntriesDiv.className = "nearby-entries-box";
nearbyEntriesDiv.innerText = "» ";
// Need to account for both HTML formats.
if (h2.parentElement.nextElementSibling.tagName === "SECTION")
h2.parentElement.nextElementSibling.prepend(nearbyEntriesDiv);
else
h2.parentElement.parentElement.insertBefore(nearbyEntriesDiv, h2.parentElement.nextElementSibling);
for (let entry of entriesBehind) {
let entryLink = document.createElement("a");
entryLink.href = `/wiki/${mw.util.wikiUrlencode(entry)}#${h2.id}`;
entryLink.title = entry;
entryLink.textContent = makeDisplayTitle(entry);
nearbyEntriesDiv.append(entryLink, " • ");
}
let selflink = document.createElement("a");
selflink.className = "mw-selflink selflink";
selflink.textContent = makeDisplayTitle(pageName);
nearbyEntriesDiv.append(selflink);
for (let entry of entriesAhead) {
let entryLink = document.createElement("a");
entryLink.href = `/wiki/${mw.util.wikiUrlencode(entry)}#${h2.id}`;
entryLink.title = entry;
entryLink.textContent = makeDisplayTitle(entry);
nearbyEntriesDiv.append(" • ", entryLink);
}
});
}
})();
// </nowiki>
qrnvvsk960y6rne2zimvyueuhlrbfh9
MediaWiki:Gadget-linkLanguageHeaders.js
8
17981
88964
2024-09-29T14:02:18Z
en>Surjection
0
88964
javascript
text/javascript
"use strict";
// {{documentation}}
// <nowiki>
(function linkLanguageHeadersGadget() {
var CACHE_DURATION = 24 * 60 * 60; // 24 hours
var ALLOWED_NAMESPACES = [
0, // main
118 // Reconstruction
];
function categoryNameFromLanguageName(name) {
return (name + " language").replace(/ Language language$/, " Language");
}
function getLanguageCodesByCanonicalName(callback, cacheDuration) {
var KEY = "enwiktLanguageCanonicalNamesJson";
var timeNow = new Date().getTime() * 1e-3;
try {
var cachedData = JSON.parse(localStorage.getItem(KEY));
if (timeNow - cachedData.timestamp < cacheDuration) {
callback(cachedData.data);
return;
}
} catch (e) { }
var request = new mw.Api().get({
"action": "parse",
"page": "Module:languages/canonical names.json",
"prop": "wikitext",
"formatversion": 2,
"format": "json"
});
request.done(function(response) {
var languageData = JSON.parse(response.parse.wikitext);
callback(languageData);
localStorage.setItem(KEY, JSON.stringify({
timestamp: timeNow,
data: languageData
}));
});
}
function linkLanguageHeaders(codeByCanonicalName) {
var categoryPrefix = encodeURIComponent(mw.config.get("wgFormattedNamespaces")[14].replace(/ /g, "_")) + ":";
$("#mw-content-text h2 .mw-headline, #mw-content-text .mw-heading2 h2").each(function() {
var headline = $(this);
var languageName = headline.text();
var languageCode = codeByCanonicalName[languageName];
if (languageCode) {
var categoryName = categoryNameFromLanguageName(languageName);
headline.html($("<a>", {
text: languageName,
title: languageName + " [" + languageCode + "]",
href: mw.config.get("wgArticlePath").replace("$1", categoryPrefix + encodeURIComponent(categoryName.replace(/ /g, "_")))
}));
}
});
}
mw.hook("wikipage.content").add(function($) {
if (mw.config.get("wgAction") === "view" &&
ALLOWED_NAMESPACES.indexOf(mw.config.get("wgNamespaceNumber")) >= 0) {
getLanguageCodesByCanonicalName(function (codeByCanonicalName) {
linkLanguageHeaders(codeByCanonicalName);
}, CACHE_DURATION);
}
});
})();
// </nowiki>
9mbznzssi1tt0p2j4o8fhijrpvduxiw
88965
88964
2026-08-06T23:21:19Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-linkLanguageHeaders.js]]
88964
javascript
text/javascript
"use strict";
// {{documentation}}
// <nowiki>
(function linkLanguageHeadersGadget() {
var CACHE_DURATION = 24 * 60 * 60; // 24 hours
var ALLOWED_NAMESPACES = [
0, // main
118 // Reconstruction
];
function categoryNameFromLanguageName(name) {
return (name + " language").replace(/ Language language$/, " Language");
}
function getLanguageCodesByCanonicalName(callback, cacheDuration) {
var KEY = "enwiktLanguageCanonicalNamesJson";
var timeNow = new Date().getTime() * 1e-3;
try {
var cachedData = JSON.parse(localStorage.getItem(KEY));
if (timeNow - cachedData.timestamp < cacheDuration) {
callback(cachedData.data);
return;
}
} catch (e) { }
var request = new mw.Api().get({
"action": "parse",
"page": "Module:languages/canonical names.json",
"prop": "wikitext",
"formatversion": 2,
"format": "json"
});
request.done(function(response) {
var languageData = JSON.parse(response.parse.wikitext);
callback(languageData);
localStorage.setItem(KEY, JSON.stringify({
timestamp: timeNow,
data: languageData
}));
});
}
function linkLanguageHeaders(codeByCanonicalName) {
var categoryPrefix = encodeURIComponent(mw.config.get("wgFormattedNamespaces")[14].replace(/ /g, "_")) + ":";
$("#mw-content-text h2 .mw-headline, #mw-content-text .mw-heading2 h2").each(function() {
var headline = $(this);
var languageName = headline.text();
var languageCode = codeByCanonicalName[languageName];
if (languageCode) {
var categoryName = categoryNameFromLanguageName(languageName);
headline.html($("<a>", {
text: languageName,
title: languageName + " [" + languageCode + "]",
href: mw.config.get("wgArticlePath").replace("$1", categoryPrefix + encodeURIComponent(categoryName.replace(/ /g, "_")))
}));
}
});
}
mw.hook("wikipage.content").add(function($) {
if (mw.config.get("wgAction") === "view" &&
ALLOWED_NAMESPACES.indexOf(mw.config.get("wgNamespaceNumber")) >= 0) {
getLanguageCodesByCanonicalName(function (codeByCanonicalName) {
linkLanguageHeaders(codeByCanonicalName);
}, CACHE_DURATION);
}
});
})();
// </nowiki>
9mbznzssi1tt0p2j4o8fhijrpvduxiw
MediaWiki:Gadget-AggregateInterprojectLinks.js
8
17982
88966
2024-09-11T13:19:41Z
en>Surjection
0
88966
javascript
text/javascript
/*
** Generates a sidebar list of links to other projects from {{pedia}}, etc.
** <nowiki>
*/
$(function () {
// Broken on Minerva...
if (mw.config.values.skin === "minerva") return;
var spans = $('span.interProject a:first-child').clone();
if (spans.length === 0) return;
// sort alphabetically
function sortbylabel(a, b) {
// get labels
a = $(a).text();
b = $(b).text();
// return sort order
if (a < b) return -1;
if (a > b) return 1;
return 0;
}
spans.sort(sortbylabel);
var isVector2022 = mw.config.get('skin') === 'vector-2022';
var newLinksWrapper = $('<div>', {
'class': 'pBody body vector-menu-content',
'style': 'display:block'
});
if (isVector2022)
newLinksWrapper.addClass('mw-portlet mw-portlet-projects');
$(spans).wrap('<li class="mw-list-item">').parent()
.appendTo($('<ul class="vector-menu-content-list">')).parent()
.appendTo(newLinksWrapper);
var collapsed = mw.cookie.get("vector-nav-p-projects") == "false";
var projectBox = $('<div>', {
addClass: 'vector-menu vector-menu-portal portlet portal ' + (collapsed ? "collapsed" : "expanded"),
id: 'p-projects'
})
.append($(isVector2022
? '<div id="p-projects-label" class="vector-menu-heading">In other projects</div>'
: '<label id="p-projects-label" aria-label="" class="vector-menu-heading"><span class="vector-menu-heading-label">In other projects</span></label>'))
.append(newLinksWrapper);
$('#p-tb').after(projectBox);
});
//</nowiki>
4mxst2h6pxj1o22mlp5bicxzmqi368j
88967
88966
2026-08-06T23:21:29Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-AggregateInterprojectLinks.js]]
88966
javascript
text/javascript
/*
** Generates a sidebar list of links to other projects from {{pedia}}, etc.
** <nowiki>
*/
$(function () {
// Broken on Minerva...
if (mw.config.values.skin === "minerva") return;
var spans = $('span.interProject a:first-child').clone();
if (spans.length === 0) return;
// sort alphabetically
function sortbylabel(a, b) {
// get labels
a = $(a).text();
b = $(b).text();
// return sort order
if (a < b) return -1;
if (a > b) return 1;
return 0;
}
spans.sort(sortbylabel);
var isVector2022 = mw.config.get('skin') === 'vector-2022';
var newLinksWrapper = $('<div>', {
'class': 'pBody body vector-menu-content',
'style': 'display:block'
});
if (isVector2022)
newLinksWrapper.addClass('mw-portlet mw-portlet-projects');
$(spans).wrap('<li class="mw-list-item">').parent()
.appendTo($('<ul class="vector-menu-content-list">')).parent()
.appendTo(newLinksWrapper);
var collapsed = mw.cookie.get("vector-nav-p-projects") == "false";
var projectBox = $('<div>', {
addClass: 'vector-menu vector-menu-portal portlet portal ' + (collapsed ? "collapsed" : "expanded"),
id: 'p-projects'
})
.append($(isVector2022
? '<div id="p-projects-label" class="vector-menu-heading">In other projects</div>'
: '<label id="p-projects-label" aria-label="" class="vector-menu-heading"><span class="vector-menu-heading-label">In other projects</span></label>'))
.append(newLinksWrapper);
$('#p-tb').after(projectBox);
});
//</nowiki>
4mxst2h6pxj1o22mlp5bicxzmqi368j
MediaWiki:Gadget-TargetedTranslations.js
8
17983
88968
2025-11-06T18:30:16Z
en>Surjection
0
88968
javascript
text/javascript
// Initialize preferred languages from local storage.
const storedLangs = localStorage.getItem("targetedTranslationsLangs");
let preferredLanguages = new Set(storedLangs ? JSON.parse(storedLangs) : []);
// SVG icons for bookmark states.
const ICONS = {
unselected: `
<svg xmlns="http://www.w3.org/2000/svg" width="25" height="17" viewBox="0 0 20 20"
fill="currentColor" style="vertical-align: text-top; cursor: pointer">
<g>
<path d="M5 1a2 2 0 00-2 2v16l7-5 7 5V3a2 2 0 00-2-2zm10 14.25-5-3.5-5 3.5V3h10z"/>
</g>
</svg>`,
selected: `
<svg xmlns="http://www.w3.org/2000/svg" width="25" height="17" viewBox="0 0 20 20"
fill="currentColor" style="vertical-align: text-top; cursor: pointer">
<g>
<path d="M5 1a2 2 0 00-2 2v16l7-5 7 5V3a2 2 0 00-2-2z"/>
</g>
</svg>`,
};
// Cache all translation tables up front.
const translationTables = Array.from(
document.querySelectorAll("table.translations")
);
/**
* Iterate over each non-empty translation line (<li> or <dd>) and compute its language key.
* Calls callback(lineElem, langKey) for each valid line.
*/
function forEachTranslationLine(table, callback) {
const lines = table.querySelectorAll(
".translations-cell li, .translations-cell dd"
);
for (const line of lines) {
const text = line.textContent.trim();
// Must have “Lang: …” format.
if (!text.includes(":")) continue;
const [base, ...rest] = text.split(":");
const baseLang = base.trim();
if (!baseLang || rest.join(":").trim().length === 0) continue;
let fullKey = baseLang;
// If this <li> is actually a sub‐entry, its grandparent is an <li> whose text is the macrolanguage.
const containerLi =
line.parentElement.parentElement.tagName === "LI"
? line.parentElement.parentElement
: null;
if (containerLi) {
const parentText = containerLi.textContent.split(":")[0].trim();
fullKey = `${parentText}>${baseLang}`;
}
callback(line, fullKey);
}
}
/**
* Render the “— Lang: translation; …” snippet under each NavHead,
* based on preferredLanguages.
*/
function renderTargetedTranslations() {
// Remove any existing markers.
document
.querySelectorAll(".targeted-translations")
.forEach((el) => el.remove());
for (const table of translationTables) {
// *** FIXED: NavHead is the previous sibling of table.parentElement, not table itself.
const navHead = table.parentElement.previousElementSibling;
if (!navHead || !navHead.matches("div.NavHead")) {
continue;
}
const frag = document.createDocumentFragment();
let foundAny = false;
forEachTranslationLine(table, (line, langKey) => {
if (!preferredLanguages.has(langKey)) return;
if (foundAny) {
frag.append(document.createTextNode("; "));
}
foundAny = true;
const span = document.createElement("span");
const [macro, sub] = langKey.split(">");
if (sub) {
span.append(`${macro} (${sub}): `);
} else {
span.append(`${macro}: `);
}
// Clone all child nodes except the first text node (which holds the language name).
for (let i = 1; i < line.childNodes.length; i++) {
if (line.childNodes[i].tagName !== "DL") {
span.append(line.childNodes[i].cloneNode(true));
}
}
frag.append(span);
});
if (foundAny) {
const wrapper = document.createElement("span");
wrapper.className = "targeted-translations";
wrapper.append(" — ", frag);
navHead.append(wrapper);
}
}
}
/**
* Toggle a single icon’s class and update preferredLanguages.
*/
function toggleIcon(iconEl, langKey) {
if (preferredLanguages.has(langKey)) {
preferredLanguages.delete(langKey);
iconEl.classList.replace("tt-icon-selected", "tt-icon-unselected");
iconEl.innerHTML = ICONS.unselected;
} else {
preferredLanguages.add(langKey);
iconEl.classList.replace("tt-icon-unselected", "tt-icon-selected");
iconEl.innerHTML = ICONS.selected;
}
}
/**
* Enter “select mode” on a given table:
* - Convert each translation line into a clickable icon.
* - Change the button to “Save preferred languages.”
* - Show the “Clear all” link.
*/
function enterSelectMode(table, selectBtn, clearBtn) {
// If any other table is already in “save” mode, force it to save first.
document.querySelectorAll(".tt-button-select").forEach((btn) => {
if (btn !== selectBtn && btn.textContent === "Save preferred languages") {
btn.click();
}
});
selectBtn.textContent = "Save preferred languages";
clearBtn.parentElement.style.display = "";
forEachTranslationLine(table, (line, langKey) => {
const icon = document.createElement("span");
icon.setAttribute("role", "button");
icon.classList.add(
preferredLanguages.has(langKey)
? "tt-icon-selected"
: "tt-icon-unselected"
);
icon.innerHTML = preferredLanguages.has(langKey)
? ICONS.selected
: ICONS.unselected;
line.insertAdjacentElement("afterbegin", icon);
icon.addEventListener("click", () => toggleIcon(icon, langKey));
});
}
/**
* Exit “select mode” on a given table:
* - Save preferredLanguages → localStorage.
* - Remove all icons.
* - Re-render the targeted translations.
*/
function exitSelectMode(table, selectBtn, clearBtn) {
localStorage.setItem(
"targetedTranslationsLangs",
JSON.stringify([...preferredLanguages])
);
table
.querySelectorAll(".tt-icon-selected, .tt-icon-unselected")
.forEach((el) => el.remove());
selectBtn.textContent = "Select preferred languages";
clearBtn.parentElement.style.display = "none";
renderTargetedTranslations();
}
// ----------------------------------------------------
// INITIAL SETUP: insert the control row & wire up events
// ----------------------------------------------------
translationTables.forEach((table) => {
// Build the <tr> for “Select preferred languages” / “Clear all”.
const headerRow = document.createElement("tr");
headerRow.innerHTML = `
<td colspan="100%" style="font-size: 85%" class="targeted-translations-select">
[<a role="button" class="tt-button-select">Select preferred languages</a>]
<span style="display: none">
 
[<a role="button" class="tt-button-clear">Clear all</a>]
</span>
</td>
`;
// Insert at the very top of <tbody>.
const tbody = table.firstElementChild;
tbody.insertBefore(headerRow, tbody.firstElementChild);
const selectBtn = table.querySelector(".tt-button-select");
const clearBtn = table.querySelector(".tt-button-clear");
selectBtn.addEventListener("click", () => {
if (selectBtn.textContent === "Select preferred languages") {
enterSelectMode(table, selectBtn, clearBtn);
} else {
exitSelectMode(table, selectBtn, clearBtn);
}
});
clearBtn.addEventListener("click", () => {
preferredLanguages.clear();
table.querySelectorAll(".tt-icon-selected").forEach((icon) => {
icon.classList.replace("tt-icon-selected", "tt-icon-unselected");
icon.innerHTML = ICONS.unselected;
});
});
});
// Initial display of preferred translations.
renderTargetedTranslations();
bjskcznhr4hueglve7hzzkkk78c0cxq
88969
88968
2026-08-06T23:23:17Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-TargetedTranslations.js]]
88968
javascript
text/javascript
// Initialize preferred languages from local storage.
const storedLangs = localStorage.getItem("targetedTranslationsLangs");
let preferredLanguages = new Set(storedLangs ? JSON.parse(storedLangs) : []);
// SVG icons for bookmark states.
const ICONS = {
unselected: `
<svg xmlns="http://www.w3.org/2000/svg" width="25" height="17" viewBox="0 0 20 20"
fill="currentColor" style="vertical-align: text-top; cursor: pointer">
<g>
<path d="M5 1a2 2 0 00-2 2v16l7-5 7 5V3a2 2 0 00-2-2zm10 14.25-5-3.5-5 3.5V3h10z"/>
</g>
</svg>`,
selected: `
<svg xmlns="http://www.w3.org/2000/svg" width="25" height="17" viewBox="0 0 20 20"
fill="currentColor" style="vertical-align: text-top; cursor: pointer">
<g>
<path d="M5 1a2 2 0 00-2 2v16l7-5 7 5V3a2 2 0 00-2-2z"/>
</g>
</svg>`,
};
// Cache all translation tables up front.
const translationTables = Array.from(
document.querySelectorAll("table.translations")
);
/**
* Iterate over each non-empty translation line (<li> or <dd>) and compute its language key.
* Calls callback(lineElem, langKey) for each valid line.
*/
function forEachTranslationLine(table, callback) {
const lines = table.querySelectorAll(
".translations-cell li, .translations-cell dd"
);
for (const line of lines) {
const text = line.textContent.trim();
// Must have “Lang: …” format.
if (!text.includes(":")) continue;
const [base, ...rest] = text.split(":");
const baseLang = base.trim();
if (!baseLang || rest.join(":").trim().length === 0) continue;
let fullKey = baseLang;
// If this <li> is actually a sub‐entry, its grandparent is an <li> whose text is the macrolanguage.
const containerLi =
line.parentElement.parentElement.tagName === "LI"
? line.parentElement.parentElement
: null;
if (containerLi) {
const parentText = containerLi.textContent.split(":")[0].trim();
fullKey = `${parentText}>${baseLang}`;
}
callback(line, fullKey);
}
}
/**
* Render the “— Lang: translation; …” snippet under each NavHead,
* based on preferredLanguages.
*/
function renderTargetedTranslations() {
// Remove any existing markers.
document
.querySelectorAll(".targeted-translations")
.forEach((el) => el.remove());
for (const table of translationTables) {
// *** FIXED: NavHead is the previous sibling of table.parentElement, not table itself.
const navHead = table.parentElement.previousElementSibling;
if (!navHead || !navHead.matches("div.NavHead")) {
continue;
}
const frag = document.createDocumentFragment();
let foundAny = false;
forEachTranslationLine(table, (line, langKey) => {
if (!preferredLanguages.has(langKey)) return;
if (foundAny) {
frag.append(document.createTextNode("; "));
}
foundAny = true;
const span = document.createElement("span");
const [macro, sub] = langKey.split(">");
if (sub) {
span.append(`${macro} (${sub}): `);
} else {
span.append(`${macro}: `);
}
// Clone all child nodes except the first text node (which holds the language name).
for (let i = 1; i < line.childNodes.length; i++) {
if (line.childNodes[i].tagName !== "DL") {
span.append(line.childNodes[i].cloneNode(true));
}
}
frag.append(span);
});
if (foundAny) {
const wrapper = document.createElement("span");
wrapper.className = "targeted-translations";
wrapper.append(" — ", frag);
navHead.append(wrapper);
}
}
}
/**
* Toggle a single icon’s class and update preferredLanguages.
*/
function toggleIcon(iconEl, langKey) {
if (preferredLanguages.has(langKey)) {
preferredLanguages.delete(langKey);
iconEl.classList.replace("tt-icon-selected", "tt-icon-unselected");
iconEl.innerHTML = ICONS.unselected;
} else {
preferredLanguages.add(langKey);
iconEl.classList.replace("tt-icon-unselected", "tt-icon-selected");
iconEl.innerHTML = ICONS.selected;
}
}
/**
* Enter “select mode” on a given table:
* - Convert each translation line into a clickable icon.
* - Change the button to “Save preferred languages.”
* - Show the “Clear all” link.
*/
function enterSelectMode(table, selectBtn, clearBtn) {
// If any other table is already in “save” mode, force it to save first.
document.querySelectorAll(".tt-button-select").forEach((btn) => {
if (btn !== selectBtn && btn.textContent === "Save preferred languages") {
btn.click();
}
});
selectBtn.textContent = "Save preferred languages";
clearBtn.parentElement.style.display = "";
forEachTranslationLine(table, (line, langKey) => {
const icon = document.createElement("span");
icon.setAttribute("role", "button");
icon.classList.add(
preferredLanguages.has(langKey)
? "tt-icon-selected"
: "tt-icon-unselected"
);
icon.innerHTML = preferredLanguages.has(langKey)
? ICONS.selected
: ICONS.unselected;
line.insertAdjacentElement("afterbegin", icon);
icon.addEventListener("click", () => toggleIcon(icon, langKey));
});
}
/**
* Exit “select mode” on a given table:
* - Save preferredLanguages → localStorage.
* - Remove all icons.
* - Re-render the targeted translations.
*/
function exitSelectMode(table, selectBtn, clearBtn) {
localStorage.setItem(
"targetedTranslationsLangs",
JSON.stringify([...preferredLanguages])
);
table
.querySelectorAll(".tt-icon-selected, .tt-icon-unselected")
.forEach((el) => el.remove());
selectBtn.textContent = "Select preferred languages";
clearBtn.parentElement.style.display = "none";
renderTargetedTranslations();
}
// ----------------------------------------------------
// INITIAL SETUP: insert the control row & wire up events
// ----------------------------------------------------
translationTables.forEach((table) => {
// Build the <tr> for “Select preferred languages” / “Clear all”.
const headerRow = document.createElement("tr");
headerRow.innerHTML = `
<td colspan="100%" style="font-size: 85%" class="targeted-translations-select">
[<a role="button" class="tt-button-select">Select preferred languages</a>]
<span style="display: none">
 
[<a role="button" class="tt-button-clear">Clear all</a>]
</span>
</td>
`;
// Insert at the very top of <tbody>.
const tbody = table.firstElementChild;
tbody.insertBefore(headerRow, tbody.firstElementChild);
const selectBtn = table.querySelector(".tt-button-select");
const clearBtn = table.querySelector(".tt-button-clear");
selectBtn.addEventListener("click", () => {
if (selectBtn.textContent === "Select preferred languages") {
enterSelectMode(table, selectBtn, clearBtn);
} else {
exitSelectMode(table, selectBtn, clearBtn);
}
});
clearBtn.addEventListener("click", () => {
preferredLanguages.clear();
table.querySelectorAll(".tt-icon-selected").forEach((icon) => {
icon.classList.replace("tt-icon-selected", "tt-icon-unselected");
icon.innerHTML = ICONS.unselected;
});
});
});
// Initial display of preferred translations.
renderTargetedTranslations();
bjskcznhr4hueglve7hzzkkk78c0cxq
MediaWiki:Gadget-TargetedTranslations.css
8
17984
88970
2021-05-28T08:17:31Z
en>Erutuon
0
fix images: replace unstable Vector skin paths with Commons paths
88970
css
text/css
.translationtargetstar {
background-image: url(https://upload.wikimedia.org/wikipedia/commons/thumb/3/33/Watch-icon.svg/16px-Watch-icon.svg.png);
position: absolute;
margin-top: 3px;
margin-left: -17px;
cursor: pointer;
width: 16px;
height: 15px;
}
.translationtargetstar:hover {
background-image: url(https://upload.wikimedia.org/wikipedia/commons/thumb/8/81/Watch-icon-hl.svg/16px-Watch-icon-hl.svg.png)
}
.translationtargetstarchecked {
background-image: url(https://upload.wikimedia.org/wikipedia/commons/thumb/8/8a/Unwatch-icon.svg/16px-Unwatch-icon.svg.png);
position: absolute;
margin-top: 3px;
margin-left: -17px;
cursor: pointer;
width: 16px;
height: 15px;
}
.translationtargetstarchecked:hover {
background-image: url(https://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Unwatch-icon-hl.svg/16px-Unwatch-icon-hl.svg.png);
}
ja0uic48113z7jqimkwe2ujkpgbxs24
88971
88970
2026-08-06T23:23:21Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-TargetedTranslations.css]]
88970
css
text/css
.translationtargetstar {
background-image: url(https://upload.wikimedia.org/wikipedia/commons/thumb/3/33/Watch-icon.svg/16px-Watch-icon.svg.png);
position: absolute;
margin-top: 3px;
margin-left: -17px;
cursor: pointer;
width: 16px;
height: 15px;
}
.translationtargetstar:hover {
background-image: url(https://upload.wikimedia.org/wikipedia/commons/thumb/8/81/Watch-icon-hl.svg/16px-Watch-icon-hl.svg.png)
}
.translationtargetstarchecked {
background-image: url(https://upload.wikimedia.org/wikipedia/commons/thumb/8/8a/Unwatch-icon.svg/16px-Unwatch-icon.svg.png);
position: absolute;
margin-top: 3px;
margin-left: -17px;
cursor: pointer;
width: 16px;
height: 15px;
}
.translationtargetstarchecked:hover {
background-image: url(https://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Unwatch-icon-hl.svg/16px-Unwatch-icon-hl.svg.png);
}
ja0uic48113z7jqimkwe2ujkpgbxs24
MediaWiki:Gadget-searchFocus.js
8
17985
88972
2024-09-02T16:43:37Z
en>Ioaxxere
0
this gadget should probably focus the main input bar instead
88972
javascript
text/javascript
if (mw.config.get("wgIsMainPage"))
document.querySelector(".mw-searchInput").focus();
tevyt1geg33xkr9fk0chzx33hp7t9t5
88973
88972
2026-08-06T23:23:31Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-searchFocus.js]]
88972
javascript
text/javascript
if (mw.config.get("wgIsMainPage"))
document.querySelector(".mw-searchInput").focus();
tevyt1geg33xkr9fk0chzx33hp7t9t5
MediaWiki:Gadget-TabbedLanguages.js
8
17986
88974
2025-09-17T01:06:23Z
en>Ioaxxere
0
Polyfill deprecated function newNode
88974
javascript
text/javascript
// ******** imported from [[MediaWiki:Gadget-LegacyScriptsNewNode.js]] ********
var newNode = window.newNode = function newNode(tagname) {
var node = document.createElement(tagname);
for (var i = 1; i < arguments.length; i++) {
var argument = arguments[i];
if (typeof argument == 'string') { //Text
node.appendChild(document.createTextNode(argument));
} else if (typeof argument == 'object') {
if (argument instanceof Node) { // If it is a DOM Node
node.appendChild(argument);
} else { // Attributes (hopefully)
for (var j in argument) {
if (j === 'class') { // Classname different because...
node.className = argument[j];
} else if (j == 'style') { // Style is special
node.style.cssText = argument[j];
} else if (typeof argument[j] == 'function') { // Basic event handlers
node.addEventListener(j, argument[j], false);
} else {
node.setAttribute(j, argument[j]); //Normal attributes
}
}
}
}
}
return node;
};
// ****************************************************************************
(function ( $ ) {
// {{documentation}}
// This is a (very) modified version of User:Atelaes/TabbedLanguages.js.
// Tabbed languages with tabs on the side.
// Tabs design by [[User:Jorm (WMF)]]
/*jshint maxerr:1048576, strict:true, undef:true, latedef:true, es5:true */
/*global mw, jQuery, importScript, importScriptURI, $, ObjectStorage */
if (($.cookie('disable-tabbed-languages') !== null) || (location.search.indexOf("tabbedlanguages=off") !== -1))
return;
if (!((mw.config.get('wgNamespaceNumber') === 0) || (mw.config.get('wgPageName') === "Wiktionary:Sandbox")))
return;
var bodyContent = $(".mw-content-ltr .mw-parser-output")[0], // NOT #bodyContent
languageLinks, ttr,
languageButtons,
caption,
bodyContentFragment,
complete = false,
MO = window.MutationObserver || window.WebKitMutationObserver,
observerStyleSheet;
// Setting up the tabs has not yet been started.
function makeTabsfromScratch() {
// Set up the variables...
window.tabbedLanguages = [];
window.languageContainers = [];
window.currentLanguageTab = 0;
window.languageHeaderEditButtons = [];
languageButtons = [];
window.tabstable = newNode('table', {id: 'tabstable'},
newNode('tbody',
ttr = newNode('tr',
newNode('td', {'style': 'padding-top:0px;vertical-align:top;'},
newNode('table', {'style': 'margin-top: -2px;'},
languageLinks = newNode('tbody', {id: 'languageLinks'}))))));
window.loadremovecatbuttons = false;
bodyContentFragment = document.createDocumentFragment();
// If bodyContent is complete, do it all at once.
// Otherwise, only start working, but don't attempt the whole thing.
// Use catlinks+*, as gEBCN isn't always available.
var catlinks = document.getElementById( "catlinks" );
if( bodyContent && catlinks && catlinks.nextSibling ) {
allAtOnce();
} else {
createObserver();
}
}
function allAtOnce() {
var languageContainer, toc = document.getElementById( "toc" );
currentLanguageTab = 0;
while ( bodyContent.firstChild ) {
if ( bodyContent.firstChild.nodeName === "SECTION" ) {
// Unwrap and remove top-level <section> tags in Parsoid content
// https://www.mediawiki.org/wiki/Specs/HTML/2.8.0#Headings_and_Sections
while ( bodyContent.firstChild.firstChild ) {
bodyContentFragment.appendChild( bodyContent.firstChild.firstChild );
}
bodyContent.removeChild( bodyContent.firstChild );
} else {
bodyContentFragment.appendChild( bodyContent.firstChild );
}
}
try {
for( var child = bodyContentFragment.firstChild; child && !isHeader( child ); ){
child = child.nextSibling;
}
for (
child = child && bodyContentFragment.insertBefore(tabstable, child).nextSibling;
child && child.className !== 'printfooter' && child.className !== 'catlinks';
child = child.nextSibling
) {
if ( isHeader( child ) ) {
var langspan = getHeaderContent( child ),
language = langspan && ( langspan.innerText || langspan.textContent );
if ( language ) {
newTab( tabbedLanguages.push(language) - 1, language );
processEditButton( child.getElementsByClassName('mw-editsection')[ 0 ] );
// should probably be set from a return value of above.
languageContainer = languageContainers[ languageContainers.length - 1 ];
bodyContentFragment.removeChild( child );
child = tabstable;
}
} else {
if ( child.nodeName !== "HR" ) {
languageContainer.insertBefore(child, languageContainer.lastChild);
} else {
bodyContentFragment.removeChild(child);
}
child = tabstable;
}
}
if( tabbedLanguages.length ) {
if( toc ) {
toc.parentNode.removeChild( toc );
}
sortCats();
bodyContent.appendChild( bodyContentFragment );
setUpHashChange()();
if( location.hash === '' ) {
location.replace( "#" + tabbedLanguages[ currentLanguageTab ] );
}
complete = true;
} else {
bodyContent.appendChild( bodyContentFragment );
}
} catch( e ) {
window.console && console.error( e );
bodyContent.appendChild( bodyContentFragment );
complete || setUpHashChange()();
}
}
// Set up a MutationObserver to detect when new elements are loaded.
function createObserver() {
// If .ready happens early (or MO and animstart aren't supported), go ahead.
// TODO the following code was commented out 2023-11-09 as $.isReady was suddenly
// always evaluating to true, meaning that TabbedLanguages would not load.
//if( $.isReady ) {
// return; // ??? How did we get $.isReady if bC or catlinks aren't loaded?
//} else {
$( document ).ready( function () {
if( tabbedLanguages.length === 0 ) {
removeObserver();
if( !bodyContent ) {
bodyContent = $(".mw-content-ltr")[0];
}
if( bodyContent ) {
allAtOnce();
}
}
});
//}
if( !document.getElementsByClassName ) {
return;
}
if( !MO ) {
var supportsAnimations = false;
// Check if we can use animations as a fallback. If not, abort.
// Basically yoinked from Modernizr.
$.each(
"animationName WebkitAnimationName MozAnimationName OAnimationName msAnimationName".split(" "),
function(a, b){
// Do documentElements even always have .style? If not, this'll need fixing.
if( document.documentElement.style[ b ] !== undefined ) {
supportsAnimations = true;
return false;
}
}
);
if( supportsAnimations === false ) {
return;
}
}
var foundHeader = false,
toc, recentChild,
timer = false, // It's the setTimeout value, or false otherwise.
recentHeader = -1,
checkTab, tabFound = false,
// If anyone on this project even considers making a random element
// on a page have the class "visualClear"...
visualClear = document.getElementsByClassName( "visualClear" ),
languageContainer,
observer;
// TODO: Deal with the little jumping elements below the tabstable.
// ...How?
function elemFound() {
if( timer === false ) {
timer = setTimeout( function() {
reactToObserver();
timer = false;
}, 1);
}
}
if( MO ) {
observer = new MO( elemFound );
observer.observe( document, { childList: true, subtree: true } );
} else {
// Fallback for browsers that don't support MO, but do support animations:
// IE10, FF5-13, Chrome 17<, Safari 4-5.1, Opera 12-12.1
// Set up a stylesheet that uses animations/keyframes to allow
// animationStart to see each time a new node loads onto the bodyContent.
observerStyleSheet =
document.getElementsByTagName( 'head' )[0].appendChild(
document.createElement( "style" )
);
var oSSText = "@/@-moz-/@-webkit-/@-ms-/@-o-/".split("/")
.join("keyframes nodeInserted{" +
"from{outline-color:#fff;}" +
"to{outline-color:#000;}" +
"}\n") +
".mw-content-ltr>*,.mw-content-ltr+*,.visualClear{" +
// TODO: Fix duplication here.
"/-moz-/-webkit-/-ms-/-o-/".split("/").join("animation-duration:0.01s;") +
"/-moz-/-webkit-/-ms-/-o-/".split("/").join("animation-name:nodeInserted;") +
"}";
// Pretty much copied from mw.util.
// Don't have time to wait until it would ordinarily load.
if( observerStyleSheet.styleSheet ) {
observerStyleSheet.styleSheet.cssText = oSSText;
} else {
observerStyleSheet.appendChild( document.createTextNode( oSSText ) );
}
observerStyleSheet = observerStyleSheet.sheet ||
observerStyleSheet.styleSheet ||
observerStyleSheet;
document.addEventListener('animationstart', elemFound, false);
document.addEventListener('MSAnimationStart', elemFound, false);
document.addEventListener('webkitAnimationStart', elemFound, false);
}
function reactToObserver() {
if( complete ) {
return;
}
// TODO: Surround main areas in try{} so that if it breaks, at least
// the content gets dumped back into visibility.
// First: What part are we up to?
if( !foundHeader ) {
// search for header or toc
if( !recentChild ) { // Just starting, apparently.
if( !bodyContent ) {
bodyContent = $(".mw-content-ltr")[0];
if( !bodyContent ) {
// bC hasn't loaded yet. Nothing to do here.
return;
}
}
// Check if we can do the whole thing in one go.
if( visualClear.length ) {
removeObserver();
allAtOnce();
return;
}
recentChild = bodyContent.firstChild;
if( !recentChild ) {
return; // Somehow got activated between bodyContent load
// and bodyContent's content's load.
}
}
for( ; !isHeader( recentChild ) && recentChild.nextSibling; ) {
recentChild = recentChild.nextSibling;
}
if( isHeader( recentChild ) ) { // should simplify check. Maybe !recentChild.nextSibling?
// We have our first header.
foundHeader = true;
checkTab = setUpHashChange();
// Default to the first tab.
currentLanguageTab = 0;
toc = document.getElementById('toc');
if ( toc ) {
if ( toc.nextSibling ) {
// And we have a usable ToC. Makes things much easier.
// Analyze the ToC. We'll be using it to determine what
// the tabs are going to be.
$( ".toclevel-1 > a > .toctext" ).each( function() {
var language = $( this ).text();
tabbedLanguages.push( language );
});
// Build all the tabs.
$( tabbedLanguages ).each( newTab );
toc.parentNode.removeChild( toc );
// Afterwards, we'll check if the right section
// is already available.
} else {
// Potential panic situation: Evil formatting places
// ToC *after* the first header, so it's actually
// *partly* loaded at this point, with no nextSibling.
// (Alternatively, someone put it alone in a box.) CSS
// builds it up as a huge tabbing block, but we can't
// remove it here. Oy.
// "Solution":
// Temporarily hide the ToC until ready() fires, then
// remove it. I'm not removing it right away, as I have
// no idea what doom might occur if something like this
// is removed while stuff is being loaded into it.
toc.style.display = "none";
( function ( toc ) {
$( function() {
if( toc.parentNode ) {
toc.parentNode.removeChild( toc );
}
});
})( toc );
toc = undefined;
window.console && console.error( "TL notice: " +
"Malformed entry. ToC either appears after " +
"headers, or is placed in a box. Mind fixing " +
"it or alerting me? Thanks. -- YR" );
}
}
languageContainer = languageContainers[ 0 ];
// Edit buttons.
processEditButton(
recentChild.getElementsByClassName( "mw-editsection" )[ 0 ]
);
// Either way, start displaying the tabs right.
bodyContent.insertBefore( tabstable, recentChild );
if( toc ) {
tabFound = checkTab();
}
}
}
if( foundHeader ) {
// Note that there may or may not be a ToC available...
// Should this be reworked so that the toc check is inside the loop?
if( recentChild.nextSibling ) {
// I hope I'm not going to regret leaving out a recentChild && condition here...
for( var nextChild; recentChild.nextSibling; ) {
nextChild = recentChild.nextSibling;
if( isHeader( recentChild ) ) {
var langspan = getHeaderContent( recentChild );
var editspan = recentChild.getElementsByClassName('mw-editsection')[ 0 ];
if( recentHeader >= 0 ) {
languageContainer.insertBefore( bodyContentFragment, languageContainer.lastChild );
}
recentHeader++;
var language = ( language = langspan ).innerText || language.textContent;
// Make sure it actually matches, when necessary.
if( toc && language !== tabbedLanguages[ recentHeader ]) {
// PANIC!!!
// Okay, maybe don't panic. TL has encountered
// a header that doesn't match the ToC's
// description of the page. Possibilities
// include an h1 on the page, a fake header
// around somewhere, or some other malformed
// kind of header. This really isn't supposed
// to happen ever.
// Response: Clear everything after this point,
// and from there act as though the ToC never
// existed.
if( recentHeader > 0 ) {
toggleLanguageTabs( tabbedLanguages[ 0 ] );
} else {
currentLanguageTab = 0;
}
while( tabbedLanguages.length > recentHeader ) {
tabbedLanguages.pop();
ttr.removeChild(
languageContainers.pop()
);
languageLinks.removeChild(
languageButtons.pop().parentNode
);
}
toc = undefined; // Never. Existed.
tabFound = false;
window.console && console.error( "TL notice: " +
"Malformed entry. ToC does not match " +
"headers. Possibly a misplaced H1, fake " +
"header, or header with incorrect " +
"contents. Mind fixing it or " +
"alerting me? Thanks. -- YR "
);
}
if( !toc ) {
tabbedLanguages.push( language );
newTab( recentHeader, language );
}
// Check to see if the target tab is found.
// TODO: Need a better system of knowing when to check:
// If it's found, stop checking.
// Whether or not we have a ToC, checking can be necessary.
// If we do have a ToC, but no hash, we don't check, right?
// Put another way:
// No hash? W/ ToC, don't check. W/o ToC, only check when new headers come in.
// Hash? Depends.
// W/ ToC: Technically, target could come in at any non-header element... ???
// W/o ToC: Could be whenever. Target header coming in is rather likely, in fact. Oy.
// Either way: If the target, whether header or random element, is found, stop looking.
// Does delaying the tab switch to a element target until full load matter? Not sure.
//
// Plan B: Regardless of ToC presence, only
// check for potential target elems
// at each new header, unless target is found.
if( recentHeader > 0 && tabFound === false ) {
tabFound = checkTab();
}
languageContainer = languageContainers[ recentHeader ];
if( recentHeader > 0 ) { // I have too many of these checks...
// Don't duplicate earlier pEB for first header.
processEditButton( editspan );
}
// All done. Clear.
bodyContent.removeChild( recentChild );
} else {
if( recentChild.nodeName === "HR" ) {
// Kill unnecessary bars.
bodyContent.removeChild( recentChild );
} else {
// Regular content. Prepare for dumping into the
// latest tab.
bodyContentFragment.appendChild( recentChild );
}
}
recentChild = nextChild;
}
}
/*
// If we're all done, finish up.
// How to tell?
// Option one: Repeatedly check for nextSibling.
// - Won't work. bodyContent doesn't always have a nS at the end.
// - Currently in use only for dealing with the last elem. (Uses O2 as fallback.)
// Option two: Set up getElemsByClassName, and poll for length.
// - presumably visualClear. This would probably be pretty heavy.
// - Currently in use.
// Option three: Use $.ready.
// - Potentially introduces substantial delay...
// Option four: In the listener, check for classnames each time.
// - Every time there's a new node? Lots of processing.
// Option five: ...
*/
if( bodyContent.nextSibling && bodyContent.lastChild === recentChild ) { // are we done? ...
// bodyContent.nextSibling doesn't always exist, but if it
// does, get a head start on the stuff that can be done already.
bodyContentFragment.appendChild( bodyContent.lastChild );
/*
// Problem: recentChild is now inside the languageContainer,
// and it's possible that reactToObserver will be called again
// before the post-visualClear stuff runs.
// Option one: recentChild = tabstable;
// - Nope. That would mean bodyContent.lastChild = recentChild again
// Option two: recentChild = false;
// - Nope. The check at the top would cause us to start over again.
// Blargh. This is an awful solution that probably violates every...
*/
recentChild = 1;
// "1" is hereby the indicator that we're not doing anything with
// this particular variable, okay?
// I'll try to make this nicer later.
}
// Dump collected elems into most recent languageContainer.
if( bodyContentFragment.firstChild ) { // Exact duplicate of above code. TODO: Fix.
languageContainer.insertBefore( bodyContentFragment, languageContainer.lastChild );
}
if( visualClear.length ) {
complete = true;
if( bodyContent.lastChild === recentChild ) {
// bC.nS didn't exist. Move the last one left now.
languageContainer.insertBefore( bodyContent.lastChild, languageContainer.lastChild );
}
removeObserver();
sortCats();
if( tabFound !== true ) {
checkTab();
if( location.hash === '' ) {
location.replace( "#" + tabbedLanguages[currentLanguageTab] );
}
}
}
}
}
function removeObserver() {
if( observer ) {
observer.disconnect();
} else if( observerStyleSheet ) {
document.removeEventListener('animationstart', elemFound, false);
document.removeEventListener('MSAnimationStart', elemFound, false);
document.removeEventListener('webkitAnimationStart', elemFound, false);
observerStyleSheet.disabled = true;
}
if( timer !== false ) {
clearTimeout( timer );
}
}
}
function isHeader( elem ) {
// Handle two versions of the markup: https://www.mediawiki.org/wiki/Heading_HTML_changes
return elem && elem.nodeType === Node.ELEMENT_NODE && (
elem.nodeName === "H2" && elem.getElementsByClassName( "mw-headline" ).length !== 0 ||
elem.classList.contains( "mw-heading2" ) && elem.getElementsByTagName( "H2" ).length !== 0
);
}
function getHeaderContent( elem ) {
// Handle two versions of the markup: https://www.mediawiki.org/wiki/Heading_HTML_changes
return elem.nodeName === "H2" ?
elem.getElementsByClassName( "mw-headline" )[0] :
elem.getElementsByTagName( "H2" )[0];
}
function newTab( index, language ) {
var active = index === currentLanguageTab;
var languageContainer = ttr.appendChild( newNode('td', {
'class': 'languageContainer',
'id': language + 'container'
}, active ? undefined : { 'style' : 'display:none;' } ));
languageContainers.push( languageContainer );
newCategoryBox( languageContainer, language );
// lB contains .(un)?selectedTab nodes
languageButtons.push(
languageLinks.appendChild( newNode('tr', newNode('td', {
'class': ( active ? '' : 'un' ) + 'selectedTab'
}, newNode('a', language, {
// Note: ' + language' makes this inconsistent with the ordinary links
// themselves, which have the language name encoded. Issue?
// Keep in mind that the parser itself actually corrects for this,
// changing #!Xóõ links to the proper #.C7.83X.C3.B3.C3.B5 links.
// Probably nothing to worry about.
'href': location.pathname + location.search + '#' + language.replace(/ /g, '_')
}), ' '))).firstChild
);
}
function newCategoryBox( container, name ) {
// Put a container in each for categories.
return container.appendChild( newNode('div', name + ' categories: ', newNode('ul'), {
'class': 'catlinks',
'id': 'catlinks'
}));
}
function processEditButton( button ) {
if( button ) {
// Yes, theoretically if you have some fake H2s the edit buttons will
// be misplaced. Yet another TODO...
var len = languageHeaderEditButtons.push( button );
if( len === 1 ) {
caption = document.createElement('caption');
tabstable.insertBefore( caption, tabstable.firstChild );
}
button.className += " editlangsection";
// use a argument or variable ( from .push? ) instead of .length?
if( len - 1 === currentLanguageTab ) {
caption.firstChild && caption.removeChild( caption.firstChild );
caption.appendChild( button );
}
}
}
// Sets up the hash toggle system.
// Returns the checkTab() function, which returns true if
// we found whatever it was.
function setUpHashChange() {
// Important Note: The decodeURI mess is a real mess.
// #.C7.83X.C3.B3.C3.B5 should go to tab "!Xóõ", with
// the id "!Xóõcontainer" (no encoding). Urgh.
// Also, "#Old_English" needs to go to decoded "Old English".
// Remember to .substr( 1 ) before passing here.
function decodeHash( hash ) {
return decodeURI(
hash
.replace(/\.(?=[0-9A-F]{2})/g, '%')
.replace(/_/g, ' ')
);
}
// Called by onhashchange.
function hashToggleLT() {
var destination = decodeHash( location.hash.substr(1) );
toggleLanguageTabs( destination );
tabbedLanguages[currentLanguageTab] !== destination && resetHash();
}
// For updating page positioning.
// Doesn't activate hashchange, at least in Chrome. Not sure about others.
function resetHash() {
location.replace( location.hash );
}
// Need to decide if language arg is encoded or decoded.
// Will work with either for the moment. Decoded works earlier.
// Currently passed as decoded by every function but itself.
// The hashes are encoded, but hashToggleLT decodes them.
// Toggles to a different language tab.
window.toggleLanguageTabs = function (language) {
// Find the destination language.
var destinationLanguageTab = $.inArray( language, tabbedLanguages );
if( destinationLanguageTab === -1 ) {
var decoded = decodeHash( language );
if( decoded !== language ) {
destinationLanguageTab = $.inArray( decoded, tabbedLanguages );
}
}
// Style the right toggle button, hide the old language section and show the new one.
// var languageButtons = $("#languageLinks .selectedTab, #languageLinks .unselectedTab");
if (destinationLanguageTab !== -1 ) {
if( destinationLanguageTab !== currentLanguageTab ) {
languageButtons[currentLanguageTab].className = 'unselectedTab';
languageContainers[currentLanguageTab].style.display = 'none';
currentLanguageTab = destinationLanguageTab;
languageButtons[destinationLanguageTab].className = 'selectedTab';
languageContainers[destinationLanguageTab].style.display = '';
if (caption) {
// extra checks shouldn't be necessary...
caption.firstChild && caption.removeChild(caption.firstChild);
languageHeaderEditButtons[currentLanguageTab] && caption.appendChild(languageHeaderEditButtons[currentLanguageTab]);
}
}
} else {
// Does the hash match the id of a node in a tab?
language = encodeURI( language.replace(/\ /g, '_') );
// Yes, I just possibly undid the decoding from hashToggleLT.
// Worse, this might double-encode, breaking things.
// Or maybe not? I think everything might be decoded before being
// sent here?
// I'll deal with it later.
// Find the node, and go up the node tree until
// you hit .languageContainer, or nothing.
language = document.getElementById( language );
for( ; language && ( language = language.parentNode ) &&
language.className !== 'languageContainer'; )
;
// language = language && language.parentNode.parentNode;
if ( language ) {
// If someone maliciously makes a languageContainer with a
// non-compliant ID, boom.
toggleLanguageTabs( language.id.split('container')[0] );
}
// Possible doom bug: Endless loop?
}
};
// This function gets returned as checkTab(), btw.
// For during or immediately after load: Check if we have a
// good "starting" tab. Return true if we have a definitive find.
function checkTab() {
// If there's a location hash, the window may have scrolled down before
// we got a chance to reorganize everything.
// If the destination was a subsection or sense id, switch to the right
// tab, and rescroll.
// If it was simply a language, switch to the appropriate tab, and
// scroll back up.
// If there's no hash at all, work off the TargetedTranslations prefs.
try {
var hash = '';
var destination = 'English';
if ( location.hash !== '' ) {
hash = ( location.hash ).substr( 1 ); // does hash.substr always exist?
destination = decodeHash( hash );
}
// 'k, this is still awful.
if ( $.inArray( destination, tabbedLanguages ) !== -1 ) {
toggleLanguageTabs( destination );
window.scrollY && window.scroll(0, 0);
return true;
} else if ( hash !== '' && document.getElementById( hash ) ) {
// This was going
// to receive an area to search from an argument, but it turns
// out that elem.getElementById doesn't actually exist. Meh.
toggleLanguageTabs( destination );
resetHash(); // Scroll to the element. (Necessary since we
// this isn't running through hashToggleLT, which normally does
// it in these situations.)
return true;
}
// No hash. Work from TargetedTranslations.
if ('localStorage' in window) {
if (tabbedLanguages[0] !== 'Translingual' && tabbedLanguages[0] !== 'English' && localStorage.TargetedTranslations) {
for (
var tt_ = localStorage.TargetedTranslations.split("|"), tt = tt_[0].split(";").concat(
$.grep(
tt_[1].replace(/[^;\/]+\//g, '').split(";"),
function (z) {
return z && z !== "Latin" && z !== "Hebrew" && z !== "Arabic";
}
)
).concat( tt_[1].replace(/\/[^;]+/g, '').split(";") ), i = 0;
i < tt.length;
i++
) {
if ($.inArray(tt[i], tabbedLanguages) !== -1) {
toggleLanguageTabs(tabbedLanguages[$.inArray(tt[i], tabbedLanguages)]);
break;
}
}
}
}
return false;
} catch ( e ) {
// This probably isn't all that unlikely to happen. Too complicated. :(
window.console && console.error( "TL error: checkTab broke.", e );
}
}
if ("onhashchange" in window && (document.documentMode === undefined || document.documentMode > 7)) {
window.onhashchange = hashToggleLT;
} else {
$( bodyContent ).on(
'click',
'a[href^="' + location.pathname + location.search + '#"], a[href^="#"]',
function () {
setTimeout( hashToggleLT, 10 );
}
);
}
return checkTab;
}
function sortCats() {
var catDiv = document.getElementById('mw-normal-catlinks'),
currentCatDiv;
if (catDiv) {
var cats = catDiv.getElementsByTagName('li'),
catname,
langcurrent = 0,
catskip = 1;
do {
while (cats.length > 0) {
catname = cats[ 0 ].getElementsByTagName('a')[0].innerHTML;
if (catname.indexOf(tabbedLanguages[langcurrent + catskip]) === 0 &&
!/letter\snames$|script\scharacters$|mythology$/.test(catname)
) {
langcurrent += catskip;
catskip = 1;
}
currentCatDiv = languageContainers[langcurrent].lastChild;
currentCatDiv.lastChild.appendChild(cats[0]);
}
if (langcurrent + 1 < languageContainers.length - catskip) {
// Didn't make it to the end, which means there's a section
// without any categories. Dump what's left back and try again.
while (currentCatDiv.lastChild.firstChild) {
catDiv.lastChild.appendChild(currentCatDiv.lastChild.firstChild);
}
catskip++;
} else {
break;
}
} while (true);
}
if( currentCatDiv ) {
// place patrol link at the bottom of the page
var pl = currentCatDiv.previousSibling; // languageContainer.lastChild.previousSibling;
// Got it, the patrollink is sometimes the lagging last elem.
if( pl && pl.className === "patrollink" ) {
tabstable.parentNode.appendChild(pl);
}
}
/*
// category editing buttons
if (mw.config.get('wgAction') === "view" && !/&printable=yes|&diff=|&oldid=/.test(location.search)) {
for (z = 0; z < languageContainers.length; z++) {
// TODO: import
// addTabbedLanguageNewCatButton(z);
}
if ( window.loadremovecatbuttons === true ) {
$.get(
mw.config.get( 'wgScript' ),
{ 'title' : mw.config.get('wgPageName'), 'action' : 'raw' },
// Note that this isn't actually defined. I'm considering not
// actually importing the function, and just dumping this section.
addRemoveCatButtons
);
}
}
*/
// Remove old cat box, allow display of hidden cats box.
if (catDiv && !(catDiv.nextSibling && catDiv.nextSibling.className === "mw-hidden-catlinks mw-hidden-cats-user-shown")) {
catDiv.parentNode.style.display = 'none';
} else {
if (catDiv) {
catDiv.style.display = 'none';
}
}
}
function testStuff() {
var delay = 5000, f = document.createDocumentFragment();
var x = [
function () {
// $("#toc").remove();
var x = document.getElementById( "mw-content-text" );
while( x.nextSibling ) {
f.appendChild( x.nextSibling );
}
}, function () {
var y = $( ".mw-content-ltr" )[ 0 ];
var q = document.createDocumentFragment();
while( y.firstChild ) {
q.appendChild( y.firstChild );
}
function u( i, e ) {
setTimeout( function () {
y.appendChild( q.firstChild );
}, i / e * delay );
}
for( var i = 0, e = q.childNodes.length; i < e; i++ ) {
u( i, e ); // encapsulate
}
makeTabsfromScratch();
}, function () {
function u( i, e ) {
setTimeout( function () {
$(".mw-content-ltr").after( f.firstChild );
}, i / e * delay / 5 );
}
for( var i = 0, e = f.childNodes.length; i < e; i++ ) {
u( i, e ); // encapsulate
}
console.log(9);
}
];
x[0]();
for( var i = 1; i < x.length; i++ ) {
setTimeout( x[ i ], (i-1) * delay + 50 );
}
}
//testStuff();
makeTabsfromScratch();
// This is officially deprecated as of MW1.22. Should be replaced by mw.hook,
// but the docs don't say what event, so...
$(mw).on('LivePreviewDone', function () {
bodyContent = $(".mw-content-ltr")[0]; // reset
makeTabsfromScratch();
});
})( window.jQuery );
bo3vk8t8xgwm7kymt0gkaic1zrtyvzq
88975
88974
2026-08-06T23:23:55Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-TabbedLanguages.js]]
88974
javascript
text/javascript
// ******** imported from [[MediaWiki:Gadget-LegacyScriptsNewNode.js]] ********
var newNode = window.newNode = function newNode(tagname) {
var node = document.createElement(tagname);
for (var i = 1; i < arguments.length; i++) {
var argument = arguments[i];
if (typeof argument == 'string') { //Text
node.appendChild(document.createTextNode(argument));
} else if (typeof argument == 'object') {
if (argument instanceof Node) { // If it is a DOM Node
node.appendChild(argument);
} else { // Attributes (hopefully)
for (var j in argument) {
if (j === 'class') { // Classname different because...
node.className = argument[j];
} else if (j == 'style') { // Style is special
node.style.cssText = argument[j];
} else if (typeof argument[j] == 'function') { // Basic event handlers
node.addEventListener(j, argument[j], false);
} else {
node.setAttribute(j, argument[j]); //Normal attributes
}
}
}
}
}
return node;
};
// ****************************************************************************
(function ( $ ) {
// {{documentation}}
// This is a (very) modified version of User:Atelaes/TabbedLanguages.js.
// Tabbed languages with tabs on the side.
// Tabs design by [[User:Jorm (WMF)]]
/*jshint maxerr:1048576, strict:true, undef:true, latedef:true, es5:true */
/*global mw, jQuery, importScript, importScriptURI, $, ObjectStorage */
if (($.cookie('disable-tabbed-languages') !== null) || (location.search.indexOf("tabbedlanguages=off") !== -1))
return;
if (!((mw.config.get('wgNamespaceNumber') === 0) || (mw.config.get('wgPageName') === "Wiktionary:Sandbox")))
return;
var bodyContent = $(".mw-content-ltr .mw-parser-output")[0], // NOT #bodyContent
languageLinks, ttr,
languageButtons,
caption,
bodyContentFragment,
complete = false,
MO = window.MutationObserver || window.WebKitMutationObserver,
observerStyleSheet;
// Setting up the tabs has not yet been started.
function makeTabsfromScratch() {
// Set up the variables...
window.tabbedLanguages = [];
window.languageContainers = [];
window.currentLanguageTab = 0;
window.languageHeaderEditButtons = [];
languageButtons = [];
window.tabstable = newNode('table', {id: 'tabstable'},
newNode('tbody',
ttr = newNode('tr',
newNode('td', {'style': 'padding-top:0px;vertical-align:top;'},
newNode('table', {'style': 'margin-top: -2px;'},
languageLinks = newNode('tbody', {id: 'languageLinks'}))))));
window.loadremovecatbuttons = false;
bodyContentFragment = document.createDocumentFragment();
// If bodyContent is complete, do it all at once.
// Otherwise, only start working, but don't attempt the whole thing.
// Use catlinks+*, as gEBCN isn't always available.
var catlinks = document.getElementById( "catlinks" );
if( bodyContent && catlinks && catlinks.nextSibling ) {
allAtOnce();
} else {
createObserver();
}
}
function allAtOnce() {
var languageContainer, toc = document.getElementById( "toc" );
currentLanguageTab = 0;
while ( bodyContent.firstChild ) {
if ( bodyContent.firstChild.nodeName === "SECTION" ) {
// Unwrap and remove top-level <section> tags in Parsoid content
// https://www.mediawiki.org/wiki/Specs/HTML/2.8.0#Headings_and_Sections
while ( bodyContent.firstChild.firstChild ) {
bodyContentFragment.appendChild( bodyContent.firstChild.firstChild );
}
bodyContent.removeChild( bodyContent.firstChild );
} else {
bodyContentFragment.appendChild( bodyContent.firstChild );
}
}
try {
for( var child = bodyContentFragment.firstChild; child && !isHeader( child ); ){
child = child.nextSibling;
}
for (
child = child && bodyContentFragment.insertBefore(tabstable, child).nextSibling;
child && child.className !== 'printfooter' && child.className !== 'catlinks';
child = child.nextSibling
) {
if ( isHeader( child ) ) {
var langspan = getHeaderContent( child ),
language = langspan && ( langspan.innerText || langspan.textContent );
if ( language ) {
newTab( tabbedLanguages.push(language) - 1, language );
processEditButton( child.getElementsByClassName('mw-editsection')[ 0 ] );
// should probably be set from a return value of above.
languageContainer = languageContainers[ languageContainers.length - 1 ];
bodyContentFragment.removeChild( child );
child = tabstable;
}
} else {
if ( child.nodeName !== "HR" ) {
languageContainer.insertBefore(child, languageContainer.lastChild);
} else {
bodyContentFragment.removeChild(child);
}
child = tabstable;
}
}
if( tabbedLanguages.length ) {
if( toc ) {
toc.parentNode.removeChild( toc );
}
sortCats();
bodyContent.appendChild( bodyContentFragment );
setUpHashChange()();
if( location.hash === '' ) {
location.replace( "#" + tabbedLanguages[ currentLanguageTab ] );
}
complete = true;
} else {
bodyContent.appendChild( bodyContentFragment );
}
} catch( e ) {
window.console && console.error( e );
bodyContent.appendChild( bodyContentFragment );
complete || setUpHashChange()();
}
}
// Set up a MutationObserver to detect when new elements are loaded.
function createObserver() {
// If .ready happens early (or MO and animstart aren't supported), go ahead.
// TODO the following code was commented out 2023-11-09 as $.isReady was suddenly
// always evaluating to true, meaning that TabbedLanguages would not load.
//if( $.isReady ) {
// return; // ??? How did we get $.isReady if bC or catlinks aren't loaded?
//} else {
$( document ).ready( function () {
if( tabbedLanguages.length === 0 ) {
removeObserver();
if( !bodyContent ) {
bodyContent = $(".mw-content-ltr")[0];
}
if( bodyContent ) {
allAtOnce();
}
}
});
//}
if( !document.getElementsByClassName ) {
return;
}
if( !MO ) {
var supportsAnimations = false;
// Check if we can use animations as a fallback. If not, abort.
// Basically yoinked from Modernizr.
$.each(
"animationName WebkitAnimationName MozAnimationName OAnimationName msAnimationName".split(" "),
function(a, b){
// Do documentElements even always have .style? If not, this'll need fixing.
if( document.documentElement.style[ b ] !== undefined ) {
supportsAnimations = true;
return false;
}
}
);
if( supportsAnimations === false ) {
return;
}
}
var foundHeader = false,
toc, recentChild,
timer = false, // It's the setTimeout value, or false otherwise.
recentHeader = -1,
checkTab, tabFound = false,
// If anyone on this project even considers making a random element
// on a page have the class "visualClear"...
visualClear = document.getElementsByClassName( "visualClear" ),
languageContainer,
observer;
// TODO: Deal with the little jumping elements below the tabstable.
// ...How?
function elemFound() {
if( timer === false ) {
timer = setTimeout( function() {
reactToObserver();
timer = false;
}, 1);
}
}
if( MO ) {
observer = new MO( elemFound );
observer.observe( document, { childList: true, subtree: true } );
} else {
// Fallback for browsers that don't support MO, but do support animations:
// IE10, FF5-13, Chrome 17<, Safari 4-5.1, Opera 12-12.1
// Set up a stylesheet that uses animations/keyframes to allow
// animationStart to see each time a new node loads onto the bodyContent.
observerStyleSheet =
document.getElementsByTagName( 'head' )[0].appendChild(
document.createElement( "style" )
);
var oSSText = "@/@-moz-/@-webkit-/@-ms-/@-o-/".split("/")
.join("keyframes nodeInserted{" +
"from{outline-color:#fff;}" +
"to{outline-color:#000;}" +
"}\n") +
".mw-content-ltr>*,.mw-content-ltr+*,.visualClear{" +
// TODO: Fix duplication here.
"/-moz-/-webkit-/-ms-/-o-/".split("/").join("animation-duration:0.01s;") +
"/-moz-/-webkit-/-ms-/-o-/".split("/").join("animation-name:nodeInserted;") +
"}";
// Pretty much copied from mw.util.
// Don't have time to wait until it would ordinarily load.
if( observerStyleSheet.styleSheet ) {
observerStyleSheet.styleSheet.cssText = oSSText;
} else {
observerStyleSheet.appendChild( document.createTextNode( oSSText ) );
}
observerStyleSheet = observerStyleSheet.sheet ||
observerStyleSheet.styleSheet ||
observerStyleSheet;
document.addEventListener('animationstart', elemFound, false);
document.addEventListener('MSAnimationStart', elemFound, false);
document.addEventListener('webkitAnimationStart', elemFound, false);
}
function reactToObserver() {
if( complete ) {
return;
}
// TODO: Surround main areas in try{} so that if it breaks, at least
// the content gets dumped back into visibility.
// First: What part are we up to?
if( !foundHeader ) {
// search for header or toc
if( !recentChild ) { // Just starting, apparently.
if( !bodyContent ) {
bodyContent = $(".mw-content-ltr")[0];
if( !bodyContent ) {
// bC hasn't loaded yet. Nothing to do here.
return;
}
}
// Check if we can do the whole thing in one go.
if( visualClear.length ) {
removeObserver();
allAtOnce();
return;
}
recentChild = bodyContent.firstChild;
if( !recentChild ) {
return; // Somehow got activated between bodyContent load
// and bodyContent's content's load.
}
}
for( ; !isHeader( recentChild ) && recentChild.nextSibling; ) {
recentChild = recentChild.nextSibling;
}
if( isHeader( recentChild ) ) { // should simplify check. Maybe !recentChild.nextSibling?
// We have our first header.
foundHeader = true;
checkTab = setUpHashChange();
// Default to the first tab.
currentLanguageTab = 0;
toc = document.getElementById('toc');
if ( toc ) {
if ( toc.nextSibling ) {
// And we have a usable ToC. Makes things much easier.
// Analyze the ToC. We'll be using it to determine what
// the tabs are going to be.
$( ".toclevel-1 > a > .toctext" ).each( function() {
var language = $( this ).text();
tabbedLanguages.push( language );
});
// Build all the tabs.
$( tabbedLanguages ).each( newTab );
toc.parentNode.removeChild( toc );
// Afterwards, we'll check if the right section
// is already available.
} else {
// Potential panic situation: Evil formatting places
// ToC *after* the first header, so it's actually
// *partly* loaded at this point, with no nextSibling.
// (Alternatively, someone put it alone in a box.) CSS
// builds it up as a huge tabbing block, but we can't
// remove it here. Oy.
// "Solution":
// Temporarily hide the ToC until ready() fires, then
// remove it. I'm not removing it right away, as I have
// no idea what doom might occur if something like this
// is removed while stuff is being loaded into it.
toc.style.display = "none";
( function ( toc ) {
$( function() {
if( toc.parentNode ) {
toc.parentNode.removeChild( toc );
}
});
})( toc );
toc = undefined;
window.console && console.error( "TL notice: " +
"Malformed entry. ToC either appears after " +
"headers, or is placed in a box. Mind fixing " +
"it or alerting me? Thanks. -- YR" );
}
}
languageContainer = languageContainers[ 0 ];
// Edit buttons.
processEditButton(
recentChild.getElementsByClassName( "mw-editsection" )[ 0 ]
);
// Either way, start displaying the tabs right.
bodyContent.insertBefore( tabstable, recentChild );
if( toc ) {
tabFound = checkTab();
}
}
}
if( foundHeader ) {
// Note that there may or may not be a ToC available...
// Should this be reworked so that the toc check is inside the loop?
if( recentChild.nextSibling ) {
// I hope I'm not going to regret leaving out a recentChild && condition here...
for( var nextChild; recentChild.nextSibling; ) {
nextChild = recentChild.nextSibling;
if( isHeader( recentChild ) ) {
var langspan = getHeaderContent( recentChild );
var editspan = recentChild.getElementsByClassName('mw-editsection')[ 0 ];
if( recentHeader >= 0 ) {
languageContainer.insertBefore( bodyContentFragment, languageContainer.lastChild );
}
recentHeader++;
var language = ( language = langspan ).innerText || language.textContent;
// Make sure it actually matches, when necessary.
if( toc && language !== tabbedLanguages[ recentHeader ]) {
// PANIC!!!
// Okay, maybe don't panic. TL has encountered
// a header that doesn't match the ToC's
// description of the page. Possibilities
// include an h1 on the page, a fake header
// around somewhere, or some other malformed
// kind of header. This really isn't supposed
// to happen ever.
// Response: Clear everything after this point,
// and from there act as though the ToC never
// existed.
if( recentHeader > 0 ) {
toggleLanguageTabs( tabbedLanguages[ 0 ] );
} else {
currentLanguageTab = 0;
}
while( tabbedLanguages.length > recentHeader ) {
tabbedLanguages.pop();
ttr.removeChild(
languageContainers.pop()
);
languageLinks.removeChild(
languageButtons.pop().parentNode
);
}
toc = undefined; // Never. Existed.
tabFound = false;
window.console && console.error( "TL notice: " +
"Malformed entry. ToC does not match " +
"headers. Possibly a misplaced H1, fake " +
"header, or header with incorrect " +
"contents. Mind fixing it or " +
"alerting me? Thanks. -- YR "
);
}
if( !toc ) {
tabbedLanguages.push( language );
newTab( recentHeader, language );
}
// Check to see if the target tab is found.
// TODO: Need a better system of knowing when to check:
// If it's found, stop checking.
// Whether or not we have a ToC, checking can be necessary.
// If we do have a ToC, but no hash, we don't check, right?
// Put another way:
// No hash? W/ ToC, don't check. W/o ToC, only check when new headers come in.
// Hash? Depends.
// W/ ToC: Technically, target could come in at any non-header element... ???
// W/o ToC: Could be whenever. Target header coming in is rather likely, in fact. Oy.
// Either way: If the target, whether header or random element, is found, stop looking.
// Does delaying the tab switch to a element target until full load matter? Not sure.
//
// Plan B: Regardless of ToC presence, only
// check for potential target elems
// at each new header, unless target is found.
if( recentHeader > 0 && tabFound === false ) {
tabFound = checkTab();
}
languageContainer = languageContainers[ recentHeader ];
if( recentHeader > 0 ) { // I have too many of these checks...
// Don't duplicate earlier pEB for first header.
processEditButton( editspan );
}
// All done. Clear.
bodyContent.removeChild( recentChild );
} else {
if( recentChild.nodeName === "HR" ) {
// Kill unnecessary bars.
bodyContent.removeChild( recentChild );
} else {
// Regular content. Prepare for dumping into the
// latest tab.
bodyContentFragment.appendChild( recentChild );
}
}
recentChild = nextChild;
}
}
/*
// If we're all done, finish up.
// How to tell?
// Option one: Repeatedly check for nextSibling.
// - Won't work. bodyContent doesn't always have a nS at the end.
// - Currently in use only for dealing with the last elem. (Uses O2 as fallback.)
// Option two: Set up getElemsByClassName, and poll for length.
// - presumably visualClear. This would probably be pretty heavy.
// - Currently in use.
// Option three: Use $.ready.
// - Potentially introduces substantial delay...
// Option four: In the listener, check for classnames each time.
// - Every time there's a new node? Lots of processing.
// Option five: ...
*/
if( bodyContent.nextSibling && bodyContent.lastChild === recentChild ) { // are we done? ...
// bodyContent.nextSibling doesn't always exist, but if it
// does, get a head start on the stuff that can be done already.
bodyContentFragment.appendChild( bodyContent.lastChild );
/*
// Problem: recentChild is now inside the languageContainer,
// and it's possible that reactToObserver will be called again
// before the post-visualClear stuff runs.
// Option one: recentChild = tabstable;
// - Nope. That would mean bodyContent.lastChild = recentChild again
// Option two: recentChild = false;
// - Nope. The check at the top would cause us to start over again.
// Blargh. This is an awful solution that probably violates every...
*/
recentChild = 1;
// "1" is hereby the indicator that we're not doing anything with
// this particular variable, okay?
// I'll try to make this nicer later.
}
// Dump collected elems into most recent languageContainer.
if( bodyContentFragment.firstChild ) { // Exact duplicate of above code. TODO: Fix.
languageContainer.insertBefore( bodyContentFragment, languageContainer.lastChild );
}
if( visualClear.length ) {
complete = true;
if( bodyContent.lastChild === recentChild ) {
// bC.nS didn't exist. Move the last one left now.
languageContainer.insertBefore( bodyContent.lastChild, languageContainer.lastChild );
}
removeObserver();
sortCats();
if( tabFound !== true ) {
checkTab();
if( location.hash === '' ) {
location.replace( "#" + tabbedLanguages[currentLanguageTab] );
}
}
}
}
}
function removeObserver() {
if( observer ) {
observer.disconnect();
} else if( observerStyleSheet ) {
document.removeEventListener('animationstart', elemFound, false);
document.removeEventListener('MSAnimationStart', elemFound, false);
document.removeEventListener('webkitAnimationStart', elemFound, false);
observerStyleSheet.disabled = true;
}
if( timer !== false ) {
clearTimeout( timer );
}
}
}
function isHeader( elem ) {
// Handle two versions of the markup: https://www.mediawiki.org/wiki/Heading_HTML_changes
return elem && elem.nodeType === Node.ELEMENT_NODE && (
elem.nodeName === "H2" && elem.getElementsByClassName( "mw-headline" ).length !== 0 ||
elem.classList.contains( "mw-heading2" ) && elem.getElementsByTagName( "H2" ).length !== 0
);
}
function getHeaderContent( elem ) {
// Handle two versions of the markup: https://www.mediawiki.org/wiki/Heading_HTML_changes
return elem.nodeName === "H2" ?
elem.getElementsByClassName( "mw-headline" )[0] :
elem.getElementsByTagName( "H2" )[0];
}
function newTab( index, language ) {
var active = index === currentLanguageTab;
var languageContainer = ttr.appendChild( newNode('td', {
'class': 'languageContainer',
'id': language + 'container'
}, active ? undefined : { 'style' : 'display:none;' } ));
languageContainers.push( languageContainer );
newCategoryBox( languageContainer, language );
// lB contains .(un)?selectedTab nodes
languageButtons.push(
languageLinks.appendChild( newNode('tr', newNode('td', {
'class': ( active ? '' : 'un' ) + 'selectedTab'
}, newNode('a', language, {
// Note: ' + language' makes this inconsistent with the ordinary links
// themselves, which have the language name encoded. Issue?
// Keep in mind that the parser itself actually corrects for this,
// changing #!Xóõ links to the proper #.C7.83X.C3.B3.C3.B5 links.
// Probably nothing to worry about.
'href': location.pathname + location.search + '#' + language.replace(/ /g, '_')
}), ' '))).firstChild
);
}
function newCategoryBox( container, name ) {
// Put a container in each for categories.
return container.appendChild( newNode('div', name + ' categories: ', newNode('ul'), {
'class': 'catlinks',
'id': 'catlinks'
}));
}
function processEditButton( button ) {
if( button ) {
// Yes, theoretically if you have some fake H2s the edit buttons will
// be misplaced. Yet another TODO...
var len = languageHeaderEditButtons.push( button );
if( len === 1 ) {
caption = document.createElement('caption');
tabstable.insertBefore( caption, tabstable.firstChild );
}
button.className += " editlangsection";
// use a argument or variable ( from .push? ) instead of .length?
if( len - 1 === currentLanguageTab ) {
caption.firstChild && caption.removeChild( caption.firstChild );
caption.appendChild( button );
}
}
}
// Sets up the hash toggle system.
// Returns the checkTab() function, which returns true if
// we found whatever it was.
function setUpHashChange() {
// Important Note: The decodeURI mess is a real mess.
// #.C7.83X.C3.B3.C3.B5 should go to tab "!Xóõ", with
// the id "!Xóõcontainer" (no encoding). Urgh.
// Also, "#Old_English" needs to go to decoded "Old English".
// Remember to .substr( 1 ) before passing here.
function decodeHash( hash ) {
return decodeURI(
hash
.replace(/\.(?=[0-9A-F]{2})/g, '%')
.replace(/_/g, ' ')
);
}
// Called by onhashchange.
function hashToggleLT() {
var destination = decodeHash( location.hash.substr(1) );
toggleLanguageTabs( destination );
tabbedLanguages[currentLanguageTab] !== destination && resetHash();
}
// For updating page positioning.
// Doesn't activate hashchange, at least in Chrome. Not sure about others.
function resetHash() {
location.replace( location.hash );
}
// Need to decide if language arg is encoded or decoded.
// Will work with either for the moment. Decoded works earlier.
// Currently passed as decoded by every function but itself.
// The hashes are encoded, but hashToggleLT decodes them.
// Toggles to a different language tab.
window.toggleLanguageTabs = function (language) {
// Find the destination language.
var destinationLanguageTab = $.inArray( language, tabbedLanguages );
if( destinationLanguageTab === -1 ) {
var decoded = decodeHash( language );
if( decoded !== language ) {
destinationLanguageTab = $.inArray( decoded, tabbedLanguages );
}
}
// Style the right toggle button, hide the old language section and show the new one.
// var languageButtons = $("#languageLinks .selectedTab, #languageLinks .unselectedTab");
if (destinationLanguageTab !== -1 ) {
if( destinationLanguageTab !== currentLanguageTab ) {
languageButtons[currentLanguageTab].className = 'unselectedTab';
languageContainers[currentLanguageTab].style.display = 'none';
currentLanguageTab = destinationLanguageTab;
languageButtons[destinationLanguageTab].className = 'selectedTab';
languageContainers[destinationLanguageTab].style.display = '';
if (caption) {
// extra checks shouldn't be necessary...
caption.firstChild && caption.removeChild(caption.firstChild);
languageHeaderEditButtons[currentLanguageTab] && caption.appendChild(languageHeaderEditButtons[currentLanguageTab]);
}
}
} else {
// Does the hash match the id of a node in a tab?
language = encodeURI( language.replace(/\ /g, '_') );
// Yes, I just possibly undid the decoding from hashToggleLT.
// Worse, this might double-encode, breaking things.
// Or maybe not? I think everything might be decoded before being
// sent here?
// I'll deal with it later.
// Find the node, and go up the node tree until
// you hit .languageContainer, or nothing.
language = document.getElementById( language );
for( ; language && ( language = language.parentNode ) &&
language.className !== 'languageContainer'; )
;
// language = language && language.parentNode.parentNode;
if ( language ) {
// If someone maliciously makes a languageContainer with a
// non-compliant ID, boom.
toggleLanguageTabs( language.id.split('container')[0] );
}
// Possible doom bug: Endless loop?
}
};
// This function gets returned as checkTab(), btw.
// For during or immediately after load: Check if we have a
// good "starting" tab. Return true if we have a definitive find.
function checkTab() {
// If there's a location hash, the window may have scrolled down before
// we got a chance to reorganize everything.
// If the destination was a subsection or sense id, switch to the right
// tab, and rescroll.
// If it was simply a language, switch to the appropriate tab, and
// scroll back up.
// If there's no hash at all, work off the TargetedTranslations prefs.
try {
var hash = '';
var destination = 'English';
if ( location.hash !== '' ) {
hash = ( location.hash ).substr( 1 ); // does hash.substr always exist?
destination = decodeHash( hash );
}
// 'k, this is still awful.
if ( $.inArray( destination, tabbedLanguages ) !== -1 ) {
toggleLanguageTabs( destination );
window.scrollY && window.scroll(0, 0);
return true;
} else if ( hash !== '' && document.getElementById( hash ) ) {
// This was going
// to receive an area to search from an argument, but it turns
// out that elem.getElementById doesn't actually exist. Meh.
toggleLanguageTabs( destination );
resetHash(); // Scroll to the element. (Necessary since we
// this isn't running through hashToggleLT, which normally does
// it in these situations.)
return true;
}
// No hash. Work from TargetedTranslations.
if ('localStorage' in window) {
if (tabbedLanguages[0] !== 'Translingual' && tabbedLanguages[0] !== 'English' && localStorage.TargetedTranslations) {
for (
var tt_ = localStorage.TargetedTranslations.split("|"), tt = tt_[0].split(";").concat(
$.grep(
tt_[1].replace(/[^;\/]+\//g, '').split(";"),
function (z) {
return z && z !== "Latin" && z !== "Hebrew" && z !== "Arabic";
}
)
).concat( tt_[1].replace(/\/[^;]+/g, '').split(";") ), i = 0;
i < tt.length;
i++
) {
if ($.inArray(tt[i], tabbedLanguages) !== -1) {
toggleLanguageTabs(tabbedLanguages[$.inArray(tt[i], tabbedLanguages)]);
break;
}
}
}
}
return false;
} catch ( e ) {
// This probably isn't all that unlikely to happen. Too complicated. :(
window.console && console.error( "TL error: checkTab broke.", e );
}
}
if ("onhashchange" in window && (document.documentMode === undefined || document.documentMode > 7)) {
window.onhashchange = hashToggleLT;
} else {
$( bodyContent ).on(
'click',
'a[href^="' + location.pathname + location.search + '#"], a[href^="#"]',
function () {
setTimeout( hashToggleLT, 10 );
}
);
}
return checkTab;
}
function sortCats() {
var catDiv = document.getElementById('mw-normal-catlinks'),
currentCatDiv;
if (catDiv) {
var cats = catDiv.getElementsByTagName('li'),
catname,
langcurrent = 0,
catskip = 1;
do {
while (cats.length > 0) {
catname = cats[ 0 ].getElementsByTagName('a')[0].innerHTML;
if (catname.indexOf(tabbedLanguages[langcurrent + catskip]) === 0 &&
!/letter\snames$|script\scharacters$|mythology$/.test(catname)
) {
langcurrent += catskip;
catskip = 1;
}
currentCatDiv = languageContainers[langcurrent].lastChild;
currentCatDiv.lastChild.appendChild(cats[0]);
}
if (langcurrent + 1 < languageContainers.length - catskip) {
// Didn't make it to the end, which means there's a section
// without any categories. Dump what's left back and try again.
while (currentCatDiv.lastChild.firstChild) {
catDiv.lastChild.appendChild(currentCatDiv.lastChild.firstChild);
}
catskip++;
} else {
break;
}
} while (true);
}
if( currentCatDiv ) {
// place patrol link at the bottom of the page
var pl = currentCatDiv.previousSibling; // languageContainer.lastChild.previousSibling;
// Got it, the patrollink is sometimes the lagging last elem.
if( pl && pl.className === "patrollink" ) {
tabstable.parentNode.appendChild(pl);
}
}
/*
// category editing buttons
if (mw.config.get('wgAction') === "view" && !/&printable=yes|&diff=|&oldid=/.test(location.search)) {
for (z = 0; z < languageContainers.length; z++) {
// TODO: import
// addTabbedLanguageNewCatButton(z);
}
if ( window.loadremovecatbuttons === true ) {
$.get(
mw.config.get( 'wgScript' ),
{ 'title' : mw.config.get('wgPageName'), 'action' : 'raw' },
// Note that this isn't actually defined. I'm considering not
// actually importing the function, and just dumping this section.
addRemoveCatButtons
);
}
}
*/
// Remove old cat box, allow display of hidden cats box.
if (catDiv && !(catDiv.nextSibling && catDiv.nextSibling.className === "mw-hidden-catlinks mw-hidden-cats-user-shown")) {
catDiv.parentNode.style.display = 'none';
} else {
if (catDiv) {
catDiv.style.display = 'none';
}
}
}
function testStuff() {
var delay = 5000, f = document.createDocumentFragment();
var x = [
function () {
// $("#toc").remove();
var x = document.getElementById( "mw-content-text" );
while( x.nextSibling ) {
f.appendChild( x.nextSibling );
}
}, function () {
var y = $( ".mw-content-ltr" )[ 0 ];
var q = document.createDocumentFragment();
while( y.firstChild ) {
q.appendChild( y.firstChild );
}
function u( i, e ) {
setTimeout( function () {
y.appendChild( q.firstChild );
}, i / e * delay );
}
for( var i = 0, e = q.childNodes.length; i < e; i++ ) {
u( i, e ); // encapsulate
}
makeTabsfromScratch();
}, function () {
function u( i, e ) {
setTimeout( function () {
$(".mw-content-ltr").after( f.firstChild );
}, i / e * delay / 5 );
}
for( var i = 0, e = f.childNodes.length; i < e; i++ ) {
u( i, e ); // encapsulate
}
console.log(9);
}
];
x[0]();
for( var i = 1; i < x.length; i++ ) {
setTimeout( x[ i ], (i-1) * delay + 50 );
}
}
//testStuff();
makeTabsfromScratch();
// This is officially deprecated as of MW1.22. Should be replaced by mw.hook,
// but the docs don't say what event, so...
$(mw).on('LivePreviewDone', function () {
bodyContent = $(".mw-content-ltr")[0]; // reset
makeTabsfromScratch();
});
})( window.jQuery );
bo3vk8t8xgwm7kymt0gkaic1zrtyvzq
MediaWiki:Gadget-TabbedLanguages.css
8
17987
88976
2025-12-24T19:33:28Z
en>Benwing
0
[[User:Mahagaja]] exclude mammoth-minitoc tables from being turned off by TabbedLanguages
88976
css
text/css
/* Styles for the Tabbed Languages gadget */
#languageLinks .selectedTab a {
font-size: 19px;
color: #000;
padding: 6px;
display: block;
border-left: 1px solid #CCC;
border-bottom: 1px solid #CCC;
margin-bottom: -3px;
background-color: #fff;
margin-right: -6px;
position: relative;
border-top: 1px solid #CCC;
white-space: nowrap;
}
#languageLinks .unselectedTab a {
font-size: 16px;
color: #929292;
padding: 6px;
display: block;
border-left: 1px solid #CCC;
border-top: 1px solid #CCC;
border-bottom: 1px solid #CCC;
margin-bottom: -3px;
margin-left: 15px;
margin-right: -5px;
white-space: nowrap;
background-color: #F6F6F6;
}
#languageLinks tr td {
padding: 0px;
}
tr td.languageContainer {
padding-left: 8px;
padding-bottom: 10px;
padding-right: 8px;
border: 1px solid #CCC;
vertical-align: top;
width: 100%;
}
.unselectedtab {
margin-left: 5px;
}
.editlangsection {
margin-top: -13px;
margin-right: 5px;
border: 1px solid #CCC;
margin-bottom: -2px;
border-bottom-width: 0px;
-webkit-border-top-left-radius: 5px;
-webkit-border-top-right-radius: 5px;
-moz-border-radius-topleft: 5px;
-moz-border-radius-topright: 5px;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
color: white;
line-height: 14px;
font-size: 12px;
padding: 0px 7px;
text-align: center;
clear: both;
background-color: white;
float: right;
}
#languageLinks a.addLanguageButton {
font-size: 12px;
padding: 3px 6px;
}
@media screen and (max-width:36em) {
#tabstable > tbody > tr > td {
display: block;
float: left;
width: 100%;
}
#languageLinks tr {
display: block;
float: left;
margin-bottom: -3px;
margin-left: -5px;
margin-top: 5px;
}
#languageLinks .unselectedTab a {
margin-left: 0;
margin-right: 5px;
border-right: 1px solid #CCC;
}
#languageLinks .selectedTab a {
margin-left: 0;
margin-right: 5px;
border-right: 1px solid #CCC;
border-bottom: 1px solid #FFF;
}
#tabstable {
width: 100%;
}
#languageLinks a.addLanguageButton {
margin-top: 2px;
margin-bottom: 1px;
}
.editlangsection {
border-bottom-width: 1px;
}
}
.minitoc:not(.mammoth-minitoc) {
display: none;
}
0vfsobgurlx0r6mcpypw92v32flnn6t
88977
88976
2026-08-06T23:23:58Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-TabbedLanguages.css]]
88976
css
text/css
/* Styles for the Tabbed Languages gadget */
#languageLinks .selectedTab a {
font-size: 19px;
color: #000;
padding: 6px;
display: block;
border-left: 1px solid #CCC;
border-bottom: 1px solid #CCC;
margin-bottom: -3px;
background-color: #fff;
margin-right: -6px;
position: relative;
border-top: 1px solid #CCC;
white-space: nowrap;
}
#languageLinks .unselectedTab a {
font-size: 16px;
color: #929292;
padding: 6px;
display: block;
border-left: 1px solid #CCC;
border-top: 1px solid #CCC;
border-bottom: 1px solid #CCC;
margin-bottom: -3px;
margin-left: 15px;
margin-right: -5px;
white-space: nowrap;
background-color: #F6F6F6;
}
#languageLinks tr td {
padding: 0px;
}
tr td.languageContainer {
padding-left: 8px;
padding-bottom: 10px;
padding-right: 8px;
border: 1px solid #CCC;
vertical-align: top;
width: 100%;
}
.unselectedtab {
margin-left: 5px;
}
.editlangsection {
margin-top: -13px;
margin-right: 5px;
border: 1px solid #CCC;
margin-bottom: -2px;
border-bottom-width: 0px;
-webkit-border-top-left-radius: 5px;
-webkit-border-top-right-radius: 5px;
-moz-border-radius-topleft: 5px;
-moz-border-radius-topright: 5px;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
color: white;
line-height: 14px;
font-size: 12px;
padding: 0px 7px;
text-align: center;
clear: both;
background-color: white;
float: right;
}
#languageLinks a.addLanguageButton {
font-size: 12px;
padding: 3px 6px;
}
@media screen and (max-width:36em) {
#tabstable > tbody > tr > td {
display: block;
float: left;
width: 100%;
}
#languageLinks tr {
display: block;
float: left;
margin-bottom: -3px;
margin-left: -5px;
margin-top: 5px;
}
#languageLinks .unselectedTab a {
margin-left: 0;
margin-right: 5px;
border-right: 1px solid #CCC;
}
#languageLinks .selectedTab a {
margin-left: 0;
margin-right: 5px;
border-right: 1px solid #CCC;
border-bottom: 1px solid #FFF;
}
#tabstable {
width: 100%;
}
#languageLinks a.addLanguageButton {
margin-top: 2px;
margin-bottom: 1px;
}
.editlangsection {
border-bottom-width: 1px;
}
}
.minitoc:not(.mammoth-minitoc) {
display: none;
}
0vfsobgurlx0r6mcpypw92v32flnn6t
MediaWiki:Gadget-FastRollback.js
8
17988
88978
2025-09-17T22:36:28Z
en>Ioaxxere
0
[[mw:Migrating mw.Uri to URL]]
88978
javascript
text/javascript
// {{documentation}}
/*jshint undef:true, latedef:true, undef:true, shadow:true, scripturl:true */
/*global mw, jQuery */
(function () {
var api = new mw.Api();
function processLink(link, title, token, user, markbot) {
var wrapper = link.parentNode;
var bracketed = (wrapper.textContent[0] === '[');
link.addEventListener('click', function (ev) {
ev.preventDefault();
var summary = prompt('About to rollback edits by [[User:' + user + ']] to [[' + title + ']].\n\nEdit summary (empty for default):',
'Reverted edits by [[Special:Contributions/' + user + '|' + user + ']]. If you think this rollback is in error, please leave a message on my [[User talk:' + mw.config.get('wgUserName') + '|talk page]].');
if (summary === null)
return;
wrapper.textContent = bracketed ? '[wait]' : 'wait';
api.post({
action: 'rollback',
title: title,
user: user,
token: token,
markbot: markbot ? '1' : void(0),
summary: summary ? summary : void(0)
}).then(function (result) {
var diffLink = document.createElement('a');
diffLink.href = mw.config.get('wgScript') + '?diff=' + result.rollback.revid + '&diffonly=1';
diffLink.textContent = 'diff';
var reloadLink = document.createElement('a');
reloadLink.href = 'javascript:location.reload();';
reloadLink.textContent = 'reload';
wrapper.textContent = '';
if (bracketed) wrapper.appendChild(document.createTextNode('['));
wrapper.appendChild(document.createTextNode('done: '));
wrapper.appendChild(diffLink);
wrapper.appendChild(document.createTextNode(' | '));
wrapper.appendChild(reloadLink);
if (bracketed) wrapper.appendChild(document.createTextNode(']'));
}, function (code, result, xhr) {
link.textContent = 'retry';
var cnode = document.createElement('code');
cnode.style.borderBottom = '1px dotted black';
cnode.style.cursor = 'help';
cnode.textContent = code;
switch (code) {
case 'http':
cnode.title = 'HTTP error: ' + result.textStatus;
break;
case 'ok-but-empty':
cnode.title = 'Received empty response from the server';
break;
default:
cnode.title = result.error.info;
}
wrapper.textContent = '';
if (bracketed) wrapper.appendChild(document.createTextNode('['));
wrapper.appendChild(document.createTextNode('error: '));
wrapper.appendChild(cnode);
wrapper.appendChild(document.createTextNode(' ('));
wrapper.appendChild(link);
wrapper.appendChild(document.createTextNode(')'));
if (bracketed) wrapper.appendChild(document.createTextNode(']'));
console.error(result);
});
}, false);
}
jQuery(document).ready(function() {
var rolllinks = document.getElementsByClassName('mw-rollback-link');
for (var i = 0; i < rolllinks.length; ++i) {
var links = rolllinks[i].getElementsByTagName('a');
if (links.length !== 1)
continue;
var link = links[0];
try {
var uri = new URL(link.href);
if (uri.searchParams.get("action") === 'rollback')
processLink(link, uri.searchParams.get("title"), uri.searchParams.get("token"), uri.searchParams.get("from"), uri.searchParams.get("bot") && true);
} catch (e) {
/* swallow */
}
}
function makeRestoreLink(oldid, uname) {
var link = document.createElement('a');
var span = document.createElement('span');
link.href = mw.config.get('wgScript') + '?action=edit&oldid=' + oldid;
function doRestore() {
function showError(action, code, result) {
var cnode = document.createElement('code');
cnode.textContent = code;
switch (code) {
case 'http':
cnode.title = 'HTTP error: ' + result.textStatus;
break;
case 'ok-but-empty':
cnode.title = 'Received empty response from the server';
break;
case 'edit-failure':
cnode.textContent = result.edit.code;
cnode.title = 'Edit error: ' + result.edit.info;
break;
default:
cnode.title = result.error.info;
}
var retryLink = document.createElement('a');
retryLink.href = link.href;
retryLink.textContent = 'retry';
retryLink.addEventListener('click', function (ev) {
ev.preventDefault();
doRestore();
}, false);
span.textContent = '';
span.appendChild(document.createTextNode('error: '));
span.appendChild(cnode);
span.appendChild(document.createTextNode(' ('));
span.appendChild(retryLink);
span.appendChild(document.createTextNode(')'));
}
var summary = prompt('Edit summary for this reversion:', 'Restored [[Special:Permalink/' + oldid + '|revision ' + oldid + ']] by [[User:' + uname + ']]');
if (summary === null)
return;
span.textContent = 'grabbing oldid';
if (link.parentNode) {
link.parentNode.insertBefore(span, link);
link.parentNode.removeChild(link);
}
api.get({
action: 'query',
pageids: mw.config.get('wgArticleId'),
prop: 'revisions',
rvstartid: oldid,
rvlimit: 1,
rvprop: 'content'
}).then(function (result, xhr) {
console.info(result);
var starttimestamp = (new Date(xhr.getResponseHeader('Date'))).toISOString(); // XXX: acceptable?
var content = result.query.pages[mw.config.get('wgArticleId')].revisions[0]['*'];
span.textContent = 'grabbing latest revision';
api.get({
action: 'query',
pageids: mw.config.get('wgArticleId'),
prop: 'revisions',
rvlimit: 1,
rvprop: 'ids|timestamp'
}).then(function (result) {
console.info(result);
var basetimestamp = result.query.pages[mw.config.get('wgArticleId')].revisions[0].timestamp;
if (result.query.pages[mw.config.get('wgArticleId')].revisions[0].revid === Number(oldid)) {
span.textContent = 'this is already the latest revision';
return;
}
span.textContent = 'reverting';
api.post({
action: 'edit',
pageid: mw.config.get('wgArticleId'),
token: mw.user.tokens.get('csrfToken'),
basetimestamp: basetimestamp,
starttimestamp: starttimestamp,
nocreate: '1',
text: content,
summary: summary
}).then(function (result) {
if (result.edit.result === 'Success') {
span.textContent = 'done';
} else {
showError('saving', 'edit-failure', result);
}
}, function (code, result) {
showError('saving', code, result);
});
}, function (code, result) {
showError('grabbing latest revision data', code, result);
});
}, function (code, result) {
showError('grabbing old revision text', code, result);
});
}
link.addEventListener('click', function (ev) {
ev.preventDefault();
doRestore();
}, false);
return link;
}
// are we viewing an old revision?
var revnav = document.getElementById('mw-revision-nav');
if (revnav) {
var uname = document.getElementById('mw-revision-info').getElementsByClassName('mw-userlink')[0].textContent;
var oldid = mw.config.get('wgRevisionId'); // XXX: suppress if this is the latest revid
var link = makeRestoreLink(oldid, uname);
link.textContent = 'restore this version';
revnav.appendChild(document.createTextNode(' ('));
revnav.appendChild(link);
revnav.appendChild(document.createTextNode(')'));
return;
}
// intentionally not enabled on history pages;
// I want the user to see to what version they are reverting
// either directly or at least a diff
// may be changed on request, though
function addRestoreLink(titlenode) {
if (!titlenode)
return;
var titlelinks = titlenode.getElementsByTagName('a');
var userlink = titlenode.parentNode.getElementsByClassName('mw-userlink')[0];
var uname = userlink.textContent;
var uri = new URL(titlelinks[0].href);
// XXX: suppress for latest revid
var rlink = makeRestoreLink(uri.searchParams.get("oldid"), userlink.textContent);
rlink.textContent = 'restore';
titlelinks[1].parentNode.insertBefore(rlink, titlelinks[1].nextSibling);
titlelinks[1].parentNode.insertBefore(document.createTextNode(' | '), titlelinks[1].nextSibling);
}
// are we viewing a diff?
var ot = document.getElementById('mw-diff-otitle1');
var nt = document.getElementById('mw-diff-ntitle1');
var ri = document.getElementById('mw-revision-info');
if ((nt || ot) && mw.config.get('wgArticleId')) {
addRestoreLink(ot);
addRestoreLink(nt);
} else if (ri && (mw.config.get('wgRevisionId') !== mw.config.get('wgCurRevisionId'))) {
var userlink = ri.getElementsByClassName('mw-userlink')[0];
var rlink = makeRestoreLink(mw.config.get('wgRevisionId'), userlink.textContent);
rlink.textContent = 'restore';
var fh = document.getElementById('firstHeading');
var span = document.createElement('span');
span.className = 'mw-editsection';
span.appendChild(document.createTextNode('['));
span.appendChild(rlink);
span.appendChild(document.createTextNode(']'));
fh.appendChild(span);
}
});
})();
qf0oxyah5m8wbu56gcchqp2bu762ig5
88979
88978
2026-08-06T23:24:08Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-FastRollback.js]]
88978
javascript
text/javascript
// {{documentation}}
/*jshint undef:true, latedef:true, undef:true, shadow:true, scripturl:true */
/*global mw, jQuery */
(function () {
var api = new mw.Api();
function processLink(link, title, token, user, markbot) {
var wrapper = link.parentNode;
var bracketed = (wrapper.textContent[0] === '[');
link.addEventListener('click', function (ev) {
ev.preventDefault();
var summary = prompt('About to rollback edits by [[User:' + user + ']] to [[' + title + ']].\n\nEdit summary (empty for default):',
'Reverted edits by [[Special:Contributions/' + user + '|' + user + ']]. If you think this rollback is in error, please leave a message on my [[User talk:' + mw.config.get('wgUserName') + '|talk page]].');
if (summary === null)
return;
wrapper.textContent = bracketed ? '[wait]' : 'wait';
api.post({
action: 'rollback',
title: title,
user: user,
token: token,
markbot: markbot ? '1' : void(0),
summary: summary ? summary : void(0)
}).then(function (result) {
var diffLink = document.createElement('a');
diffLink.href = mw.config.get('wgScript') + '?diff=' + result.rollback.revid + '&diffonly=1';
diffLink.textContent = 'diff';
var reloadLink = document.createElement('a');
reloadLink.href = 'javascript:location.reload();';
reloadLink.textContent = 'reload';
wrapper.textContent = '';
if (bracketed) wrapper.appendChild(document.createTextNode('['));
wrapper.appendChild(document.createTextNode('done: '));
wrapper.appendChild(diffLink);
wrapper.appendChild(document.createTextNode(' | '));
wrapper.appendChild(reloadLink);
if (bracketed) wrapper.appendChild(document.createTextNode(']'));
}, function (code, result, xhr) {
link.textContent = 'retry';
var cnode = document.createElement('code');
cnode.style.borderBottom = '1px dotted black';
cnode.style.cursor = 'help';
cnode.textContent = code;
switch (code) {
case 'http':
cnode.title = 'HTTP error: ' + result.textStatus;
break;
case 'ok-but-empty':
cnode.title = 'Received empty response from the server';
break;
default:
cnode.title = result.error.info;
}
wrapper.textContent = '';
if (bracketed) wrapper.appendChild(document.createTextNode('['));
wrapper.appendChild(document.createTextNode('error: '));
wrapper.appendChild(cnode);
wrapper.appendChild(document.createTextNode(' ('));
wrapper.appendChild(link);
wrapper.appendChild(document.createTextNode(')'));
if (bracketed) wrapper.appendChild(document.createTextNode(']'));
console.error(result);
});
}, false);
}
jQuery(document).ready(function() {
var rolllinks = document.getElementsByClassName('mw-rollback-link');
for (var i = 0; i < rolllinks.length; ++i) {
var links = rolllinks[i].getElementsByTagName('a');
if (links.length !== 1)
continue;
var link = links[0];
try {
var uri = new URL(link.href);
if (uri.searchParams.get("action") === 'rollback')
processLink(link, uri.searchParams.get("title"), uri.searchParams.get("token"), uri.searchParams.get("from"), uri.searchParams.get("bot") && true);
} catch (e) {
/* swallow */
}
}
function makeRestoreLink(oldid, uname) {
var link = document.createElement('a');
var span = document.createElement('span');
link.href = mw.config.get('wgScript') + '?action=edit&oldid=' + oldid;
function doRestore() {
function showError(action, code, result) {
var cnode = document.createElement('code');
cnode.textContent = code;
switch (code) {
case 'http':
cnode.title = 'HTTP error: ' + result.textStatus;
break;
case 'ok-but-empty':
cnode.title = 'Received empty response from the server';
break;
case 'edit-failure':
cnode.textContent = result.edit.code;
cnode.title = 'Edit error: ' + result.edit.info;
break;
default:
cnode.title = result.error.info;
}
var retryLink = document.createElement('a');
retryLink.href = link.href;
retryLink.textContent = 'retry';
retryLink.addEventListener('click', function (ev) {
ev.preventDefault();
doRestore();
}, false);
span.textContent = '';
span.appendChild(document.createTextNode('error: '));
span.appendChild(cnode);
span.appendChild(document.createTextNode(' ('));
span.appendChild(retryLink);
span.appendChild(document.createTextNode(')'));
}
var summary = prompt('Edit summary for this reversion:', 'Restored [[Special:Permalink/' + oldid + '|revision ' + oldid + ']] by [[User:' + uname + ']]');
if (summary === null)
return;
span.textContent = 'grabbing oldid';
if (link.parentNode) {
link.parentNode.insertBefore(span, link);
link.parentNode.removeChild(link);
}
api.get({
action: 'query',
pageids: mw.config.get('wgArticleId'),
prop: 'revisions',
rvstartid: oldid,
rvlimit: 1,
rvprop: 'content'
}).then(function (result, xhr) {
console.info(result);
var starttimestamp = (new Date(xhr.getResponseHeader('Date'))).toISOString(); // XXX: acceptable?
var content = result.query.pages[mw.config.get('wgArticleId')].revisions[0]['*'];
span.textContent = 'grabbing latest revision';
api.get({
action: 'query',
pageids: mw.config.get('wgArticleId'),
prop: 'revisions',
rvlimit: 1,
rvprop: 'ids|timestamp'
}).then(function (result) {
console.info(result);
var basetimestamp = result.query.pages[mw.config.get('wgArticleId')].revisions[0].timestamp;
if (result.query.pages[mw.config.get('wgArticleId')].revisions[0].revid === Number(oldid)) {
span.textContent = 'this is already the latest revision';
return;
}
span.textContent = 'reverting';
api.post({
action: 'edit',
pageid: mw.config.get('wgArticleId'),
token: mw.user.tokens.get('csrfToken'),
basetimestamp: basetimestamp,
starttimestamp: starttimestamp,
nocreate: '1',
text: content,
summary: summary
}).then(function (result) {
if (result.edit.result === 'Success') {
span.textContent = 'done';
} else {
showError('saving', 'edit-failure', result);
}
}, function (code, result) {
showError('saving', code, result);
});
}, function (code, result) {
showError('grabbing latest revision data', code, result);
});
}, function (code, result) {
showError('grabbing old revision text', code, result);
});
}
link.addEventListener('click', function (ev) {
ev.preventDefault();
doRestore();
}, false);
return link;
}
// are we viewing an old revision?
var revnav = document.getElementById('mw-revision-nav');
if (revnav) {
var uname = document.getElementById('mw-revision-info').getElementsByClassName('mw-userlink')[0].textContent;
var oldid = mw.config.get('wgRevisionId'); // XXX: suppress if this is the latest revid
var link = makeRestoreLink(oldid, uname);
link.textContent = 'restore this version';
revnav.appendChild(document.createTextNode(' ('));
revnav.appendChild(link);
revnav.appendChild(document.createTextNode(')'));
return;
}
// intentionally not enabled on history pages;
// I want the user to see to what version they are reverting
// either directly or at least a diff
// may be changed on request, though
function addRestoreLink(titlenode) {
if (!titlenode)
return;
var titlelinks = titlenode.getElementsByTagName('a');
var userlink = titlenode.parentNode.getElementsByClassName('mw-userlink')[0];
var uname = userlink.textContent;
var uri = new URL(titlelinks[0].href);
// XXX: suppress for latest revid
var rlink = makeRestoreLink(uri.searchParams.get("oldid"), userlink.textContent);
rlink.textContent = 'restore';
titlelinks[1].parentNode.insertBefore(rlink, titlelinks[1].nextSibling);
titlelinks[1].parentNode.insertBefore(document.createTextNode(' | '), titlelinks[1].nextSibling);
}
// are we viewing a diff?
var ot = document.getElementById('mw-diff-otitle1');
var nt = document.getElementById('mw-diff-ntitle1');
var ri = document.getElementById('mw-revision-info');
if ((nt || ot) && mw.config.get('wgArticleId')) {
addRestoreLink(ot);
addRestoreLink(nt);
} else if (ri && (mw.config.get('wgRevisionId') !== mw.config.get('wgCurRevisionId'))) {
var userlink = ri.getElementsByClassName('mw-userlink')[0];
var rlink = makeRestoreLink(mw.config.get('wgRevisionId'), userlink.textContent);
rlink.textContent = 'restore';
var fh = document.getElementById('firstHeading');
var span = document.createElement('span');
span.className = 'mw-editsection';
span.appendChild(document.createTextNode('['));
span.appendChild(rlink);
span.appendChild(document.createTextNode(']'));
fh.appendChild(span);
}
});
})();
qf0oxyah5m8wbu56gcchqp2bu762ig5
MediaWiki:Gadget-AudioBotBlacklist.js
8
17989
88980
2026-02-20T22:56:09Z
en>Fenakhay
0
88980
javascript
text/javascript
/**
* AudioBot Blacklist Gadget
*
* Adds a button on the blacklist page to add audio files without editing.
* The blacklist is stored as JSON at [[Wiktionary:Approved Lingua Libre users/AudioBotBlacklist]].
*/
/* jshint esversion: 8 */
( function () {
'use strict';
const BLACKLIST_PAGE = 'Wiktionary:Approved Lingua Libre users/AudioBotBlacklist';
let AddDialog = null;
let EditDialog = null;
let BulkAddDialog = null;
const createStore = ( initial ) => {
let data = initial;
let subscribers = [];
const notify = () => subscribers.forEach( ( fn ) => fn( data ) );
return {
get: () => data,
set: ( value ) => { data = value; notify(); },
subscribe: ( fn ) => {
subscribers.push( fn );
return () => { subscribers = subscribers.filter( ( f ) => f !== fn ); };
},
has: ( key ) => data.hasOwnProperty( key ),
put: ( key, value ) => { data[ key ] = value; notify(); },
remove: ( key ) => { delete data[ key ]; notify(); },
entries: () => Object.entries( data ).sort( sortByAdded ),
count: () => Object.keys( data ).length
};
};
const blacklistStore = createStore( {} );
let blacklistLoaded = false;
let blacklistBaseRevId = null;
const normalizeFilename = ( name ) => name.trim().replace( /^File:/i, '' ).replace( /_/g, ' ' ).trim();
const getEntryReason = ( val ) => ( val && val.reason !== undefined ? val.reason : '' );
const getEntryAdded = ( val ) => ( val && val.added );
const makeEntry = ( reason, added ) => ( added ? { reason, added } : { reason } );
const sortByAdded = ( a, b ) => {
const addedA = getEntryAdded( a[ 1 ] ) || '\u0000';
const addedB = getEntryAdded( b[ 1 ] ) || '\u0000';
return addedB.localeCompare( addedA );
};
let api = null;
let commonsApi = null;
const loadBlacklist = async ( forceReload ) => {
if ( blacklistLoaded && !forceReload ) {
return;
}
const response = await api.get( {
action: 'query',
titles: BLACKLIST_PAGE,
prop: 'revisions',
rvprop: 'content|ids',
rvslots: 'main',
formatversion: 2
} );
const page = response.query.pages[ 0 ];
if ( page.missing ) {
blacklistStore.set( {} );
blacklistBaseRevId = null;
} else {
blacklistStore.set( JSON.parse( page.revisions[ 0 ].slots.main.content ) );
blacklistBaseRevId = page.revisions[ 0 ].revid;
}
blacklistLoaded = true;
};
const saveBlacklist = async ( summary ) => {
const data = blacklistStore.get();
const sortedBlacklist = Object.entries( data ).sort( sortByAdded ).reduce( ( acc, entry ) => {
acc[ entry[ 0 ] ] = entry[ 1 ];
return acc;
}, {} );
const params = {
action: 'edit',
title: BLACKLIST_PAGE,
text: JSON.stringify( sortedBlacklist, null, 2 ),
summary,
minor: false
};
if ( blacklistBaseRevId ) {
params.baserevid = blacklistBaseRevId;
}
const result = await api.postWithToken( 'csrf', params );
if ( result.edit && result.edit.newrevid ) {
blacklistBaseRevId = result.edit.newrevid;
}
};
const checkFileExists = async ( filename ) => {
const data = await commonsApi.get( {
action: 'query',
titles: 'File:' + filename,
formatversion: 2
} );
const page = data.query.pages[ 0 ];
return !page.missing;
};
const addToBlacklist = async ( filename, reason ) => {
await loadBlacklist( true );
if ( blacklistStore.has( filename ) ) {
throw new Error( 'This file is already in the blacklist.' );
}
const added = new Date().toISOString();
blacklistStore.put( filename, makeEntry( reason, added ) );
await saveBlacklist( `Added [[File:${filename}]] to blacklist: ${reason.substring( 0, 100 )}` );
};
const addBulkToBlacklist = async ( entries ) => {
await loadBlacklist( true );
let added = 0;
const data = blacklistStore.get();
const addedAt = new Date().toISOString();
for ( const [ filename, reason ] of entries ) {
if ( !data.hasOwnProperty( filename ) ) {
data[ filename ] = makeEntry( reason, addedAt );
added++;
}
}
if ( added > 0 ) {
blacklistStore.set( data );
await saveBlacklist( `Bulk added ${added} file(s) to blacklist` );
}
return added;
};
const editBlacklistEntry = async ( filename, newReason ) => {
await loadBlacklist( true );
if ( !blacklistStore.has( filename ) ) {
throw new Error( 'This file is not in the blacklist.' );
}
const existingAdded = getEntryAdded( blacklistStore.get()[ filename ] );
blacklistStore.put( filename, makeEntry( newReason, existingAdded ) );
await saveBlacklist( `Updated reason for [[File:${filename}]]` );
};
const removeFromBlacklist = async ( filename ) => {
await loadBlacklist( true );
if ( !blacklistStore.has( filename ) ) {
throw new Error( 'This file is not in the blacklist.' );
}
blacklistStore.remove( filename );
await saveBlacklist( `Removed [[File:${filename}]] from blacklist` );
};
const removeBulkFromBlacklist = async ( filenames ) => {
await loadBlacklist( true );
let removed = 0;
const data = blacklistStore.get();
for ( const filename of filenames ) {
if ( data.hasOwnProperty( filename ) ) {
delete data[ filename ];
removed++;
}
}
if ( removed > 0 ) {
blacklistStore.set( data );
await saveBlacklist( `Bulk removed ${removed} file(s) from blacklist` );
}
return removed;
};
const createDialog = ( config ) => {
class Dialog extends OO.ui.ProcessDialog {
initialize() {
super.initialize();
config.init( this );
return this;
}
getSetupProcess( data ) {
return super.getSetupProcess( data ).next( () => config.setup( this, data || {} ) );
}
getActionProcess( action ) {
if ( action === 'save' ) {
return new OO.ui.Process( async () => {
try {
await config.onSave( this );
this.close( { action } );
} catch ( error ) {
throw new OO.ui.Error( error.message || String( error ) );
}
} );
}
return super.getActionProcess( action );
}
getBodyHeight() {
return config.bodyHeight || 200;
}
}
Dialog.static = Object.assign( {}, OO.ui.ProcessDialog.static, {
name: config.name,
title: config.title,
actions: [
{ action: 'save', label: config.saveLabel || 'Save', flags: [ 'primary', 'progressive' ] },
{ label: 'Cancel', flags: 'safe' }
]
} );
return Dialog;
};
const createAddDialog = () => createDialog( {
name: 'audiobotAdd',
title: 'Add to AudioBot Blacklist',
saveLabel: 'Add to Blacklist',
bodyHeight: 250,
init( dialog ) {
dialog.filenameInput = new OO.ui.TextInputWidget( { placeholder: 'e.g. En-us-example.ogg' } );
dialog.reasonInput = new OO.ui.MultilineTextInputWidget( { placeholder: 'Reason for blacklisting...', rows: 3 } );
dialog.fileExistsMessage = new OO.ui.MessageWidget( { type: 'warning', inline: true, label: 'This file does not exist on Commons.' } );
dialog.fileExistsMessage.$element.hide();
dialog.filenameInput.on( 'change', OO.ui.debounce( async () => {
const filename = normalizeFilename( dialog.filenameInput.getValue() );
if ( filename ) {
const exists = await checkFileExists( filename );
dialog.fileExistsMessage.$element.toggle( !exists );
} else {
dialog.fileExistsMessage.$element.hide();
}
}, 500 ) );
const content = new OO.ui.PanelLayout( { padded: true, expanded: false } );
content.$element.append(
new OO.ui.FieldLayout( dialog.filenameInput, { label: 'Audio filename (without File: prefix):', align: 'top' } ).$element,
dialog.fileExistsMessage.$element,
new OO.ui.FieldLayout( dialog.reasonInput, { label: 'Reason:', align: 'top' } ).$element
);
dialog.$body.append( content.$element );
},
setup( dialog, data ) {
dialog.filenameInput.setValue( data.prefillFilename || '' );
dialog.reasonInput.setValue( '' );
dialog.fileExistsMessage.$element.hide();
if ( data.prefillFilename ) {
dialog.filenameInput.emit( 'change' );
}
},
async onSave( dialog ) {
const filename = normalizeFilename( dialog.filenameInput.getValue() );
const reason = dialog.reasonInput.getValue().trim();
if ( !filename ) { throw new Error( 'Please enter a filename.' ); }
if ( !reason ) { throw new Error( 'Please enter a reason.' ); }
await addToBlacklist( filename, reason );
mw.notify( `Successfully added "${filename}" to the blacklist.`, { type: 'success' } );
}
} );
const createEditDialog = () => createDialog( {
name: 'audiobotEdit',
title: 'Edit Blacklist Reason',
saveLabel: 'Save',
bodyHeight: 180,
init( dialog ) {
dialog.filename = '';
dialog.filenameLabel = new OO.ui.LabelWidget( { label: '' } );
dialog.reasonInput = new OO.ui.MultilineTextInputWidget( { placeholder: 'Reason for blacklisting...', rows: 3 } );
const content = new OO.ui.PanelLayout( { padded: true, expanded: false } );
content.$element.append(
new OO.ui.FieldLayout( dialog.filenameLabel, { label: 'Filename:', align: 'top' } ).$element,
new OO.ui.FieldLayout( dialog.reasonInput, { label: 'Reason:', align: 'top' } ).$element
);
dialog.$body.append( content.$element );
},
setup( dialog, data ) {
dialog.filename = data.filename || '';
dialog.filenameLabel.setLabel( dialog.filename );
dialog.reasonInput.setValue( data.reason || '' );
},
async onSave( dialog ) {
const reason = dialog.reasonInput.getValue().trim();
if ( !reason ) { throw new Error( 'Please enter a reason.' ); }
await editBlacklistEntry( dialog.filename, reason );
mw.notify( `Successfully updated reason for "${dialog.filename}".`, { type: 'success' } );
}
} );
const createBulkAddDialog = () => createDialog( {
name: 'audiobotBulkAdd',
title: 'Bulk Add to AudioBot Blacklist',
saveLabel: 'Add All',
bodyHeight: 320,
init( dialog ) {
dialog.filesInput = new OO.ui.MultilineTextInputWidget( { placeholder: 'Enter one filename per line...', rows: 6 } );
dialog.reasonInput = new OO.ui.MultilineTextInputWidget( { placeholder: 'Reason for blacklisting...', rows: 3 } );
const content = new OO.ui.PanelLayout( { padded: true, expanded: false } );
content.$element.append(
new OO.ui.FieldLayout( dialog.filesInput, { label: 'Filenames (one per line, without File: prefix):', align: 'top' } ).$element,
new OO.ui.FieldLayout( dialog.reasonInput, { label: 'Reason (applies to all):', align: 'top' } ).$element
);
dialog.$body.append( content.$element );
},
setup( dialog ) {
dialog.filesInput.setValue( '' );
dialog.reasonInput.setValue( '' );
},
async onSave( dialog ) {
const filesText = dialog.filesInput.getValue().trim();
const reason = dialog.reasonInput.getValue().trim();
if ( !filesText ) { throw new Error( 'Please enter at least one filename.' ); }
if ( !reason ) { throw new Error( 'Please enter a reason.' ); }
const filenames = filesText.split( '\n' ).map( normalizeFilename ).filter( ( n ) => n.length > 0 );
if ( filenames.length === 0 ) { throw new Error( 'Please enter at least one valid filename.' ); }
const entries = filenames.map( ( f ) => [ f, reason ] );
const added = await addBulkToBlacklist( entries );
mw.notify( `Successfully added ${added} file(s) to the blacklist.`, { type: 'success' } );
}
} );
let windowManager = null;
const showDialog = ( DialogClass, data ) => {
if ( !windowManager ) {
windowManager = new OO.ui.WindowManager();
$( document.body ).append( windowManager.$element );
}
const dialog = new DialogClass( { size: 'medium' } );
windowManager.addWindows( [ dialog ] );
windowManager.openWindow( dialog, data || {} );
};
let selectedFiles = new Set();
let $selectAllCheckbox = null;
let $removeSelectedBtn = null;
const CELL_STYLE = {
padding: '8px 12px',
borderBottom: '1px solid var(--border-color-base)',
verticalAlign: 'middle'
};
const cellCss = ( overrides ) => Object.assign( {}, CELL_STYLE, overrides );
const createIconButton = ( icon, title, destructive ) => new OO.ui.ButtonWidget( {
icon,
title,
framed: false,
flags: destructive ? [ 'destructive' ] : []
} );
const confirmAndRun = async ( message, action, successMsg ) => {
const confirmed = await OO.ui.confirm( message );
if ( !confirmed ) {
return false;
}
try {
const result = await action();
const msg = typeof successMsg === 'function' ? successMsg( result ) : successMsg;
mw.notify( msg, { type: 'success' } );
return true;
} catch ( error ) {
mw.notify( error.message || String( error ), { type: 'error' } );
return false;
}
};
const updateRemoveSelectedButton = () => {
if ( $removeSelectedBtn ) {
$removeSelectedBtn.setDisabled( selectedFiles.size === 0 );
$removeSelectedBtn.setLabel( `Remove selected (${selectedFiles.size})` );
}
};
const updateEntryCount = () => {
$( '#audiobot-entry-count' ).text( `${blacklistStore.count()} file(s) in blacklist` );
};
const renderBlacklistTable = () => {
if ( !$( '#audiobot-blacklist-controls' ).length ) {
return;
}
const entries = blacklistStore.entries();
updateEntryCount();
$( '.mw-json' ).hide();
$( '#audiobot-blacklist-table' ).remove();
selectedFiles.clear();
$selectAllCheckbox = new OO.ui.CheckboxInputWidget();
$selectAllCheckbox.on( 'change', ( selected ) => {
selectedFiles.clear();
$( '#audiobot-blacklist-table tbody tr' ).each( function () {
const $checkbox = $( this ).find( '.oo-ui-checkboxInputWidget input' );
$checkbox.prop( 'checked', selected ).trigger( 'change' );
if ( selected ) {
const filename = $( this ).data( 'filename' );
selectedFiles.add( filename );
}
} );
updateRemoveSelectedButton();
} );
const $table = $( '<table>' )
.attr( 'id', 'audiobot-blacklist-table' )
.css( { width: '100%', borderCollapse: 'collapse', marginTop: '1em' } );
const formatAdded = ( iso ) => {
if ( !iso ) { return ''; }
const d = new Date( iso );
if ( isNaN( d.getTime() ) ) { return iso; }
return d.toLocaleDateString( 'en-GB', { year: 'numeric', month: '2-digit', day: '2-digit' } );
};
const $thead = $( '<thead>' ).append(
$( '<tr>' ).css( { backgroundColor: 'var(--background-color-interactive)' } ).append(
$( '<th>' ).css( cellCss( { width: '40px', textAlign: 'center' } ) ).append( $selectAllCheckbox.$element ),
$( '<th>' ).text( 'Filename' ).css( cellCss( { textAlign: 'left' } ) ),
$( '<th>' ).text( 'Reason' ).css( cellCss( { textAlign: 'left' } ) ),
$( '<th>' ).text( 'Added' ).css( cellCss( { width: '100px', textAlign: 'left' } ) ),
$( '<th>' ).text( 'Actions' ).css( cellCss( { width: '100px', textAlign: 'center' } ) )
)
);
const $tbody = $( '<tbody>' );
for ( const [ filename, rawValue ] of entries ) {
const reason = getEntryReason( rawValue );
const added = getEntryAdded( rawValue );
const checkbox = new OO.ui.CheckboxInputWidget();
checkbox.on( 'change', ( selected ) => {
if ( selected ) {
selectedFiles.add( filename );
} else {
selectedFiles.delete( filename );
}
updateRemoveSelectedButton();
} );
const editBtn = createIconButton( 'edit', 'Edit reason', false );
editBtn.on( 'click', () => showDialog( EditDialog, { filename, reason } ) );
const removeBtn = createIconButton( 'trash', 'Remove from blacklist', true );
removeBtn.on( 'click', () => confirmAndRun(
`Remove "${filename}" from the blacklist?`,
() => removeFromBlacklist( filename ),
`Removed "${filename}" from the blacklist.`
) );
const $row = $( '<tr>' ).data( 'filename', filename ).append(
$( '<td>' ).css( cellCss( { textAlign: 'center' } ) ).append( checkbox.$element ),
$( '<td>' ).css( CELL_STYLE ).text( filename ),
$( '<td>' ).css( CELL_STYLE ).text( reason ),
$( '<td>' ).css( CELL_STYLE ).attr( 'title', added || '' ).text( formatAdded( added ) ),
$( '<td>' ).css( cellCss( { textAlign: 'center', whiteSpace: 'nowrap' } ) ).append( editBtn.$element, removeBtn.$element )
);
$tbody.append( $row );
}
$table.append( $thead, $tbody );
$( '#audiobot-blacklist-controls' ).after( $table );
};
const addBlacklistPageButtons = () => {
const addButton = new OO.ui.ButtonWidget( {
label: 'Add file',
icon: 'add',
flags: [ 'progressive' ]
} );
addButton.on( 'click', () => showDialog( AddDialog ) );
const bulkAddButton = new OO.ui.ButtonWidget( {
label: 'Bulk add',
icon: 'add'
} );
bulkAddButton.on( 'click', () => showDialog( BulkAddDialog ) );
$removeSelectedBtn = new OO.ui.ButtonWidget( {
label: 'Remove selected (0)',
icon: 'trash',
flags: [ 'destructive' ],
disabled: true
} );
$removeSelectedBtn.on( 'click', () => {
if ( selectedFiles.size === 0 ) {
return;
}
confirmAndRun(
`Remove ${selectedFiles.size} file(s) from the blacklist?`,
() => removeBulkFromBlacklist( Array.from( selectedFiles ) ),
( removed ) => `Removed ${removed} file(s) from the blacklist.`
);
} );
const $label = $( '<span>' )
.text( 'AudioBot Blacklist: ' )
.css( 'font-weight', 'bold' );
const $entryCount = $( '<span>' )
.attr( 'id', 'audiobot-entry-count' )
.css( { marginLeft: '1em', fontStyle: 'italic' } )
.text( 'Loading...' );
const $controls = $( '<div>' )
.attr( 'id', 'audiobot-blacklist-controls' )
.css( {
marginBottom: '1em',
padding: '0.5em 1em',
backgroundColor: 'var(--background-color-neutral-subtle)',
border: '1px solid var(--border-color-subtle)',
borderRadius: '2px',
color: 'var(--color-base)'
} )
.append(
$label,
addButton.$element,
' ',
bulkAddButton.$element,
' ',
$removeSelectedBtn.$element,
$entryCount
);
$( '#mw-content-text' ).prepend( $controls );
renderBlacklistTable();
};
const addInlineBlacklistButtons = ( $content ) => {
const $root = ( $content && $content.length ) ? $content : $( document );
for ( const table of $root.find( 'table.audiotable' ).toArray() ) {
const $table = $( table );
const $player = $table.find( '[data-mwtitle]' );
if ( !$player.length ) {
continue;
}
const filename = $player.attr( 'data-mwtitle' );
if ( !filename || $table.data( 'blacklistAdded' ) ) {
continue;
}
$table.data( 'blacklistAdded', true );
const btn = createIconButton( 'block', 'Add to AudioBot blacklist', true );
btn.on( 'click', () => showDialog( AddDialog, { prefillFilename: normalizeFilename( filename ) } ) );
const $actionsCell = $( '<td>' )
.addClass( 'audio-actions' )
.css( { verticalAlign: 'middle' } )
.append( btn.$element );
$table.find( 'tr' ).first().append( $actionsCell );
}
};
const init = async () => {
try {
await mw.loader.using( [
'oojs-ui-core',
'oojs-ui-widgets',
'oojs-ui-windows',
'oojs-ui.styles.icons-moderation',
'oojs-ui.styles.icons-editing-core',
'oojs-ui.styles.icons-interactions',
'mediawiki.api',
'mediawiki.ForeignApi'
] );
api = new mw.Api();
commonsApi = new mw.ForeignApi( 'https://commons.wikimedia.org/w/api.php' );
const userGroups = mw.config.get( 'wgUserGroups' ) || [];
if ( !userGroups.includes( 'autoconfirmed' ) ) {
return;
}
const prefs = mw.wiktGadgetPrefs.get( 'audiobot-blacklist', {
label: {
en: 'AudioBot Blacklist'
}
}, {
showInlineButtons: {
type: 'boolean',
default: false,
label: {
en: 'Show inline blacklist buttons next to audio files'
}
}
} );
AddDialog = createAddDialog();
EditDialog = createEditDialog();
BulkAddDialog = createBulkAddDialog();
const isBlacklistPage = mw.config.get( 'wgPageName' ) === BLACKLIST_PAGE.replace( / /g, '_' );
const isViewAction = mw.config.get( 'wgAction' ) === 'view';
const isDiff = mw.config.get( 'wgDiffOldId' ) || mw.config.get( 'wgDiffNewId' );
if ( isBlacklistPage && !isDiff ) {
$( '#ca-edit, #ca-ve-edit' ).hide();
if ( isViewAction ) {
await loadBlacklist();
blacklistStore.subscribe( () => {
updateEntryCount();
renderBlacklistTable();
} );
addBlacklistPageButtons();
}
}
if ( prefs.showInlineButtons ) {
mw.hook( 'wikipage.content' ).add( addInlineBlacklistButtons );
}
} catch ( error ) {
mw.log.error( 'AudioBot Blacklist gadget: failed', error );
}
};
init();
}() );
00dk55mkgf6r5lm0fgfxlj7b254k1ff
88981
88980
2026-08-06T23:24:23Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-AudioBotBlacklist.js]]
88980
javascript
text/javascript
/**
* AudioBot Blacklist Gadget
*
* Adds a button on the blacklist page to add audio files without editing.
* The blacklist is stored as JSON at [[Wiktionary:Approved Lingua Libre users/AudioBotBlacklist]].
*/
/* jshint esversion: 8 */
( function () {
'use strict';
const BLACKLIST_PAGE = 'Wiktionary:Approved Lingua Libre users/AudioBotBlacklist';
let AddDialog = null;
let EditDialog = null;
let BulkAddDialog = null;
const createStore = ( initial ) => {
let data = initial;
let subscribers = [];
const notify = () => subscribers.forEach( ( fn ) => fn( data ) );
return {
get: () => data,
set: ( value ) => { data = value; notify(); },
subscribe: ( fn ) => {
subscribers.push( fn );
return () => { subscribers = subscribers.filter( ( f ) => f !== fn ); };
},
has: ( key ) => data.hasOwnProperty( key ),
put: ( key, value ) => { data[ key ] = value; notify(); },
remove: ( key ) => { delete data[ key ]; notify(); },
entries: () => Object.entries( data ).sort( sortByAdded ),
count: () => Object.keys( data ).length
};
};
const blacklistStore = createStore( {} );
let blacklistLoaded = false;
let blacklistBaseRevId = null;
const normalizeFilename = ( name ) => name.trim().replace( /^File:/i, '' ).replace( /_/g, ' ' ).trim();
const getEntryReason = ( val ) => ( val && val.reason !== undefined ? val.reason : '' );
const getEntryAdded = ( val ) => ( val && val.added );
const makeEntry = ( reason, added ) => ( added ? { reason, added } : { reason } );
const sortByAdded = ( a, b ) => {
const addedA = getEntryAdded( a[ 1 ] ) || '\u0000';
const addedB = getEntryAdded( b[ 1 ] ) || '\u0000';
return addedB.localeCompare( addedA );
};
let api = null;
let commonsApi = null;
const loadBlacklist = async ( forceReload ) => {
if ( blacklistLoaded && !forceReload ) {
return;
}
const response = await api.get( {
action: 'query',
titles: BLACKLIST_PAGE,
prop: 'revisions',
rvprop: 'content|ids',
rvslots: 'main',
formatversion: 2
} );
const page = response.query.pages[ 0 ];
if ( page.missing ) {
blacklistStore.set( {} );
blacklistBaseRevId = null;
} else {
blacklistStore.set( JSON.parse( page.revisions[ 0 ].slots.main.content ) );
blacklistBaseRevId = page.revisions[ 0 ].revid;
}
blacklistLoaded = true;
};
const saveBlacklist = async ( summary ) => {
const data = blacklistStore.get();
const sortedBlacklist = Object.entries( data ).sort( sortByAdded ).reduce( ( acc, entry ) => {
acc[ entry[ 0 ] ] = entry[ 1 ];
return acc;
}, {} );
const params = {
action: 'edit',
title: BLACKLIST_PAGE,
text: JSON.stringify( sortedBlacklist, null, 2 ),
summary,
minor: false
};
if ( blacklistBaseRevId ) {
params.baserevid = blacklistBaseRevId;
}
const result = await api.postWithToken( 'csrf', params );
if ( result.edit && result.edit.newrevid ) {
blacklistBaseRevId = result.edit.newrevid;
}
};
const checkFileExists = async ( filename ) => {
const data = await commonsApi.get( {
action: 'query',
titles: 'File:' + filename,
formatversion: 2
} );
const page = data.query.pages[ 0 ];
return !page.missing;
};
const addToBlacklist = async ( filename, reason ) => {
await loadBlacklist( true );
if ( blacklistStore.has( filename ) ) {
throw new Error( 'This file is already in the blacklist.' );
}
const added = new Date().toISOString();
blacklistStore.put( filename, makeEntry( reason, added ) );
await saveBlacklist( `Added [[File:${filename}]] to blacklist: ${reason.substring( 0, 100 )}` );
};
const addBulkToBlacklist = async ( entries ) => {
await loadBlacklist( true );
let added = 0;
const data = blacklistStore.get();
const addedAt = new Date().toISOString();
for ( const [ filename, reason ] of entries ) {
if ( !data.hasOwnProperty( filename ) ) {
data[ filename ] = makeEntry( reason, addedAt );
added++;
}
}
if ( added > 0 ) {
blacklistStore.set( data );
await saveBlacklist( `Bulk added ${added} file(s) to blacklist` );
}
return added;
};
const editBlacklistEntry = async ( filename, newReason ) => {
await loadBlacklist( true );
if ( !blacklistStore.has( filename ) ) {
throw new Error( 'This file is not in the blacklist.' );
}
const existingAdded = getEntryAdded( blacklistStore.get()[ filename ] );
blacklistStore.put( filename, makeEntry( newReason, existingAdded ) );
await saveBlacklist( `Updated reason for [[File:${filename}]]` );
};
const removeFromBlacklist = async ( filename ) => {
await loadBlacklist( true );
if ( !blacklistStore.has( filename ) ) {
throw new Error( 'This file is not in the blacklist.' );
}
blacklistStore.remove( filename );
await saveBlacklist( `Removed [[File:${filename}]] from blacklist` );
};
const removeBulkFromBlacklist = async ( filenames ) => {
await loadBlacklist( true );
let removed = 0;
const data = blacklistStore.get();
for ( const filename of filenames ) {
if ( data.hasOwnProperty( filename ) ) {
delete data[ filename ];
removed++;
}
}
if ( removed > 0 ) {
blacklistStore.set( data );
await saveBlacklist( `Bulk removed ${removed} file(s) from blacklist` );
}
return removed;
};
const createDialog = ( config ) => {
class Dialog extends OO.ui.ProcessDialog {
initialize() {
super.initialize();
config.init( this );
return this;
}
getSetupProcess( data ) {
return super.getSetupProcess( data ).next( () => config.setup( this, data || {} ) );
}
getActionProcess( action ) {
if ( action === 'save' ) {
return new OO.ui.Process( async () => {
try {
await config.onSave( this );
this.close( { action } );
} catch ( error ) {
throw new OO.ui.Error( error.message || String( error ) );
}
} );
}
return super.getActionProcess( action );
}
getBodyHeight() {
return config.bodyHeight || 200;
}
}
Dialog.static = Object.assign( {}, OO.ui.ProcessDialog.static, {
name: config.name,
title: config.title,
actions: [
{ action: 'save', label: config.saveLabel || 'Save', flags: [ 'primary', 'progressive' ] },
{ label: 'Cancel', flags: 'safe' }
]
} );
return Dialog;
};
const createAddDialog = () => createDialog( {
name: 'audiobotAdd',
title: 'Add to AudioBot Blacklist',
saveLabel: 'Add to Blacklist',
bodyHeight: 250,
init( dialog ) {
dialog.filenameInput = new OO.ui.TextInputWidget( { placeholder: 'e.g. En-us-example.ogg' } );
dialog.reasonInput = new OO.ui.MultilineTextInputWidget( { placeholder: 'Reason for blacklisting...', rows: 3 } );
dialog.fileExistsMessage = new OO.ui.MessageWidget( { type: 'warning', inline: true, label: 'This file does not exist on Commons.' } );
dialog.fileExistsMessage.$element.hide();
dialog.filenameInput.on( 'change', OO.ui.debounce( async () => {
const filename = normalizeFilename( dialog.filenameInput.getValue() );
if ( filename ) {
const exists = await checkFileExists( filename );
dialog.fileExistsMessage.$element.toggle( !exists );
} else {
dialog.fileExistsMessage.$element.hide();
}
}, 500 ) );
const content = new OO.ui.PanelLayout( { padded: true, expanded: false } );
content.$element.append(
new OO.ui.FieldLayout( dialog.filenameInput, { label: 'Audio filename (without File: prefix):', align: 'top' } ).$element,
dialog.fileExistsMessage.$element,
new OO.ui.FieldLayout( dialog.reasonInput, { label: 'Reason:', align: 'top' } ).$element
);
dialog.$body.append( content.$element );
},
setup( dialog, data ) {
dialog.filenameInput.setValue( data.prefillFilename || '' );
dialog.reasonInput.setValue( '' );
dialog.fileExistsMessage.$element.hide();
if ( data.prefillFilename ) {
dialog.filenameInput.emit( 'change' );
}
},
async onSave( dialog ) {
const filename = normalizeFilename( dialog.filenameInput.getValue() );
const reason = dialog.reasonInput.getValue().trim();
if ( !filename ) { throw new Error( 'Please enter a filename.' ); }
if ( !reason ) { throw new Error( 'Please enter a reason.' ); }
await addToBlacklist( filename, reason );
mw.notify( `Successfully added "${filename}" to the blacklist.`, { type: 'success' } );
}
} );
const createEditDialog = () => createDialog( {
name: 'audiobotEdit',
title: 'Edit Blacklist Reason',
saveLabel: 'Save',
bodyHeight: 180,
init( dialog ) {
dialog.filename = '';
dialog.filenameLabel = new OO.ui.LabelWidget( { label: '' } );
dialog.reasonInput = new OO.ui.MultilineTextInputWidget( { placeholder: 'Reason for blacklisting...', rows: 3 } );
const content = new OO.ui.PanelLayout( { padded: true, expanded: false } );
content.$element.append(
new OO.ui.FieldLayout( dialog.filenameLabel, { label: 'Filename:', align: 'top' } ).$element,
new OO.ui.FieldLayout( dialog.reasonInput, { label: 'Reason:', align: 'top' } ).$element
);
dialog.$body.append( content.$element );
},
setup( dialog, data ) {
dialog.filename = data.filename || '';
dialog.filenameLabel.setLabel( dialog.filename );
dialog.reasonInput.setValue( data.reason || '' );
},
async onSave( dialog ) {
const reason = dialog.reasonInput.getValue().trim();
if ( !reason ) { throw new Error( 'Please enter a reason.' ); }
await editBlacklistEntry( dialog.filename, reason );
mw.notify( `Successfully updated reason for "${dialog.filename}".`, { type: 'success' } );
}
} );
const createBulkAddDialog = () => createDialog( {
name: 'audiobotBulkAdd',
title: 'Bulk Add to AudioBot Blacklist',
saveLabel: 'Add All',
bodyHeight: 320,
init( dialog ) {
dialog.filesInput = new OO.ui.MultilineTextInputWidget( { placeholder: 'Enter one filename per line...', rows: 6 } );
dialog.reasonInput = new OO.ui.MultilineTextInputWidget( { placeholder: 'Reason for blacklisting...', rows: 3 } );
const content = new OO.ui.PanelLayout( { padded: true, expanded: false } );
content.$element.append(
new OO.ui.FieldLayout( dialog.filesInput, { label: 'Filenames (one per line, without File: prefix):', align: 'top' } ).$element,
new OO.ui.FieldLayout( dialog.reasonInput, { label: 'Reason (applies to all):', align: 'top' } ).$element
);
dialog.$body.append( content.$element );
},
setup( dialog ) {
dialog.filesInput.setValue( '' );
dialog.reasonInput.setValue( '' );
},
async onSave( dialog ) {
const filesText = dialog.filesInput.getValue().trim();
const reason = dialog.reasonInput.getValue().trim();
if ( !filesText ) { throw new Error( 'Please enter at least one filename.' ); }
if ( !reason ) { throw new Error( 'Please enter a reason.' ); }
const filenames = filesText.split( '\n' ).map( normalizeFilename ).filter( ( n ) => n.length > 0 );
if ( filenames.length === 0 ) { throw new Error( 'Please enter at least one valid filename.' ); }
const entries = filenames.map( ( f ) => [ f, reason ] );
const added = await addBulkToBlacklist( entries );
mw.notify( `Successfully added ${added} file(s) to the blacklist.`, { type: 'success' } );
}
} );
let windowManager = null;
const showDialog = ( DialogClass, data ) => {
if ( !windowManager ) {
windowManager = new OO.ui.WindowManager();
$( document.body ).append( windowManager.$element );
}
const dialog = new DialogClass( { size: 'medium' } );
windowManager.addWindows( [ dialog ] );
windowManager.openWindow( dialog, data || {} );
};
let selectedFiles = new Set();
let $selectAllCheckbox = null;
let $removeSelectedBtn = null;
const CELL_STYLE = {
padding: '8px 12px',
borderBottom: '1px solid var(--border-color-base)',
verticalAlign: 'middle'
};
const cellCss = ( overrides ) => Object.assign( {}, CELL_STYLE, overrides );
const createIconButton = ( icon, title, destructive ) => new OO.ui.ButtonWidget( {
icon,
title,
framed: false,
flags: destructive ? [ 'destructive' ] : []
} );
const confirmAndRun = async ( message, action, successMsg ) => {
const confirmed = await OO.ui.confirm( message );
if ( !confirmed ) {
return false;
}
try {
const result = await action();
const msg = typeof successMsg === 'function' ? successMsg( result ) : successMsg;
mw.notify( msg, { type: 'success' } );
return true;
} catch ( error ) {
mw.notify( error.message || String( error ), { type: 'error' } );
return false;
}
};
const updateRemoveSelectedButton = () => {
if ( $removeSelectedBtn ) {
$removeSelectedBtn.setDisabled( selectedFiles.size === 0 );
$removeSelectedBtn.setLabel( `Remove selected (${selectedFiles.size})` );
}
};
const updateEntryCount = () => {
$( '#audiobot-entry-count' ).text( `${blacklistStore.count()} file(s) in blacklist` );
};
const renderBlacklistTable = () => {
if ( !$( '#audiobot-blacklist-controls' ).length ) {
return;
}
const entries = blacklistStore.entries();
updateEntryCount();
$( '.mw-json' ).hide();
$( '#audiobot-blacklist-table' ).remove();
selectedFiles.clear();
$selectAllCheckbox = new OO.ui.CheckboxInputWidget();
$selectAllCheckbox.on( 'change', ( selected ) => {
selectedFiles.clear();
$( '#audiobot-blacklist-table tbody tr' ).each( function () {
const $checkbox = $( this ).find( '.oo-ui-checkboxInputWidget input' );
$checkbox.prop( 'checked', selected ).trigger( 'change' );
if ( selected ) {
const filename = $( this ).data( 'filename' );
selectedFiles.add( filename );
}
} );
updateRemoveSelectedButton();
} );
const $table = $( '<table>' )
.attr( 'id', 'audiobot-blacklist-table' )
.css( { width: '100%', borderCollapse: 'collapse', marginTop: '1em' } );
const formatAdded = ( iso ) => {
if ( !iso ) { return ''; }
const d = new Date( iso );
if ( isNaN( d.getTime() ) ) { return iso; }
return d.toLocaleDateString( 'en-GB', { year: 'numeric', month: '2-digit', day: '2-digit' } );
};
const $thead = $( '<thead>' ).append(
$( '<tr>' ).css( { backgroundColor: 'var(--background-color-interactive)' } ).append(
$( '<th>' ).css( cellCss( { width: '40px', textAlign: 'center' } ) ).append( $selectAllCheckbox.$element ),
$( '<th>' ).text( 'Filename' ).css( cellCss( { textAlign: 'left' } ) ),
$( '<th>' ).text( 'Reason' ).css( cellCss( { textAlign: 'left' } ) ),
$( '<th>' ).text( 'Added' ).css( cellCss( { width: '100px', textAlign: 'left' } ) ),
$( '<th>' ).text( 'Actions' ).css( cellCss( { width: '100px', textAlign: 'center' } ) )
)
);
const $tbody = $( '<tbody>' );
for ( const [ filename, rawValue ] of entries ) {
const reason = getEntryReason( rawValue );
const added = getEntryAdded( rawValue );
const checkbox = new OO.ui.CheckboxInputWidget();
checkbox.on( 'change', ( selected ) => {
if ( selected ) {
selectedFiles.add( filename );
} else {
selectedFiles.delete( filename );
}
updateRemoveSelectedButton();
} );
const editBtn = createIconButton( 'edit', 'Edit reason', false );
editBtn.on( 'click', () => showDialog( EditDialog, { filename, reason } ) );
const removeBtn = createIconButton( 'trash', 'Remove from blacklist', true );
removeBtn.on( 'click', () => confirmAndRun(
`Remove "${filename}" from the blacklist?`,
() => removeFromBlacklist( filename ),
`Removed "${filename}" from the blacklist.`
) );
const $row = $( '<tr>' ).data( 'filename', filename ).append(
$( '<td>' ).css( cellCss( { textAlign: 'center' } ) ).append( checkbox.$element ),
$( '<td>' ).css( CELL_STYLE ).text( filename ),
$( '<td>' ).css( CELL_STYLE ).text( reason ),
$( '<td>' ).css( CELL_STYLE ).attr( 'title', added || '' ).text( formatAdded( added ) ),
$( '<td>' ).css( cellCss( { textAlign: 'center', whiteSpace: 'nowrap' } ) ).append( editBtn.$element, removeBtn.$element )
);
$tbody.append( $row );
}
$table.append( $thead, $tbody );
$( '#audiobot-blacklist-controls' ).after( $table );
};
const addBlacklistPageButtons = () => {
const addButton = new OO.ui.ButtonWidget( {
label: 'Add file',
icon: 'add',
flags: [ 'progressive' ]
} );
addButton.on( 'click', () => showDialog( AddDialog ) );
const bulkAddButton = new OO.ui.ButtonWidget( {
label: 'Bulk add',
icon: 'add'
} );
bulkAddButton.on( 'click', () => showDialog( BulkAddDialog ) );
$removeSelectedBtn = new OO.ui.ButtonWidget( {
label: 'Remove selected (0)',
icon: 'trash',
flags: [ 'destructive' ],
disabled: true
} );
$removeSelectedBtn.on( 'click', () => {
if ( selectedFiles.size === 0 ) {
return;
}
confirmAndRun(
`Remove ${selectedFiles.size} file(s) from the blacklist?`,
() => removeBulkFromBlacklist( Array.from( selectedFiles ) ),
( removed ) => `Removed ${removed} file(s) from the blacklist.`
);
} );
const $label = $( '<span>' )
.text( 'AudioBot Blacklist: ' )
.css( 'font-weight', 'bold' );
const $entryCount = $( '<span>' )
.attr( 'id', 'audiobot-entry-count' )
.css( { marginLeft: '1em', fontStyle: 'italic' } )
.text( 'Loading...' );
const $controls = $( '<div>' )
.attr( 'id', 'audiobot-blacklist-controls' )
.css( {
marginBottom: '1em',
padding: '0.5em 1em',
backgroundColor: 'var(--background-color-neutral-subtle)',
border: '1px solid var(--border-color-subtle)',
borderRadius: '2px',
color: 'var(--color-base)'
} )
.append(
$label,
addButton.$element,
' ',
bulkAddButton.$element,
' ',
$removeSelectedBtn.$element,
$entryCount
);
$( '#mw-content-text' ).prepend( $controls );
renderBlacklistTable();
};
const addInlineBlacklistButtons = ( $content ) => {
const $root = ( $content && $content.length ) ? $content : $( document );
for ( const table of $root.find( 'table.audiotable' ).toArray() ) {
const $table = $( table );
const $player = $table.find( '[data-mwtitle]' );
if ( !$player.length ) {
continue;
}
const filename = $player.attr( 'data-mwtitle' );
if ( !filename || $table.data( 'blacklistAdded' ) ) {
continue;
}
$table.data( 'blacklistAdded', true );
const btn = createIconButton( 'block', 'Add to AudioBot blacklist', true );
btn.on( 'click', () => showDialog( AddDialog, { prefillFilename: normalizeFilename( filename ) } ) );
const $actionsCell = $( '<td>' )
.addClass( 'audio-actions' )
.css( { verticalAlign: 'middle' } )
.append( btn.$element );
$table.find( 'tr' ).first().append( $actionsCell );
}
};
const init = async () => {
try {
await mw.loader.using( [
'oojs-ui-core',
'oojs-ui-widgets',
'oojs-ui-windows',
'oojs-ui.styles.icons-moderation',
'oojs-ui.styles.icons-editing-core',
'oojs-ui.styles.icons-interactions',
'mediawiki.api',
'mediawiki.ForeignApi'
] );
api = new mw.Api();
commonsApi = new mw.ForeignApi( 'https://commons.wikimedia.org/w/api.php' );
const userGroups = mw.config.get( 'wgUserGroups' ) || [];
if ( !userGroups.includes( 'autoconfirmed' ) ) {
return;
}
const prefs = mw.wiktGadgetPrefs.get( 'audiobot-blacklist', {
label: {
en: 'AudioBot Blacklist'
}
}, {
showInlineButtons: {
type: 'boolean',
default: false,
label: {
en: 'Show inline blacklist buttons next to audio files'
}
}
} );
AddDialog = createAddDialog();
EditDialog = createEditDialog();
BulkAddDialog = createBulkAddDialog();
const isBlacklistPage = mw.config.get( 'wgPageName' ) === BLACKLIST_PAGE.replace( / /g, '_' );
const isViewAction = mw.config.get( 'wgAction' ) === 'view';
const isDiff = mw.config.get( 'wgDiffOldId' ) || mw.config.get( 'wgDiffNewId' );
if ( isBlacklistPage && !isDiff ) {
$( '#ca-edit, #ca-ve-edit' ).hide();
if ( isViewAction ) {
await loadBlacklist();
blacklistStore.subscribe( () => {
updateEntryCount();
renderBlacklistTable();
} );
addBlacklistPageButtons();
}
}
if ( prefs.showInlineButtons ) {
mw.hook( 'wikipage.content' ).add( addInlineBlacklistButtons );
}
} catch ( error ) {
mw.log.error( 'AudioBot Blacklist gadget: failed', error );
}
};
init();
}() );
00dk55mkgf6r5lm0fgfxlj7b254k1ff
MediaWiki:Gadget-OrangeLinks.js
8
17990
88982
2025-12-06T18:34:13Z
en>Erutuon
0
make hook callback more versatile, handling $(document) or $(element) handed to wikipage.content hook, whether element is .mw-parser-content or parent or child of it; and don't makeLinksOrange in loop body
88982
javascript
text/javascript
// <nowiki>
function HTML_unescape(text) {
return new DOMParser().parseFromString(text, "text/html").body.textContent;
}
mw.util.addCSS(`
.orange-link {
color: var(--wikt-palette-gold);
}
.orange-link:visited {
color: var(--wikt-palette-dullgold);
}
.orange-link:hover, .orange-link:visited:hover {
color: var(--wikt-palette-honey);
}
`);
const actionAPI = new mw.Api({ajax: {headers: {"Api-User-Agent": "Gadget developed by [[User:Ioaxxere]]"}}});
// Maps each page title to a list of IDs.
const pageIDsOf = new Map();
let count = 0;
const linkTitles = new Set();
class OrangeLinker {
constructor() {
this.pagesToProcess = [];
this.linksToProcess = [];
}
static getTitleAndAnchor(link) {
const linkTitle =
link.pathname.startsWith("/wiki/")
? decodeURIComponent(link.pathname.split("/wiki/")[1])
: new URLSearchParams(link.search).get("title");
const linkAnchor = decodeURIComponent(link.hash.slice(1) || "");
return [linkTitle, linkAnchor];
}
addLink(link) {
// Check whether the linked page is in the right domain
// and whether the path starts with [[mw:Manual:$wgArticlePath]]
// or goes to [[mw:Manual:$wgScript]]
// and whether it exists and has not already been processed by makeLinksOrange.
if (!(link.hostname === "en.wiktionary.org"
&& (link.pathname.startsWith("/wiki/")
|| link.pathname === "/" /* This covers links without / after en.wiktionary.org */
|| link.pathname === "/w"
|| link.pathname === "/w/" || link.pathname === "/w/index.php")
&& !link.matches(".orange-link, .not-orange-link, .new")))
return;
const [linkTitle, linkAnchor] = OrangeLinker.getTitleAndAnchor(link);
if (!(linkTitle && linkAnchor) || /^[a-z]/.test(linkAnchor)
// Only handle links to main, Appendix, Reconstruction namespaces.
|| ![0, 100, 118].includes(new mw.Title(linkTitle).namespace))
return;
if (!linkTitles.has(linkTitle)) {
this.pagesToProcess.push(linkTitle);
linkTitles.add(linkTitle);
}
this.linksToProcess.push(link);
}
addLinksInElement(element) {
for (const link of element.querySelectorAll("a")) {
this.addLink(link);
}
}
async makeLinksOrange() {
if (this.linksToProcess.length === 0) return;
// Process links in small enough chunks that [[Module:get IDs]]
// doesn't hit a timeout error.
// There should be no duplicates because we check linkTitles before
// adding a link.
const queries = [];
for (let i = 0; i < this.pagesToProcess.length; i += 100) {
const pageTitles = this.pagesToProcess.slice(i, i + 100);
// If there are no titles to query, create an empty set of promises
// and immediately start oranging links.
if (pageTitles.length === 0) break;
count++;
console.info("OrangeLinks request #" + count + " for " + pageTitles.join(", "));
// Query the IDs for all the pages using [[Module:get IDs]].
const params = {
action: "expandtemplates",
format: "json",
prop: "wikitext",
text: `{{#invoke:get IDs|show|${pageTitles.join("|")}}}`,
};
queries.push(actionAPI.post(params).then(response => {
// Integrate the results into `pageIDsOf`.
const pageIDs = HTML_unescape(response.expandtemplates.wikitext).split("\n\n");
for (let j = 0; j < pageTitles.length; j++)
pageIDsOf.set(pageTitles[j], pageIDs[j].split(" "));
}));
}
// After all the queries have returned, determine whether each link needs to be orange.
await Promise.all(queries);
for (const link of this.linksToProcess) {
const [linkTitle, linkAnchor] = OrangeLinker.getTitleAndAnchor(link);
const existingAnchors = pageIDsOf.get(linkTitle);
const anchorExists = existingAnchors instanceof Array && existingAnchors.includes(linkAnchor);
link.classList.add(anchorExists ? "not-orange-link" : "orange-link");
}
this.pagesToProcess = [];
this.linksToProcess = [];
}
}
// Activate the gadget.
mw.hook("wikipage.content").add($content => {
var orangeLinker = new OrangeLinker();
for (const element of $content) {
if (!(element instanceof Element || element instanceof Document)) continue;
const parserOutput = element.querySelector(".mw-parser-output");
if (parserOutput) {
orangeLinker.addLinksInElement(parserOutput);
} else if (element.matches(".mw-parser-output, .mw-parser-output *")) {
if (element.tagName === "A") {
orangeLinker.addLink(element);
} else {
orangeLinker.addLinksInElement(element);
}
}
}
orangeLinker.makeLinksOrange();
});
// Create a global hook in case any other gadget or script would like to activate it.
window.OrangeLinker = OrangeLinker;
// </nowiki>
15b9o6bccse18zbj9bz81anab6u3dq5
88983
88982
2026-08-06T23:24:32Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-OrangeLinks.js]]
88982
javascript
text/javascript
// <nowiki>
function HTML_unescape(text) {
return new DOMParser().parseFromString(text, "text/html").body.textContent;
}
mw.util.addCSS(`
.orange-link {
color: var(--wikt-palette-gold);
}
.orange-link:visited {
color: var(--wikt-palette-dullgold);
}
.orange-link:hover, .orange-link:visited:hover {
color: var(--wikt-palette-honey);
}
`);
const actionAPI = new mw.Api({ajax: {headers: {"Api-User-Agent": "Gadget developed by [[User:Ioaxxere]]"}}});
// Maps each page title to a list of IDs.
const pageIDsOf = new Map();
let count = 0;
const linkTitles = new Set();
class OrangeLinker {
constructor() {
this.pagesToProcess = [];
this.linksToProcess = [];
}
static getTitleAndAnchor(link) {
const linkTitle =
link.pathname.startsWith("/wiki/")
? decodeURIComponent(link.pathname.split("/wiki/")[1])
: new URLSearchParams(link.search).get("title");
const linkAnchor = decodeURIComponent(link.hash.slice(1) || "");
return [linkTitle, linkAnchor];
}
addLink(link) {
// Check whether the linked page is in the right domain
// and whether the path starts with [[mw:Manual:$wgArticlePath]]
// or goes to [[mw:Manual:$wgScript]]
// and whether it exists and has not already been processed by makeLinksOrange.
if (!(link.hostname === "en.wiktionary.org"
&& (link.pathname.startsWith("/wiki/")
|| link.pathname === "/" /* This covers links without / after en.wiktionary.org */
|| link.pathname === "/w"
|| link.pathname === "/w/" || link.pathname === "/w/index.php")
&& !link.matches(".orange-link, .not-orange-link, .new")))
return;
const [linkTitle, linkAnchor] = OrangeLinker.getTitleAndAnchor(link);
if (!(linkTitle && linkAnchor) || /^[a-z]/.test(linkAnchor)
// Only handle links to main, Appendix, Reconstruction namespaces.
|| ![0, 100, 118].includes(new mw.Title(linkTitle).namespace))
return;
if (!linkTitles.has(linkTitle)) {
this.pagesToProcess.push(linkTitle);
linkTitles.add(linkTitle);
}
this.linksToProcess.push(link);
}
addLinksInElement(element) {
for (const link of element.querySelectorAll("a")) {
this.addLink(link);
}
}
async makeLinksOrange() {
if (this.linksToProcess.length === 0) return;
// Process links in small enough chunks that [[Module:get IDs]]
// doesn't hit a timeout error.
// There should be no duplicates because we check linkTitles before
// adding a link.
const queries = [];
for (let i = 0; i < this.pagesToProcess.length; i += 100) {
const pageTitles = this.pagesToProcess.slice(i, i + 100);
// If there are no titles to query, create an empty set of promises
// and immediately start oranging links.
if (pageTitles.length === 0) break;
count++;
console.info("OrangeLinks request #" + count + " for " + pageTitles.join(", "));
// Query the IDs for all the pages using [[Module:get IDs]].
const params = {
action: "expandtemplates",
format: "json",
prop: "wikitext",
text: `{{#invoke:get IDs|show|${pageTitles.join("|")}}}`,
};
queries.push(actionAPI.post(params).then(response => {
// Integrate the results into `pageIDsOf`.
const pageIDs = HTML_unescape(response.expandtemplates.wikitext).split("\n\n");
for (let j = 0; j < pageTitles.length; j++)
pageIDsOf.set(pageTitles[j], pageIDs[j].split(" "));
}));
}
// After all the queries have returned, determine whether each link needs to be orange.
await Promise.all(queries);
for (const link of this.linksToProcess) {
const [linkTitle, linkAnchor] = OrangeLinker.getTitleAndAnchor(link);
const existingAnchors = pageIDsOf.get(linkTitle);
const anchorExists = existingAnchors instanceof Array && existingAnchors.includes(linkAnchor);
link.classList.add(anchorExists ? "not-orange-link" : "orange-link");
}
this.pagesToProcess = [];
this.linksToProcess = [];
}
}
// Activate the gadget.
mw.hook("wikipage.content").add($content => {
var orangeLinker = new OrangeLinker();
for (const element of $content) {
if (!(element instanceof Element || element instanceof Document)) continue;
const parserOutput = element.querySelector(".mw-parser-output");
if (parserOutput) {
orangeLinker.addLinksInElement(parserOutput);
} else if (element.matches(".mw-parser-output, .mw-parser-output *")) {
if (element.tagName === "A") {
orangeLinker.addLink(element);
} else {
orangeLinker.addLinksInElement(element);
}
}
}
orangeLinker.makeLinksOrange();
});
// Create a global hook in case any other gadget or script would like to activate it.
window.OrangeLinker = OrangeLinker;
// </nowiki>
15b9o6bccse18zbj9bz81anab6u3dq5
MediaWiki:Gadget-U2693.js
8
17991
88984
2025-09-17T22:39:01Z
en>Ioaxxere
0
[[mw:Migrating mw.Uri to URL]]
88984
javascript
text/javascript
/* jshint undef:true, latedef:true, shadow:true, boss:true, scripturl:true */
/* global mw, jQuery */
// <nowiki>
"use strict";
mw.loader.using(["mediawiki.util"]).then(() =>
$(function() {
var curRevisionId = mw.config.get("wgCurRevisionId");
function createAnchorLink(label, href, wikilink, title) {
var span = document.createElement("span");
var link = document.createElement("a");
link.href = href;
link.addEventListener(
"click",
function(ev) {
ev.preventDefault();
// IE
if (window.clipboardData && window.clipboardData.setData) {
window.clipboardData.setData("text", wikilink);
mw.util.jsMessage("Wikitext copied to the clipboard.");
return;
}
// XXX: there is a brand new shiny ClipboardEvent API, but I could not get it to work on my browser.
// plus, there is no graceful fallback if it fails to work. going back to good ol' prompt().
prompt("Hit Ctrl-C to copy this to your clipboard", wikilink);
},
false
);
link.appendChild(document.createTextNode(label));
link.title = title;
span.appendChild(document.createTextNode("["));
span.appendChild(link);
span.appendChild(document.createTextNode("]"));
span.className = "mw-editsection-like anchor-gadget-link";
return span;
}
// XXX: not ideal; it may over-unescape things, but it usually works as intended
function unescapeAnchor(anchor) {
anchor = anchor.replace(/_/g, " ");
try {
return decodeURIComponent(
anchor.replace(/\.([0-9A-F][0-9A-F])/g, "%$1")
);
} catch (e) {
return anchor;
}
}
function forEachHeader(callback) {
var headers = document.querySelectorAll(
".mw-heading > h2, .mw-heading > h3, .mw-heading > h4, .mw-heading > h5, .mw-heading > h6, .mw-headline"
);
for (var i = 0; i < headers.length; ++i) {
var links = headers[i].parentNode.getElementsByTagName("a");
var title = null;
for (var j = 0; j < links.length; ++j) {
try {
var u = new URL(links[j].href);
if (
u.hostname === location.host &&
u.pathname === mw.config.get("wgScript") &&
u.searchParams.get("action") === "edit" &&
!u.searchParams.get("redlink")
) {
title = new mw.Title(u.searchParams.get("title"));
}
} catch (e) {
/* screw it */
}
}
if (!title) continue;
callback(headers[i], title);
}
}
function getRevInfo(oldid, callback) {
// XXX: of course I have it right on the page, but I cannot rely on it being in any particular format.
// hence wasting bandwidth on an API request.
mw.loader.using(["mediawiki.api", "mediawiki.util"], function() {
var api = new mw.Api();
api
.get({
action: "query",
pageids: mw.config.get("wgArticleId"),
prop: "revisions",
rvprop: "user|timestamp",
rvstartid: oldid,
rvlimit: 1,
})
.then(function(result) {
var revobj =
result.query.pages[mw.config.get("wgArticleId")].revisions[0];
callback(revobj);
});
});
}
function createUnsignedLink(revobj) {
var userpart =
(mw.util.isIPv4Address(revobj.user) ||
mw.util.isIPv6Address(revobj.user) ?
"{{unsigned-ip|" :
"{{unsigned|") + revobj.user;
var datepart = "}}";
var m;
if (
(m = /^(\d+)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z$/.exec(
revobj.timestamp
))
) {
var monthNames = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
datepart =
"|" +
m[4] +
":" +
m[5] +
", " +
m[3].replace(/^0+/, "") +
" " +
monthNames[parseInt(m[2], 10) - 1] +
" " +
m[1] +
" (UTC)}}";
}
return createAnchorLink(
"✎",
"javascript:void window.warranty",
userpart + datepart,
"{{unsigned}}"
);
}
if (mw.config.get("wgAction") !== "view") return;
mw.util.addCSS(
".anchor-gadget-link { -moz-user-select: none; -webkit-user-select: none; -ms-user-select: none; user-select: none; }"
);
if (mw.util.getParamValue("diff")) {
// viewing a diff
var oldid, newid;
var ot = document.getElementById("mw-diff-otitle1");
var nt = document.getElementById("mw-diff-ntitle1");
if (ot) {
ot = new URL(ot.getElementsByTagName("a")[0].href);
oldid = ot.searchParams.get("oldid");
}
if (nt) {
nt = new URL(nt.getElementsByTagName("a")[0].href);
newid = nt.searchParams.get("oldid");
}
document
.getElementById("firstHeading")
.appendChild(
createAnchorLink(
"⚓",
mw.config.get("wgScript") + "?oldid=" + oldid + "&diff=" + newid,
"[[Special:Diff/" + oldid + "/" + newid + "]]",
"Link to this diff"
)
);
if (
document.getElementsByClassName("diffmulti").length === 0 &&
mw.config.get("wgArticleId")
) {
getRevInfo(newid, function(revobj) {
document
.getElementById("firstHeading")
.appendChild(createUnsignedLink(revobj));
});
}
} else if (mw.util.getParamValue("oldid") || curRevisionId) {
// viewing an old page revision
var oldid = mw.util.getParamValue("oldid") || curRevisionId;
document
.getElementById("firstHeading")
.appendChild(
createAnchorLink(
"⚓",
mw.config.get("wgScript") + "?oldid=" + oldid,
"[[Special:Permalink/" + oldid + "]]",
"Link to this revision"
)
);
getRevInfo(oldid, function(revobj) {
document
.getElementById("firstHeading")
.appendChild(createUnsignedLink(revobj));
});
}
if (
mw.config.get("wgRevisionId") === curRevisionId &&
// 100 = appendix, 114 = citations
[0, 100, 114].indexOf(mw.config.get("wgNamespaceNumber")) === -1
) {
forEachHeader(function(header, title) {
var anchorLink = createAnchorLink(
"⚓",
mw.util.getUrl(title.getPrefixedDb()) + "#" + header.id,
"[[" +
title.getPrefixedText() +
"#" +
unescapeAnchor(header.id) +
"]]"
);
var editSectionLink =
header.parentNode.querySelector(".mw-editsection");
if (editSectionLink) {
header.parentNode.insertBefore(
anchorLink,
editSectionLink.nextSibling
);
} else {
header.parentNode.appendChild(anchorLink);
}
});
}
})
);
// </nowiki>
3r6xwafb4slilr2bjt7unh1dp2j1dru
88985
88984
2026-08-06T23:24:41Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-U2693.js]]
88984
javascript
text/javascript
/* jshint undef:true, latedef:true, shadow:true, boss:true, scripturl:true */
/* global mw, jQuery */
// <nowiki>
"use strict";
mw.loader.using(["mediawiki.util"]).then(() =>
$(function() {
var curRevisionId = mw.config.get("wgCurRevisionId");
function createAnchorLink(label, href, wikilink, title) {
var span = document.createElement("span");
var link = document.createElement("a");
link.href = href;
link.addEventListener(
"click",
function(ev) {
ev.preventDefault();
// IE
if (window.clipboardData && window.clipboardData.setData) {
window.clipboardData.setData("text", wikilink);
mw.util.jsMessage("Wikitext copied to the clipboard.");
return;
}
// XXX: there is a brand new shiny ClipboardEvent API, but I could not get it to work on my browser.
// plus, there is no graceful fallback if it fails to work. going back to good ol' prompt().
prompt("Hit Ctrl-C to copy this to your clipboard", wikilink);
},
false
);
link.appendChild(document.createTextNode(label));
link.title = title;
span.appendChild(document.createTextNode("["));
span.appendChild(link);
span.appendChild(document.createTextNode("]"));
span.className = "mw-editsection-like anchor-gadget-link";
return span;
}
// XXX: not ideal; it may over-unescape things, but it usually works as intended
function unescapeAnchor(anchor) {
anchor = anchor.replace(/_/g, " ");
try {
return decodeURIComponent(
anchor.replace(/\.([0-9A-F][0-9A-F])/g, "%$1")
);
} catch (e) {
return anchor;
}
}
function forEachHeader(callback) {
var headers = document.querySelectorAll(
".mw-heading > h2, .mw-heading > h3, .mw-heading > h4, .mw-heading > h5, .mw-heading > h6, .mw-headline"
);
for (var i = 0; i < headers.length; ++i) {
var links = headers[i].parentNode.getElementsByTagName("a");
var title = null;
for (var j = 0; j < links.length; ++j) {
try {
var u = new URL(links[j].href);
if (
u.hostname === location.host &&
u.pathname === mw.config.get("wgScript") &&
u.searchParams.get("action") === "edit" &&
!u.searchParams.get("redlink")
) {
title = new mw.Title(u.searchParams.get("title"));
}
} catch (e) {
/* screw it */
}
}
if (!title) continue;
callback(headers[i], title);
}
}
function getRevInfo(oldid, callback) {
// XXX: of course I have it right on the page, but I cannot rely on it being in any particular format.
// hence wasting bandwidth on an API request.
mw.loader.using(["mediawiki.api", "mediawiki.util"], function() {
var api = new mw.Api();
api
.get({
action: "query",
pageids: mw.config.get("wgArticleId"),
prop: "revisions",
rvprop: "user|timestamp",
rvstartid: oldid,
rvlimit: 1,
})
.then(function(result) {
var revobj =
result.query.pages[mw.config.get("wgArticleId")].revisions[0];
callback(revobj);
});
});
}
function createUnsignedLink(revobj) {
var userpart =
(mw.util.isIPv4Address(revobj.user) ||
mw.util.isIPv6Address(revobj.user) ?
"{{unsigned-ip|" :
"{{unsigned|") + revobj.user;
var datepart = "}}";
var m;
if (
(m = /^(\d+)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z$/.exec(
revobj.timestamp
))
) {
var monthNames = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
datepart =
"|" +
m[4] +
":" +
m[5] +
", " +
m[3].replace(/^0+/, "") +
" " +
monthNames[parseInt(m[2], 10) - 1] +
" " +
m[1] +
" (UTC)}}";
}
return createAnchorLink(
"✎",
"javascript:void window.warranty",
userpart + datepart,
"{{unsigned}}"
);
}
if (mw.config.get("wgAction") !== "view") return;
mw.util.addCSS(
".anchor-gadget-link { -moz-user-select: none; -webkit-user-select: none; -ms-user-select: none; user-select: none; }"
);
if (mw.util.getParamValue("diff")) {
// viewing a diff
var oldid, newid;
var ot = document.getElementById("mw-diff-otitle1");
var nt = document.getElementById("mw-diff-ntitle1");
if (ot) {
ot = new URL(ot.getElementsByTagName("a")[0].href);
oldid = ot.searchParams.get("oldid");
}
if (nt) {
nt = new URL(nt.getElementsByTagName("a")[0].href);
newid = nt.searchParams.get("oldid");
}
document
.getElementById("firstHeading")
.appendChild(
createAnchorLink(
"⚓",
mw.config.get("wgScript") + "?oldid=" + oldid + "&diff=" + newid,
"[[Special:Diff/" + oldid + "/" + newid + "]]",
"Link to this diff"
)
);
if (
document.getElementsByClassName("diffmulti").length === 0 &&
mw.config.get("wgArticleId")
) {
getRevInfo(newid, function(revobj) {
document
.getElementById("firstHeading")
.appendChild(createUnsignedLink(revobj));
});
}
} else if (mw.util.getParamValue("oldid") || curRevisionId) {
// viewing an old page revision
var oldid = mw.util.getParamValue("oldid") || curRevisionId;
document
.getElementById("firstHeading")
.appendChild(
createAnchorLink(
"⚓",
mw.config.get("wgScript") + "?oldid=" + oldid,
"[[Special:Permalink/" + oldid + "]]",
"Link to this revision"
)
);
getRevInfo(oldid, function(revobj) {
document
.getElementById("firstHeading")
.appendChild(createUnsignedLink(revobj));
});
}
if (
mw.config.get("wgRevisionId") === curRevisionId &&
// 100 = appendix, 114 = citations
[0, 100, 114].indexOf(mw.config.get("wgNamespaceNumber")) === -1
) {
forEachHeader(function(header, title) {
var anchorLink = createAnchorLink(
"⚓",
mw.util.getUrl(title.getPrefixedDb()) + "#" + header.id,
"[[" +
title.getPrefixedText() +
"#" +
unescapeAnchor(header.id) +
"]]"
);
var editSectionLink =
header.parentNode.querySelector(".mw-editsection");
if (editSectionLink) {
header.parentNode.insertBefore(
anchorLink,
editSectionLink.nextSibling
);
} else {
header.parentNode.appendChild(anchorLink);
}
});
}
})
);
// </nowiki>
3r6xwafb4slilr2bjt7unh1dp2j1dru
MediaWiki:Gadget-MoreMenu-local.js
8
17992
88986
2023-09-11T01:23:07Z
en>Erutuon
0
make it easier to load [[m:MoreMenu]]
88986
javascript
text/javascript
mw.loader.load('https://meta.wikimedia.org/w/load.php?modules=ext.gadget.MoreMenu');
4a8nztnsxwdkzojyvj6omx0u6wdapgy
88987
88986
2026-08-06T23:25:00Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-MoreMenu-local.js]]
88986
javascript
text/javascript
mw.loader.load('https://meta.wikimedia.org/w/load.php?modules=ext.gadget.MoreMenu');
4a8nztnsxwdkzojyvj6omx0u6wdapgy
MediaWiki:Gadget-categoryTreeLanguageNames.js
8
17993
88988
2024-10-13T06:15:16Z
en>Ioaxxere
0
convert to ES6 style
88988
javascript
text/javascript
"use strict";
// {{documentation}}
// <nowiki>
/* global mw */
(function categoryTreeLanguageNamesGadget() {
const ALLOWED_NAMESPACES = [
0, // Main
14, // Category
100, // Appendix
118 // Reconstruction
];
const CATEGORY_PREFIX = mw.config.get("wgFormattedNamespaces")[14] + ":";
function getLanguageCodeToCanonicalName() {
const CACHE_DURATION = 24 * 60 * 60; // 24 hours
const KEY = "enwiktLanguageCodeToCanonicalNameJson";
let timeNow = new Date().getTime() * 1e-3;
try {
let cachedData = JSON.parse(localStorage.getItem(KEY));
if (timeNow - cachedData.timestamp < CACHE_DURATION)
return Promise.resolve(cachedData.data);
} catch (e) { }
const actionAPI = new mw.Api({ ajax: { headers: { "Api-User-Agent": "Gadget developed by [[User:Surjection]]" } } });
return actionAPI.get({
"action": "parse",
"page": "Module:languages/code to canonical name.json",
"prop": "wikitext",
"formatversion": "2",
"format": "json"
})
.then(response => {
let languageData = JSON.parse(response.parse.wikitext);
localStorage.setItem(KEY, JSON.stringify({
timestamp: timeNow,
data: languageData
}));
return languageData;
});
}
function addCategoryTreeLanguageName(codeToCanonicalName, catElement, doNotTagLanguageCode) {
// not a category link/name
if (!catElement.href.includes(CATEGORY_PREFIX))
return;
// do not duplicate
if (catElement.querySelector(".wikt-category-tree-language-name"))
return;
let firstTextNode = Array.from(catElement.childNodes).find(node => node.nodeType === Node.TEXT_NODE);
if (!firstTextNode)
return;
let categoryName = firstTextNode.textContent;
let parseCategoryTreeName = categoryName.match(/^([a-z-]+):(.+)/);
if (!parseCategoryTreeName)
return;
let languageCode = parseCategoryTreeName[1];
let subcategoryName = parseCategoryTreeName[2];
let canonicalName = codeToCanonicalName[languageCode];
if (!canonicalName)
return;
if (catElement.title === categoryName || catElement.title === CATEGORY_PREFIX + categoryName)
catElement.title += ` [${canonicalName}: ${subcategoryName}]`;
if (languageCode !== doNotTagLanguageCode) {
let langElement = document.createElement("span");
langElement.className = "wikt-category-tree-language-name";
langElement.textContent = ` [${canonicalName}]`;
catElement.append(langElement);
}
}
mw.hook("wikipage.content").add(() => {
if (mw.config.get("wgAction") === "view" &&
ALLOWED_NAMESPACES.includes(mw.config.get("wgNamespaceNumber"))) {
mw.util.addCSS(`
.wikt-category-tree-language-name {
font-size: 85%;
}
`);
let thisLanguageCode = null;
if (mw.config.get("wgNamespaceNumber") === 14) {
let categoryName = mw.config.get("wgTitle");
let parseCategoryTreeName = categoryName.match(/^([a-z-]+):(.+)/);
if (parseCategoryTreeName)
thisLanguageCode = parseCategoryTreeName[1];
}
getLanguageCodeToCanonicalName().then(codeToCanonicalName => {
for (let catElement of document.querySelectorAll("#catlinks ul > li a"))
addCategoryTreeLanguageName(codeToCanonicalName, catElement, thisLanguageCode);
if (mw.config.get("wgNamespaceNumber") === 14) {
for (let catElement of document.querySelectorAll("#mw-subcategories a"))
addCategoryTreeLanguageName(codeToCanonicalName, catElement, thisLanguageCode);
}
});
}
});
})();
// </nowiki>
f17fgeickevfnz8qtgra6lmbjihrfci
88989
88988
2026-08-06T23:26:06Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-categoryTreeLanguageNames.js]]
88988
javascript
text/javascript
"use strict";
// {{documentation}}
// <nowiki>
/* global mw */
(function categoryTreeLanguageNamesGadget() {
const ALLOWED_NAMESPACES = [
0, // Main
14, // Category
100, // Appendix
118 // Reconstruction
];
const CATEGORY_PREFIX = mw.config.get("wgFormattedNamespaces")[14] + ":";
function getLanguageCodeToCanonicalName() {
const CACHE_DURATION = 24 * 60 * 60; // 24 hours
const KEY = "enwiktLanguageCodeToCanonicalNameJson";
let timeNow = new Date().getTime() * 1e-3;
try {
let cachedData = JSON.parse(localStorage.getItem(KEY));
if (timeNow - cachedData.timestamp < CACHE_DURATION)
return Promise.resolve(cachedData.data);
} catch (e) { }
const actionAPI = new mw.Api({ ajax: { headers: { "Api-User-Agent": "Gadget developed by [[User:Surjection]]" } } });
return actionAPI.get({
"action": "parse",
"page": "Module:languages/code to canonical name.json",
"prop": "wikitext",
"formatversion": "2",
"format": "json"
})
.then(response => {
let languageData = JSON.parse(response.parse.wikitext);
localStorage.setItem(KEY, JSON.stringify({
timestamp: timeNow,
data: languageData
}));
return languageData;
});
}
function addCategoryTreeLanguageName(codeToCanonicalName, catElement, doNotTagLanguageCode) {
// not a category link/name
if (!catElement.href.includes(CATEGORY_PREFIX))
return;
// do not duplicate
if (catElement.querySelector(".wikt-category-tree-language-name"))
return;
let firstTextNode = Array.from(catElement.childNodes).find(node => node.nodeType === Node.TEXT_NODE);
if (!firstTextNode)
return;
let categoryName = firstTextNode.textContent;
let parseCategoryTreeName = categoryName.match(/^([a-z-]+):(.+)/);
if (!parseCategoryTreeName)
return;
let languageCode = parseCategoryTreeName[1];
let subcategoryName = parseCategoryTreeName[2];
let canonicalName = codeToCanonicalName[languageCode];
if (!canonicalName)
return;
if (catElement.title === categoryName || catElement.title === CATEGORY_PREFIX + categoryName)
catElement.title += ` [${canonicalName}: ${subcategoryName}]`;
if (languageCode !== doNotTagLanguageCode) {
let langElement = document.createElement("span");
langElement.className = "wikt-category-tree-language-name";
langElement.textContent = ` [${canonicalName}]`;
catElement.append(langElement);
}
}
mw.hook("wikipage.content").add(() => {
if (mw.config.get("wgAction") === "view" &&
ALLOWED_NAMESPACES.includes(mw.config.get("wgNamespaceNumber"))) {
mw.util.addCSS(`
.wikt-category-tree-language-name {
font-size: 85%;
}
`);
let thisLanguageCode = null;
if (mw.config.get("wgNamespaceNumber") === 14) {
let categoryName = mw.config.get("wgTitle");
let parseCategoryTreeName = categoryName.match(/^([a-z-]+):(.+)/);
if (parseCategoryTreeName)
thisLanguageCode = parseCategoryTreeName[1];
}
getLanguageCodeToCanonicalName().then(codeToCanonicalName => {
for (let catElement of document.querySelectorAll("#catlinks ul > li a"))
addCategoryTreeLanguageName(codeToCanonicalName, catElement, thisLanguageCode);
if (mw.config.get("wgNamespaceNumber") === 14) {
for (let catElement of document.querySelectorAll("#mw-subcategories a"))
addCategoryTreeLanguageName(codeToCanonicalName, catElement, thisLanguageCode);
}
});
}
});
})();
// </nowiki>
f17fgeickevfnz8qtgra6lmbjihrfci
MediaWiki:Gadget-Streamline.js
8
17994
88990
2026-04-28T15:50:09Z
en>Surjection
0
88990
javascript
text/javascript
"use strict";
// {{documentation}}
// <nowiki>
/*jshint strict:true, undef:true, latedef:true, esversion:6 */
/* global mw, createNavToggle */
const STREAMLINE_SYMBOL = "⌰";
// [[MediaWiki:Gadget-WiktGadgetPrefs.js]]
const preferences = mw.wiktGadgetPrefs.get(
"streamline",
{
label: {
en: "Streamline",
},
},
{
collapseElements: {
"type": "strenum",
"default": "aboveDefinitions",
"label": {
"en": "Elements to collapse"
},
"choices": [
"aboveDefinitions",
"allExceptNotes"
],
"choiceLabels": {
"en": {
"aboveDefinitions": "Sections before definitions",
"allExceptNotes": "Sections before and after definitions"
}
}
},
runInReconstructions: {
"type": "boolean",
"default": true,
"label": {
"en": "Apply also to reconstruction pages"
}
}
}
);
function makeNavFrame(heading, editLinks, toggleCat, contents) {
// now make a collapsible box
const navHead = document.createElement("div");
navHead.className = "NavHead";
navHead.style.cursor = "pointer";
navHead.style.fontSize = "1.1em";
const navContent = document.createElement("div");
navContent.className = "NavContent boxcontent streamline-boxcontent";
navContent.style.textAlign = "left";
const navFrame = document.createElement("div");
navFrame.className = "NavFrame NavFrame-streamline";
if (toggleCat)
navFrame.setAttribute("data-toggle-category", toggleCat);
navFrame.style.display = "block";
navFrame.append(navHead, navContent);
navHead.append(`${STREAMLINE_SYMBOL} ${heading}`);
try {
if (createNavToggle) createNavToggle(navFrame);
} catch (e) { }
if (editLinks) {
navContent.append(editLinks);
editLinks.style.float = "right";
// try displaying the edit links next to the show button
editLinks.style.marginTop = "-1.5em";
editLinks.style.marginRight = "8ch";
}
navContent.append(...contents);
return navFrame;
}
function isNavFrameOpen(frame) {
const content = frame.querySelector(".NavContent");
if (!content) return false;
const computedStyle = window.getComputedStyle(content);
if (!computedStyle) return false;
return computedStyle.display !== "none";
}
function openNavFrame(frame) {
if (!isNavFrameOpen(frame)) {
const navHead = frame.querySelector(".NavHead");
if (navHead)
navHead.click();
}
}
function anchorInUrl(anchor) {
if (window.location.hash) {
return window.location.hash.replace(/^#/, "") === anchor;
}
return false;
}
function hasElementClass(element, className) {
return element.classList.contains(className);
}
function shouldEndCollapsible(element) {
return element.matches("h1, h2, h3, h4, h5, h6, hr") // end on heading or horizontal line
|| hasElementClass(element, "NavFrame-streamline") // no nested streamline navboxes
|| hasElementClass(element, "mw-heading") // section heading
|| element.matches("section") // Parsoid
; // this is on its own line so that it's easier to add new rules
}
function shouldExcludeFromCollapsible(element) {
return hasElementClass(element, "thumb") // images
|| hasElementClass(element, "sister-project") // sister project box
|| hasElementClass(element, "kanji-table") // ja-kanjitab
|| hasElementClass(element, "floatright") // various tables
|| hasElementClass(element, "template-anchor") // anchor
|| hasElementClass(element, "t-thumbs-outer") // thumbs
|| hasElementClass(element, "interproject-box") // interwiki boxes
|| hasElementClass(element, "normalized-box") // {{normalized}}
|| hasElementClass(element, "chemical-element-box") // {{chemical element box}}
|| element.getAttribute("typeof") === "mw:File/Thumb" // new floating image boxes
|| hasElementClass(element, "mw-halign-right") // mw-halign-right, by new floating image boxes
|| element.getAttribute("align") === "right" // anything that (explicitly) aligns
|| element.style.float === "right" // anything that (explicitly) floats
|| element.matches("style") // TemplateStyles
; // this is on its own line so that it's easier to add new rules
}
// Applies only to numbered etymologies.
function shouldDisplayBeforeCollapsible(element) {
return hasElementClass(element, "normalized-box") // {{normalized}}
; // this is on its own line so that it's easier to add new rules
}
function shouldCollapseForTheSakeOf(element) {
if (element.nodeType === 1) {
// don't collapse for the sake of senseid or etymid alone
if (element.matches(".senseid, .etymid"))
return false;
if (element.matches("p, div, span"))
return Array.prototype.some.call(element.childNodes, shouldCollapseForTheSakeOf);
return element.tagName !== "BR";
} else if (element.nodeType === 3) {
return !!element.textContent.trim(); // only if not entirely whitespace
} else {
return false;
}
}
function getHeadingQuery(onlyL3) {
return (onlyL3 ? ["h2", "h3"] : ["h2", "h3", "h4", "h5"])
.map(heading => `#mw-content-text ${heading}`)
.join(", ");
}
const navFramesById = {};
function collapseHeadingInNavbox(headingCandidate, heading, toggleCat,
headingMatcher, addEditLinks) {
let headingId;
const hl = headingCandidate.querySelector(".mw-headline");
if (hl && headingMatcher(hl.textContent))
headingId = hl.id;
// Parsoid
if (hasElementClass(headingCandidate.parentElement, "mw-heading") &&
headingMatcher(headingCandidate.textContent)) {
headingId = headingCandidate.id;
headingCandidate = headingCandidate.parentElement;
}
if (!headingId) return [undefined, undefined];
// pick all elements until next heading
const contents = [];
let nx = headingCandidate.nextElementSibling;
let collapse = false;
while (nx) {
if (shouldEndCollapsible(nx) || nx.querySelector(".NavFrame-streamline"))
break;
if (!shouldExcludeFromCollapsible(nx)) {
contents.push(nx);
collapse = collapse || shouldCollapseForTheSakeOf(nx);
}
nx = nx.nextElementSibling;
}
if (!collapse) return [undefined, undefined];
const navFrame = makeNavFrame(heading,
addEditLinks ? headingCandidate.querySelector(".mw-editsection") : null,
toggleCat, contents);
navFrame.id = headingId;
navFramesById[navFrame.id] = navFrame;
return [navFrame, headingCandidate];
}
function makeSingleCollapsible(heading, toggleCat, onlyL3) {
document.querySelectorAll(getHeadingQuery(onlyL3)).forEach((e) => {
const [navFrame, headingElement] = collapseHeadingInNavbox(e,
heading, toggleCat,
(headingText) => headingText === heading, true);
if (navFrame)
headingElement.parentNode.replaceChild(navFrame, headingElement);
});
}
function makeNumberedCollapsible(heading, toggleCat) {
const re = new RegExp("^" + heading + " \\d+(?:\.\\d+)?$");
document.querySelectorAll(getHeadingQuery(false)).forEach((e) => {
const [navFrame, headingElement] = collapseHeadingInNavbox(e,
heading, toggleCat,
(headingText) => headingText.match(re), false);
if (navFrame) {
let toInsertBefore = headingElement.nextElementSibling;
while (toInsertBefore && shouldDisplayBeforeCollapsible(toInsertBefore))
toInsertBefore = toInsertBefore.nextElementSibling;
headingElement.parentNode.insertBefore(navFrame, toInsertBefore);
}
});
}
function moveAnagrams() {
// move the Anagrams box above Further reading and References
document.querySelectorAll(".NavFrame[data-toggle-category=\"anagrams\"]").forEach((e) => {
let p = e.previousElementSibling;
while (p) {
if (p.tagName === "H2") {
break;
}
if (p.tagName === "H3") {
const hl = p.querySelector(".mw-headline");
if (hl && (hl.textContent === "Further reading" || hl.textContent === "References")) {
e.parentElement.insertBefore(e, p);
}
}
p = p.previousElementSibling;
}
});
}
function addGaps() {
// add gaps before streamline navboxes if preceded by certain elements
document.querySelectorAll(".NavFrame-streamline").forEach((sl) => {
const prev = sl.previousElementSibling;
const next = sl.nextElementSibling;
let tn;
if (prev) {
tn = prev.tagName;
if (tn === "OL" || tn === "UL") {
sl.style.marginTop = "1em";
}
}
if (next) {
tn = next.tagName;
if (tn === "HR") {
sl.style.marginBottom = "1em";
}
}
});
}
function defaultStreamline() {
makeSingleCollapsible("Alternative forms", "alternative forms", true);
makeSingleCollapsible("Alternative reconstructions", "alternative reconstructions", true);
makeSingleCollapsible("Etymology", "etymology");
makeSingleCollapsible("Glyph origin", "etymology");
makeSingleCollapsible("Description", "description");
makeSingleCollapsible("Pronunciation", "pronunciations");
makeSingleCollapsible("Production", "productions");
makeNumberedCollapsible("Etymology", "etymology");
makeNumberedCollapsible("Pronunciation", "pronunciations");
makeNumberedCollapsible("Production", "productions");
addGaps();
}
function superStreamline() {
makeSingleCollapsible("Alternative forms", "alternative forms", false);
makeSingleCollapsible("Alternative reconstructions", "alternative reconstructions", false);
makeSingleCollapsible("Inflection", "inflection");
makeSingleCollapsible("Declension", "inflection");
makeSingleCollapsible("Conjugation", "inflection");
makeSingleCollapsible("Mutation", "mutation");
makeSingleCollapsible("Synonyms", "synonyms");
makeSingleCollapsible("Antonyms", "antonyms");
makeSingleCollapsible("Hypernyms", "hypernyms");
makeSingleCollapsible("Hyponyms", "hyponyms");
makeSingleCollapsible("Meronyms", "meronyms");
makeSingleCollapsible("Holonyms", "holonyms");
makeSingleCollapsible("Troponyms", "troponyms");
makeSingleCollapsible("Coordinate terms", "coordinate terms");
makeSingleCollapsible("Derived terms", "derived terms");
makeSingleCollapsible("Compounds", "derived terms");
makeSingleCollapsible("Related terms", "related terms");
makeSingleCollapsible("Collocations", "collocations");
makeSingleCollapsible("Descendants", "descendants");
makeSingleCollapsible("Trivia", "trivia");
makeSingleCollapsible("See also", "related terms");
makeSingleCollapsible("References", "references");
makeSingleCollapsible("Further reading", "further reading");
makeSingleCollapsible("Anagrams", "anagrams");
moveAnagrams();
addGaps();
}
function openNavFrameForElementWithId(targetId) {
const navFrameTarget = navFramesById[targetId];
if (navFrameTarget)
openNavFrame(navFrameTarget);
// if what we targeted is inside a navframe, open it too
const targetElement = document.getElementById(targetId);
if (targetElement && targetElement.closest) {
const navFrameContainingTarget = targetElement.closest(".NavFrame-streamline");
if (navFrameContainingTarget)
openNavFrame(navFrameContainingTarget);
}
}
const { collapseElements, runInReconstructions } = preferences;
// only run in content namespaces.
// this must also run on 4 in [[MediaWiki:Gadgets-definition]] so that [[WT:GPREFS]] works.
if (mw.config.get("wgNamespaceNumber") === 0 || (runInReconstructions && mw.config.get("wgNamespaceNumber") === 118)) {
mw.hook("wikipage.content").add(() => {
try {
defaultStreamline();
if (collapseElements !== "aboveDefinitions")
superStreamline();
// refocus URL target in case the page layout changed, which it probably did
// also open a navframe if targeted
window.requestAnimationFrame(() => {
if (window.location.hash && window.location.hash.match(/^#/)) {
const targetId = window.location.hash.replace(/^#/, "");
const targetElement = document.getElementById(targetId);
if (targetElement)
targetElement.scrollIntoView();
openNavFrameForElementWithId(targetId);
}
});
// open a navframe if an internal link targets a section
window.addEventListener("hashchange", () => {
if (window.location.hash) {
const targetId = window.location.hash.replace(/^#/, "");
openNavFrameForElementWithId(targetId);
}
});
} catch (e) {
console.error(e);
}
});
}
// </nowiki>
90zbetucltlf909bqhaojz3k8tp0wj0
88991
88990
2026-08-06T23:26:17Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-Streamline.js]]
88990
javascript
text/javascript
"use strict";
// {{documentation}}
// <nowiki>
/*jshint strict:true, undef:true, latedef:true, esversion:6 */
/* global mw, createNavToggle */
const STREAMLINE_SYMBOL = "⌰";
// [[MediaWiki:Gadget-WiktGadgetPrefs.js]]
const preferences = mw.wiktGadgetPrefs.get(
"streamline",
{
label: {
en: "Streamline",
},
},
{
collapseElements: {
"type": "strenum",
"default": "aboveDefinitions",
"label": {
"en": "Elements to collapse"
},
"choices": [
"aboveDefinitions",
"allExceptNotes"
],
"choiceLabels": {
"en": {
"aboveDefinitions": "Sections before definitions",
"allExceptNotes": "Sections before and after definitions"
}
}
},
runInReconstructions: {
"type": "boolean",
"default": true,
"label": {
"en": "Apply also to reconstruction pages"
}
}
}
);
function makeNavFrame(heading, editLinks, toggleCat, contents) {
// now make a collapsible box
const navHead = document.createElement("div");
navHead.className = "NavHead";
navHead.style.cursor = "pointer";
navHead.style.fontSize = "1.1em";
const navContent = document.createElement("div");
navContent.className = "NavContent boxcontent streamline-boxcontent";
navContent.style.textAlign = "left";
const navFrame = document.createElement("div");
navFrame.className = "NavFrame NavFrame-streamline";
if (toggleCat)
navFrame.setAttribute("data-toggle-category", toggleCat);
navFrame.style.display = "block";
navFrame.append(navHead, navContent);
navHead.append(`${STREAMLINE_SYMBOL} ${heading}`);
try {
if (createNavToggle) createNavToggle(navFrame);
} catch (e) { }
if (editLinks) {
navContent.append(editLinks);
editLinks.style.float = "right";
// try displaying the edit links next to the show button
editLinks.style.marginTop = "-1.5em";
editLinks.style.marginRight = "8ch";
}
navContent.append(...contents);
return navFrame;
}
function isNavFrameOpen(frame) {
const content = frame.querySelector(".NavContent");
if (!content) return false;
const computedStyle = window.getComputedStyle(content);
if (!computedStyle) return false;
return computedStyle.display !== "none";
}
function openNavFrame(frame) {
if (!isNavFrameOpen(frame)) {
const navHead = frame.querySelector(".NavHead");
if (navHead)
navHead.click();
}
}
function anchorInUrl(anchor) {
if (window.location.hash) {
return window.location.hash.replace(/^#/, "") === anchor;
}
return false;
}
function hasElementClass(element, className) {
return element.classList.contains(className);
}
function shouldEndCollapsible(element) {
return element.matches("h1, h2, h3, h4, h5, h6, hr") // end on heading or horizontal line
|| hasElementClass(element, "NavFrame-streamline") // no nested streamline navboxes
|| hasElementClass(element, "mw-heading") // section heading
|| element.matches("section") // Parsoid
; // this is on its own line so that it's easier to add new rules
}
function shouldExcludeFromCollapsible(element) {
return hasElementClass(element, "thumb") // images
|| hasElementClass(element, "sister-project") // sister project box
|| hasElementClass(element, "kanji-table") // ja-kanjitab
|| hasElementClass(element, "floatright") // various tables
|| hasElementClass(element, "template-anchor") // anchor
|| hasElementClass(element, "t-thumbs-outer") // thumbs
|| hasElementClass(element, "interproject-box") // interwiki boxes
|| hasElementClass(element, "normalized-box") // {{normalized}}
|| hasElementClass(element, "chemical-element-box") // {{chemical element box}}
|| element.getAttribute("typeof") === "mw:File/Thumb" // new floating image boxes
|| hasElementClass(element, "mw-halign-right") // mw-halign-right, by new floating image boxes
|| element.getAttribute("align") === "right" // anything that (explicitly) aligns
|| element.style.float === "right" // anything that (explicitly) floats
|| element.matches("style") // TemplateStyles
; // this is on its own line so that it's easier to add new rules
}
// Applies only to numbered etymologies.
function shouldDisplayBeforeCollapsible(element) {
return hasElementClass(element, "normalized-box") // {{normalized}}
; // this is on its own line so that it's easier to add new rules
}
function shouldCollapseForTheSakeOf(element) {
if (element.nodeType === 1) {
// don't collapse for the sake of senseid or etymid alone
if (element.matches(".senseid, .etymid"))
return false;
if (element.matches("p, div, span"))
return Array.prototype.some.call(element.childNodes, shouldCollapseForTheSakeOf);
return element.tagName !== "BR";
} else if (element.nodeType === 3) {
return !!element.textContent.trim(); // only if not entirely whitespace
} else {
return false;
}
}
function getHeadingQuery(onlyL3) {
return (onlyL3 ? ["h2", "h3"] : ["h2", "h3", "h4", "h5"])
.map(heading => `#mw-content-text ${heading}`)
.join(", ");
}
const navFramesById = {};
function collapseHeadingInNavbox(headingCandidate, heading, toggleCat,
headingMatcher, addEditLinks) {
let headingId;
const hl = headingCandidate.querySelector(".mw-headline");
if (hl && headingMatcher(hl.textContent))
headingId = hl.id;
// Parsoid
if (hasElementClass(headingCandidate.parentElement, "mw-heading") &&
headingMatcher(headingCandidate.textContent)) {
headingId = headingCandidate.id;
headingCandidate = headingCandidate.parentElement;
}
if (!headingId) return [undefined, undefined];
// pick all elements until next heading
const contents = [];
let nx = headingCandidate.nextElementSibling;
let collapse = false;
while (nx) {
if (shouldEndCollapsible(nx) || nx.querySelector(".NavFrame-streamline"))
break;
if (!shouldExcludeFromCollapsible(nx)) {
contents.push(nx);
collapse = collapse || shouldCollapseForTheSakeOf(nx);
}
nx = nx.nextElementSibling;
}
if (!collapse) return [undefined, undefined];
const navFrame = makeNavFrame(heading,
addEditLinks ? headingCandidate.querySelector(".mw-editsection") : null,
toggleCat, contents);
navFrame.id = headingId;
navFramesById[navFrame.id] = navFrame;
return [navFrame, headingCandidate];
}
function makeSingleCollapsible(heading, toggleCat, onlyL3) {
document.querySelectorAll(getHeadingQuery(onlyL3)).forEach((e) => {
const [navFrame, headingElement] = collapseHeadingInNavbox(e,
heading, toggleCat,
(headingText) => headingText === heading, true);
if (navFrame)
headingElement.parentNode.replaceChild(navFrame, headingElement);
});
}
function makeNumberedCollapsible(heading, toggleCat) {
const re = new RegExp("^" + heading + " \\d+(?:\.\\d+)?$");
document.querySelectorAll(getHeadingQuery(false)).forEach((e) => {
const [navFrame, headingElement] = collapseHeadingInNavbox(e,
heading, toggleCat,
(headingText) => headingText.match(re), false);
if (navFrame) {
let toInsertBefore = headingElement.nextElementSibling;
while (toInsertBefore && shouldDisplayBeforeCollapsible(toInsertBefore))
toInsertBefore = toInsertBefore.nextElementSibling;
headingElement.parentNode.insertBefore(navFrame, toInsertBefore);
}
});
}
function moveAnagrams() {
// move the Anagrams box above Further reading and References
document.querySelectorAll(".NavFrame[data-toggle-category=\"anagrams\"]").forEach((e) => {
let p = e.previousElementSibling;
while (p) {
if (p.tagName === "H2") {
break;
}
if (p.tagName === "H3") {
const hl = p.querySelector(".mw-headline");
if (hl && (hl.textContent === "Further reading" || hl.textContent === "References")) {
e.parentElement.insertBefore(e, p);
}
}
p = p.previousElementSibling;
}
});
}
function addGaps() {
// add gaps before streamline navboxes if preceded by certain elements
document.querySelectorAll(".NavFrame-streamline").forEach((sl) => {
const prev = sl.previousElementSibling;
const next = sl.nextElementSibling;
let tn;
if (prev) {
tn = prev.tagName;
if (tn === "OL" || tn === "UL") {
sl.style.marginTop = "1em";
}
}
if (next) {
tn = next.tagName;
if (tn === "HR") {
sl.style.marginBottom = "1em";
}
}
});
}
function defaultStreamline() {
makeSingleCollapsible("Alternative forms", "alternative forms", true);
makeSingleCollapsible("Alternative reconstructions", "alternative reconstructions", true);
makeSingleCollapsible("Etymology", "etymology");
makeSingleCollapsible("Glyph origin", "etymology");
makeSingleCollapsible("Description", "description");
makeSingleCollapsible("Pronunciation", "pronunciations");
makeSingleCollapsible("Production", "productions");
makeNumberedCollapsible("Etymology", "etymology");
makeNumberedCollapsible("Pronunciation", "pronunciations");
makeNumberedCollapsible("Production", "productions");
addGaps();
}
function superStreamline() {
makeSingleCollapsible("Alternative forms", "alternative forms", false);
makeSingleCollapsible("Alternative reconstructions", "alternative reconstructions", false);
makeSingleCollapsible("Inflection", "inflection");
makeSingleCollapsible("Declension", "inflection");
makeSingleCollapsible("Conjugation", "inflection");
makeSingleCollapsible("Mutation", "mutation");
makeSingleCollapsible("Synonyms", "synonyms");
makeSingleCollapsible("Antonyms", "antonyms");
makeSingleCollapsible("Hypernyms", "hypernyms");
makeSingleCollapsible("Hyponyms", "hyponyms");
makeSingleCollapsible("Meronyms", "meronyms");
makeSingleCollapsible("Holonyms", "holonyms");
makeSingleCollapsible("Troponyms", "troponyms");
makeSingleCollapsible("Coordinate terms", "coordinate terms");
makeSingleCollapsible("Derived terms", "derived terms");
makeSingleCollapsible("Compounds", "derived terms");
makeSingleCollapsible("Related terms", "related terms");
makeSingleCollapsible("Collocations", "collocations");
makeSingleCollapsible("Descendants", "descendants");
makeSingleCollapsible("Trivia", "trivia");
makeSingleCollapsible("See also", "related terms");
makeSingleCollapsible("References", "references");
makeSingleCollapsible("Further reading", "further reading");
makeSingleCollapsible("Anagrams", "anagrams");
moveAnagrams();
addGaps();
}
function openNavFrameForElementWithId(targetId) {
const navFrameTarget = navFramesById[targetId];
if (navFrameTarget)
openNavFrame(navFrameTarget);
// if what we targeted is inside a navframe, open it too
const targetElement = document.getElementById(targetId);
if (targetElement && targetElement.closest) {
const navFrameContainingTarget = targetElement.closest(".NavFrame-streamline");
if (navFrameContainingTarget)
openNavFrame(navFrameContainingTarget);
}
}
const { collapseElements, runInReconstructions } = preferences;
// only run in content namespaces.
// this must also run on 4 in [[MediaWiki:Gadgets-definition]] so that [[WT:GPREFS]] works.
if (mw.config.get("wgNamespaceNumber") === 0 || (runInReconstructions && mw.config.get("wgNamespaceNumber") === 118)) {
mw.hook("wikipage.content").add(() => {
try {
defaultStreamline();
if (collapseElements !== "aboveDefinitions")
superStreamline();
// refocus URL target in case the page layout changed, which it probably did
// also open a navframe if targeted
window.requestAnimationFrame(() => {
if (window.location.hash && window.location.hash.match(/^#/)) {
const targetId = window.location.hash.replace(/^#/, "");
const targetElement = document.getElementById(targetId);
if (targetElement)
targetElement.scrollIntoView();
openNavFrameForElementWithId(targetId);
}
});
// open a navframe if an internal link targets a section
window.addEventListener("hashchange", () => {
if (window.location.hash) {
const targetId = window.location.hash.replace(/^#/, "");
openNavFrameForElementWithId(targetId);
}
});
} catch (e) {
console.error(e);
}
});
}
// </nowiki>
90zbetucltlf909bqhaojz3k8tp0wj0
MediaWiki:Gadget-minitoc.js
8
17995
88992
2025-10-19T02:04:11Z
en>Ioaxxere
0
mobile fix, minor cleanup
88992
javascript
text/javascript
// <nowiki>
// Initialize preferred languages from local storage.
let preferredLanguages = new Set(JSON.parse(localStorage.getItem("minitocLangs")));
// Empty and filled-in bookmark icons.
const unselectedIcon = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18" height="17" viewBox="0 0 20 20" aria-hidden="true" fill="currentColor"><g><path d="M5 1a2 2 0 00-2 2v16l7-5 7 5V3a2 2 0 00-2-2zm10 14.25-5-3.5-5 3.5V3h10z"></path></g></svg>`;
const selectedIcon = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18" height="17" viewBox="0 0 20 20" aria-hidden="true" fill="currentColor"><g><path d="M5 1a2 2 0 00-2 2v16l7-5 7 5V3a2 2 0 00-2-2z"></path></g></svg>`;
mw.util.addCSS(`
.minitoc-icon-selected, .minitoc-icon-unselected {
cursor: pointer;
vertical-align: sub;
}
`);
function displayLanguages() {
// Clear out previously displayed languages.
document.querySelectorAll(".minitoc-linked-languages").forEach(elem => elem.remove());
for (let minitoc of document.querySelectorAll(".minitoc")) {
let linkedLanguages = document.createElement("span");
linkedLanguages.className = "minitoc-linked-languages";
for (let languageLink of minitoc.querySelectorAll(`.NavContent > a:not([href="#catlinks"]`)) {
if (preferredLanguages.has(languageLink.textContent)) {
let link = document.createElement("a");
link.textContent = `→ ${languageLink.textContent}`;
link.href = languageLink.href;
link.style.whiteSpace = "nowrap";
linkedLanguages.append(" ", link);
}
}
if (linkedLanguages.childElementCount)
minitoc.querySelector(".NavHead").append(linkedLanguages);
}
}
// Setup.
// Note: the code is generic and can handle multiple miniTOCs on one page, although this probably isn't necessary.
for (let minitoc of document.querySelectorAll(".minitoc")) {
minitoc.querySelector(".NavContent").insertAdjacentHTML("beforeend",
`<span style="font-size: 85%" class="minitoc-button-container">
 
[<a class="minitoc-button-select" role="button">
Select preferred languages
</a>]
<span style="display: none">
 
[<a class="minitoc-button-clear" role="button">
Clear all
</a>]
</span>
</span>`
.replace(/[\n\t]/g, "")
);
let selectButton = minitoc.querySelector(".minitoc-button-select");
let clearButton = minitoc.querySelector(".minitoc-button-clear");
selectButton.addEventListener("click", () => {
// The button can be in two states: "select" and "save".
if (selectButton.textContent === "Select preferred languages") {
clearButton.parentElement.style.display = "";
// Make sure you can't have multiple miniTOCs in "select" mode simultaneously.
for (let button of document.querySelectorAll(".minitoc-button-select")) {
if (button.textContent === "Save preferred languages") {
// Trigger the saving procedure.
button.click();
}
}
selectButton.textContent = "Save preferred languages";
for (let languageLink of minitoc.querySelectorAll(`.NavContent > a:not([href="#catlinks"]`)) {
let iconContainer = document.createElement("span");
iconContainer.role = "button";
languageLink.insertAdjacentElement("beforebegin", iconContainer);
if (preferredLanguages.has(languageLink.textContent)) {
iconContainer.className = "minitoc-icon-selected";
iconContainer.innerHTML = selectedIcon;
} else {
iconContainer.className = "minitoc-icon-unselected";
iconContainer.innerHTML = unselectedIcon;
}
iconContainer.addEventListener("click", () => {
if (preferredLanguages.has(languageLink.textContent)) {
preferredLanguages.delete(languageLink.textContent);
iconContainer.className = "minitoc-icon-unselected";
iconContainer.innerHTML = unselectedIcon;
} else {
preferredLanguages.add(languageLink.textContent);
iconContainer.className = "minitoc-icon-selected";
iconContainer.innerHTML = selectedIcon;
}
});
}
} else {
// Save to local storage.
localStorage.setItem("minitocLangs", JSON.stringify(Array.from(preferredLanguages)));
// Reset state.
minitoc.querySelectorAll(".minitoc-icon-selected, .minitoc-icon-unselected").forEach(elem => elem.remove());
selectButton.textContent = "Select preferred languages";
clearButton.parentElement.style.display = "none";
displayLanguages();
}
});
clearButton.addEventListener("click", () => {
preferredLanguages.clear();
for (let iconContainer of minitoc.querySelectorAll(".minitoc-icon-selected")) {
iconContainer.innerHTML = unselectedIcon;
iconContainer.className = "minitoc-icon-unselected";
}
});
}
displayLanguages();
// </nowiki>
08fibjrel8pcvuuyqfxb4fc8oev6f0l
88993
88992
2026-08-06T23:26:28Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-minitoc.js]]
88992
javascript
text/javascript
// <nowiki>
// Initialize preferred languages from local storage.
let preferredLanguages = new Set(JSON.parse(localStorage.getItem("minitocLangs")));
// Empty and filled-in bookmark icons.
const unselectedIcon = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18" height="17" viewBox="0 0 20 20" aria-hidden="true" fill="currentColor"><g><path d="M5 1a2 2 0 00-2 2v16l7-5 7 5V3a2 2 0 00-2-2zm10 14.25-5-3.5-5 3.5V3h10z"></path></g></svg>`;
const selectedIcon = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18" height="17" viewBox="0 0 20 20" aria-hidden="true" fill="currentColor"><g><path d="M5 1a2 2 0 00-2 2v16l7-5 7 5V3a2 2 0 00-2-2z"></path></g></svg>`;
mw.util.addCSS(`
.minitoc-icon-selected, .minitoc-icon-unselected {
cursor: pointer;
vertical-align: sub;
}
`);
function displayLanguages() {
// Clear out previously displayed languages.
document.querySelectorAll(".minitoc-linked-languages").forEach(elem => elem.remove());
for (let minitoc of document.querySelectorAll(".minitoc")) {
let linkedLanguages = document.createElement("span");
linkedLanguages.className = "minitoc-linked-languages";
for (let languageLink of minitoc.querySelectorAll(`.NavContent > a:not([href="#catlinks"]`)) {
if (preferredLanguages.has(languageLink.textContent)) {
let link = document.createElement("a");
link.textContent = `→ ${languageLink.textContent}`;
link.href = languageLink.href;
link.style.whiteSpace = "nowrap";
linkedLanguages.append(" ", link);
}
}
if (linkedLanguages.childElementCount)
minitoc.querySelector(".NavHead").append(linkedLanguages);
}
}
// Setup.
// Note: the code is generic and can handle multiple miniTOCs on one page, although this probably isn't necessary.
for (let minitoc of document.querySelectorAll(".minitoc")) {
minitoc.querySelector(".NavContent").insertAdjacentHTML("beforeend",
`<span style="font-size: 85%" class="minitoc-button-container">
 
[<a class="minitoc-button-select" role="button">
Select preferred languages
</a>]
<span style="display: none">
 
[<a class="minitoc-button-clear" role="button">
Clear all
</a>]
</span>
</span>`
.replace(/[\n\t]/g, "")
);
let selectButton = minitoc.querySelector(".minitoc-button-select");
let clearButton = minitoc.querySelector(".minitoc-button-clear");
selectButton.addEventListener("click", () => {
// The button can be in two states: "select" and "save".
if (selectButton.textContent === "Select preferred languages") {
clearButton.parentElement.style.display = "";
// Make sure you can't have multiple miniTOCs in "select" mode simultaneously.
for (let button of document.querySelectorAll(".minitoc-button-select")) {
if (button.textContent === "Save preferred languages") {
// Trigger the saving procedure.
button.click();
}
}
selectButton.textContent = "Save preferred languages";
for (let languageLink of minitoc.querySelectorAll(`.NavContent > a:not([href="#catlinks"]`)) {
let iconContainer = document.createElement("span");
iconContainer.role = "button";
languageLink.insertAdjacentElement("beforebegin", iconContainer);
if (preferredLanguages.has(languageLink.textContent)) {
iconContainer.className = "minitoc-icon-selected";
iconContainer.innerHTML = selectedIcon;
} else {
iconContainer.className = "minitoc-icon-unselected";
iconContainer.innerHTML = unselectedIcon;
}
iconContainer.addEventListener("click", () => {
if (preferredLanguages.has(languageLink.textContent)) {
preferredLanguages.delete(languageLink.textContent);
iconContainer.className = "minitoc-icon-unselected";
iconContainer.innerHTML = unselectedIcon;
} else {
preferredLanguages.add(languageLink.textContent);
iconContainer.className = "minitoc-icon-selected";
iconContainer.innerHTML = selectedIcon;
}
});
}
} else {
// Save to local storage.
localStorage.setItem("minitocLangs", JSON.stringify(Array.from(preferredLanguages)));
// Reset state.
minitoc.querySelectorAll(".minitoc-icon-selected, .minitoc-icon-unselected").forEach(elem => elem.remove());
selectButton.textContent = "Select preferred languages";
clearButton.parentElement.style.display = "none";
displayLanguages();
}
});
clearButton.addEventListener("click", () => {
preferredLanguages.clear();
for (let iconContainer of minitoc.querySelectorAll(".minitoc-icon-selected")) {
iconContainer.innerHTML = unselectedIcon;
iconContainer.className = "minitoc-icon-unselected";
}
});
}
displayLanguages();
// </nowiki>
08fibjrel8pcvuuyqfxb4fc8oev6f0l
MediaWiki:Gadget-JumpToError.js
8
17996
88995
2026-08-03T08:47:50Z
en>Fenakhay
0
88995
javascript
text/javascript
// <nowiki>
(async () => {
"use strict";
const NOTIFY_TAG = "jump-to-scribunto-error";
const closestAll = (elem, selector) => {
const out = [];
let node = elem;
while (node) {
node = node.closest(selector);
if (!node) break;
out.push(node);
node = node.parentElement;
}
return out;
};
const isDisplayNone = elem =>
!!elem && window.getComputedStyle(elem).display === "none";
const isVisible = elem => !!elem && elem.getClientRects().length > 0;
const clickIfPresent = elem => {
if (elem && typeof elem.click === "function") {
elem.click();
return true;
}
return false;
};
const revealers = [
error => closestAll(error, ".NavContent")
.filter(isDisplayNone)
.filter(nav => {
const sibling = nav.previousElementSibling;
if (sibling && sibling.classList.contains("NavHead"))
return clickIfPresent(sibling);
const frame = nav.closest(".NavFrame");
return !!frame && clickIfPresent(frame.querySelector(".NavHead"));
}).length,
error => closestAll(error, ".mw-collapsed")
.filter(collapsed => {
const toggle = [...collapsed.querySelectorAll(".mw-collapsible-toggle-collapsed")]
.find(t => t.closest(".mw-collapsed") === collapsed);
return clickIfPresent(toggle);
}).length,
error => closestAll(error, ".vsShow, .vsHide")
.filter(isDisplayNone)
.filter(vs => {
const switcher = vs.closest(".vsSwitcher");
return !!switcher && clickIfPresent(switcher.querySelector(`[role="button"]`));
}).length,
error => closestAll(error, "[data-relation-class]")
.filter(nym => {
if (!isDisplayNone(nym.closest("dl"))) return false;
const li = nym.closest("li");
if (!li) return false;
const relation = nym.getAttribute("data-relation-class") || "";
const toggle = [...li.querySelectorAll(".nyms-toggle > a")]
.find(a => a.textContent.includes(relation));
return clickIfPresent(toggle);
}).length,
error => closestAll(error, ".wikt-quote-container")
.filter(isDisplayNone)
.filter(quotes => {
const parent = quotes.parentElement;
return !!parent && clickIfPresent(parent.querySelector(".HQToggle > a"));
}).length
];
const reveal = error => {
for (let pass = 0; pass < 10; pass++) {
if (isVisible(error)) return;
let clicks = 0;
for (const revealer of revealers) {
// One broken revealer must not stop the others.
try {
clicks += revealer(error);
} catch (err) {
mw.log.warn("[jump-to-scribunto-error] revealer failed", err);
}
}
if (!clicks) return;
}
};
const scrollIntoViewWhenSettled = elem => {
const deadline = Date.now() + 1000;
let lastTop = NaN;
const step = () => {
elem.scrollIntoView({block: "center"});
const docTop = elem.getBoundingClientRect().top + window.scrollY;
if (Math.abs(docTop - lastTop) < 1 || Date.now() > deadline) return;
lastTop = docTop;
window.setTimeout(step, 100);
};
requestAnimationFrame(step);
};
const notifyIfErrors = async () => {
const errors = document.querySelectorAll(".scribunto-error");
if (!errors.length) return;
const message = document.createElement("span");
message.id = NOTIFY_TAG;
message.setAttribute("role", "button");
message.tabIndex = 0;
message.style.cursor = "pointer";
message.textContent = `Warning: this page has ${errors.length} Lua error${errors.length === 1 ? "" : "s"}; click here to jump to the error`;
const jump = () => {
const target = document.querySelector(".scribunto-error");
if (!target) return;
reveal(target);
scrollIntoViewWhenSettled(target);
};
message.addEventListener("click", jump);
message.addEventListener("keydown", event => {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
jump();
}
});
try {
await mw.notify(message, {autoHide: false, tag: NOTIFY_TAG});
} catch (err) {
mw.log.warn("[jump-to-scribunto-error] mw.notify failed", err);
}
};
mw.hook("wikipage.content").add(() => {
notifyIfErrors();
});
})();
// </nowiki>
gj68lrlv2ab3fybfqxx8jqglifjfyww
88996
88995
2026-08-06T23:32:31Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-JumpToError.js]]
88995
javascript
text/javascript
// <nowiki>
(async () => {
"use strict";
const NOTIFY_TAG = "jump-to-scribunto-error";
const closestAll = (elem, selector) => {
const out = [];
let node = elem;
while (node) {
node = node.closest(selector);
if (!node) break;
out.push(node);
node = node.parentElement;
}
return out;
};
const isDisplayNone = elem =>
!!elem && window.getComputedStyle(elem).display === "none";
const isVisible = elem => !!elem && elem.getClientRects().length > 0;
const clickIfPresent = elem => {
if (elem && typeof elem.click === "function") {
elem.click();
return true;
}
return false;
};
const revealers = [
error => closestAll(error, ".NavContent")
.filter(isDisplayNone)
.filter(nav => {
const sibling = nav.previousElementSibling;
if (sibling && sibling.classList.contains("NavHead"))
return clickIfPresent(sibling);
const frame = nav.closest(".NavFrame");
return !!frame && clickIfPresent(frame.querySelector(".NavHead"));
}).length,
error => closestAll(error, ".mw-collapsed")
.filter(collapsed => {
const toggle = [...collapsed.querySelectorAll(".mw-collapsible-toggle-collapsed")]
.find(t => t.closest(".mw-collapsed") === collapsed);
return clickIfPresent(toggle);
}).length,
error => closestAll(error, ".vsShow, .vsHide")
.filter(isDisplayNone)
.filter(vs => {
const switcher = vs.closest(".vsSwitcher");
return !!switcher && clickIfPresent(switcher.querySelector(`[role="button"]`));
}).length,
error => closestAll(error, "[data-relation-class]")
.filter(nym => {
if (!isDisplayNone(nym.closest("dl"))) return false;
const li = nym.closest("li");
if (!li) return false;
const relation = nym.getAttribute("data-relation-class") || "";
const toggle = [...li.querySelectorAll(".nyms-toggle > a")]
.find(a => a.textContent.includes(relation));
return clickIfPresent(toggle);
}).length,
error => closestAll(error, ".wikt-quote-container")
.filter(isDisplayNone)
.filter(quotes => {
const parent = quotes.parentElement;
return !!parent && clickIfPresent(parent.querySelector(".HQToggle > a"));
}).length
];
const reveal = error => {
for (let pass = 0; pass < 10; pass++) {
if (isVisible(error)) return;
let clicks = 0;
for (const revealer of revealers) {
// One broken revealer must not stop the others.
try {
clicks += revealer(error);
} catch (err) {
mw.log.warn("[jump-to-scribunto-error] revealer failed", err);
}
}
if (!clicks) return;
}
};
const scrollIntoViewWhenSettled = elem => {
const deadline = Date.now() + 1000;
let lastTop = NaN;
const step = () => {
elem.scrollIntoView({block: "center"});
const docTop = elem.getBoundingClientRect().top + window.scrollY;
if (Math.abs(docTop - lastTop) < 1 || Date.now() > deadline) return;
lastTop = docTop;
window.setTimeout(step, 100);
};
requestAnimationFrame(step);
};
const notifyIfErrors = async () => {
const errors = document.querySelectorAll(".scribunto-error");
if (!errors.length) return;
const message = document.createElement("span");
message.id = NOTIFY_TAG;
message.setAttribute("role", "button");
message.tabIndex = 0;
message.style.cursor = "pointer";
message.textContent = `Warning: this page has ${errors.length} Lua error${errors.length === 1 ? "" : "s"}; click here to jump to the error`;
const jump = () => {
const target = document.querySelector(".scribunto-error");
if (!target) return;
reveal(target);
scrollIntoViewWhenSettled(target);
};
message.addEventListener("click", jump);
message.addEventListener("keydown", event => {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
jump();
}
});
try {
await mw.notify(message, {autoHide: false, tag: NOTIFY_TAG});
} catch (err) {
mw.log.warn("[jump-to-scribunto-error] mw.notify failed", err);
}
};
mw.hook("wikipage.content").add(() => {
notifyIfErrors();
});
})();
// </nowiki>
gj68lrlv2ab3fybfqxx8jqglifjfyww
MediaWiki:Gadget-VetVoters.js
8
17997
88997
2026-04-20T04:35:01Z
en>Fenakhay
0
88997
javascript
text/javascript
const EDIT_COUNT_AT_LEAST = 500; // note: above 500 would require multiple api requests
const FIRST_EDIT_PREDATES_VOTE_BY = 30; // days
const ELIGIBLE_NAMESPACES = "0|100|106|110|114|118";
const IPV4_REGEX = /^(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?$/;
const IPV6_REGEX = /^[\da-f:]+(?:\/\d{1,3})?$/i;
const TEMP_ACCT_REGEX = /^~[0-9]{4}(?:-[0-9]{1,5})+/;
const USER_LINK_PATTERNS = {
'.mw-body-content [href^="//en.wiktionary.org/wiki/User:"]': (title) =>
title.replace(/^User:/, ""),
'.new[href^="//en.wiktionary.org/w/index.php?title=User:"]': (title) =>
title.replace(/^User:/, "").replace(/ \(page does not exist\)$/, ""),
};
const API_HEADERS = {
"Api-User-Agent": "Vet Voters (gadget developed by [[User:Ioaxxere]])",
};
const actionAPI = new mw.Api({ ajax: { headers: API_HEADERS } });
const restAPI = new mw.Rest({ ajax: { headers: API_HEADERS } });
/**
* Guesses when the vote started
* @param {Document} doc the document
* @returns {Date | null}
*/
function voteStart(doc) {
let endDate = new Date(NaN);
let dateMatch = doc
.querySelector(".mw-parser-output")
.textContent.match(
/Vote start(?:s|ed):.*?([0-9]{2}:[0-9]{2}), ([0-9]+ \w+ [0-9]{4})/,
);
if (dateMatch) {
endDate = new Date(`${dateMatch[2]} ${dateMatch[1]} UTC`);
if (!isNaN(endDate)) return endDate;
}
// If parsing fails here, just check the title.
// Votes typically contain the date string in the title (e.g. 2026-04).
// Conservatively assume the 1st of the month (catch false positives, avoid false negatives)
let titleMatch = mw.config.values.wgTitle.match(/[0-9]{4}-[0-9]{2}/);
if (titleMatch) {
endDate = new Date(`${titleMatch[0]}-01`);
if (!isNaN(endDate)) return endDate;
}
return null;
}
let alreadyAddedCss = false;
/**
* Adds a class to an HTML element for the user, which highlights them as being ineligible to vote
* @param {HTMLAnchorElement} elem
* @returns {void}
*/
function highlightIneligible(elem) {
if (!alreadyAddedCss) {
mw.util.addCSS(`
.ineligible-voter {
text-decoration: var(--wikt-palette-darkred) wavy underline 0.15em !important;
}
.ineligible-voter:hover::after {
content: " User is ineligible to vote!";
font-size: 0.8em;
color: var(--wikt-palette-darkred);
text-decoration: none;
font-family: sans-serif;
}
`);
alreadyAddedCss = true;
}
elem.classList.add("ineligible-voter");
}
/**
* Finds a user's current username
* @param {string} username
* @returns {string} the current username
*/
async function resolveRenames(username) {
while (true) {
let resp = await actionAPI.post({
action: "query",
format: "json",
list: "logevents",
letype: "renameuser",
letitle: `User:${username}`,
lelimit: 1,
});
let entries = resp.query.logevents;
if (!entries.length) break;
let newuser = entries[0].params.newuser;
if (newuser.endsWith("(usurped)")) break; // hacky but ok
username = newuser;
}
return username;
}
/**
* Returns true if the given user can vote, otherwise false
* @param {string} username a user or anon user
* @param {Date} endDate when the vote ends
* @returns boolean
*/
async function verifyUser(username, endDate) {
if (userIsAnon(username)) return false; // anons can never vote
username = await resolveRenames(username);
const earliestEditPredates = new Date(endDate);
earliestEditPredates.setDate(
earliestEditPredates.getDate() - FIRST_EDIT_PREDATES_VOTE_BY,
);
// This API call already excludes transwiki contributions.
const resp1 = await actionAPI.post({
action: "query",
format: "json",
list: "usercontribs",
ucuser: username,
uclimit: EDIT_COUNT_AT_LEAST,
ucnamespace: ELIGIBLE_NAMESPACES,
ucdir: "newer",
});
const contribs = resp1.query.usercontribs;
let total = contribs.length;
let oldest = contribs[0] ? new Date(contribs[0].timestamp) : null;
if (total >= EDIT_COUNT_AT_LEAST && oldest <= earliestEditPredates)
return true;
const resp2 = await actionAPI.post({
action: "query",
format: "json",
list: "alldeletedrevisions",
adrlimit: EDIT_COUNT_AT_LEAST,
adruser: username,
adrnamespace: ELIGIBLE_NAMESPACES,
adrdir: "newer",
});
const deleted = resp2.query.alldeletedrevisions;
total += deleted.length;
if (deleted[0]) {
let deletedOldest = new Date(deleted[0].timestamp);
oldest = oldest
? new Date(Math.min(oldest.getTime(), deletedOldest.getTime()))
: deletedOldest;
}
return total >= EDIT_COUNT_AT_LEAST && oldest && oldest <= earliestEditPredates;
}
/**
* Returns true if cidr is undefined or in range [0, maxBits].
* @param {string|undefined} cidr
* @param {number} maxBits
* @returns {boolean}
*/
function isValidCidr(cidr, maxBits) {
if (cidr === undefined) return true;
let cidrNum = Number(cidr);
return Number.isInteger(cidrNum) && cidrNum >= 0 && cidrNum <= maxBits;
}
/**
* Validates IPv4 with optional CIDR suffix.
* @param {string} candidate
* @returns {boolean}
*/
function isValidIPv4(candidate) {
if (!IPV4_REGEX.test(candidate)) return false;
let [addr, cidr] = candidate.split("/");
let octets = addr.split(".").map(Number);
let ipv4Valid =
octets.length === 4 &&
octets.every((n) => Number.isInteger(n) && n >= 0 && n <= 255);
return ipv4Valid && isValidCidr(cidr, 32);
}
/**
* Validates IPv6 (full or compressed) with optional CIDR suffix.
* @param {string} candidate
* @returns {boolean}
*/
function isValidIPv6(candidate) {
if (!IPV6_REGEX.test(candidate) || !candidate.includes(":")) return false;
let [addr, cidr] = candidate.split("/");
if (!isValidCidr(cidr, 128)) return false;
if (addr.includes(":::")) return false;
if (addr.includes("::")) {
if ((addr.match(/::/g) || []).length > 1) return false;
let [left, right] = addr.split("::");
let leftParts = left ? left.split(":").filter(Boolean) : [];
let rightParts = right ? right.split(":").filter(Boolean) : [];
let hextets = leftParts.length + rightParts.length;
let validParts = [...leftParts, ...rightParts].every((part) =>
/^[\da-f]{1,4}$/i.test(part),
);
return validParts && hextets < 8;
}
let parts = addr.split(":");
return parts.length === 8 && parts.every((part) => /^[\da-f]{1,4}$/i.test(part));
}
/**
* Checks if a user is an IP/anon user, returning true if they are
* @param {string} user
* @returns {bool}
*/
function userIsAnon(user) {
const normalized = user.trim();
if (TEMP_ACCT_REGEX.test(normalized)) return true;
return isValidIPv4(normalized) || isValidIPv6(normalized);
}
// only fire on votes pages
let pageName = mw.config.values.wgPageName;
if (
pageName.startsWith("Wiktionary:Votes/") ||
pageName.startsWith("Wiktionary_talk:Votes/")
) {
(async () => {
// Step 1: find out when the vote ends
let endDate;
if (pageName.startsWith("Wiktionary_talk:")) {
let mainTitle = pageName.replace(/^Wiktionary_talk:/, "Wiktionary:");
let mainPageResp = await restAPI.get(
`/v1/page/${encodeURIComponent(mainTitle)}/html`,
);
let mainPageDoc = new DOMParser().parseFromString(
mainPageResp,
"text/html",
);
endDate = voteStart(mainPageDoc);
} else {
endDate = voteStart(document);
}
if (!(endDate instanceof Date) || isNaN(endDate)) {
console.warn(
"[vetvoters] Could not parse vote start date; skipping eligibility checks.",
);
return;
}
// Step 2: collect user elements, including anons
let userlinks = new Map(); // linkElem [HTMLElement] -> acctName [string]
Object.entries(USER_LINK_PATTERNS).forEach(([selector, normalize]) => {
document
.querySelectorAll(selector)
.forEach((link) => userlinks.set(link, normalize(link.title)));
});
for (let link of document.querySelectorAll(
`.mw-body-content [href^="//en.wiktionary.org/wiki/Special:Contributions/"]`,
)) {
let acct = link.title.replace(/^Special:Contributions\//, "");
if (!userIsAnon(acct)) continue;
userlinks.set(link, acct);
}
let usernames = [...new Set(userlinks.values())];
let queries = usernames.map(async (user) => {
try {
return [user, await verifyUser(user, endDate)];
} catch (err) {
console.warn(
`[vetvoters] Failed to verify ${user}; treating as eligible.`,
err,
);
return [user, true];
}
});
let userIsEligible = new Map(await Promise.all(queries));
for (let [elem, user] of userlinks) {
if (userIsEligible.get(user) === false) highlightIneligible(elem);
}
})();
}
9gnom4jfbcsvkwno0gcyo0t8wii1m9k
88998
88997
2026-08-06T23:32:38Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-VetVoters.js]]
88997
javascript
text/javascript
const EDIT_COUNT_AT_LEAST = 500; // note: above 500 would require multiple api requests
const FIRST_EDIT_PREDATES_VOTE_BY = 30; // days
const ELIGIBLE_NAMESPACES = "0|100|106|110|114|118";
const IPV4_REGEX = /^(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?$/;
const IPV6_REGEX = /^[\da-f:]+(?:\/\d{1,3})?$/i;
const TEMP_ACCT_REGEX = /^~[0-9]{4}(?:-[0-9]{1,5})+/;
const USER_LINK_PATTERNS = {
'.mw-body-content [href^="//en.wiktionary.org/wiki/User:"]': (title) =>
title.replace(/^User:/, ""),
'.new[href^="//en.wiktionary.org/w/index.php?title=User:"]': (title) =>
title.replace(/^User:/, "").replace(/ \(page does not exist\)$/, ""),
};
const API_HEADERS = {
"Api-User-Agent": "Vet Voters (gadget developed by [[User:Ioaxxere]])",
};
const actionAPI = new mw.Api({ ajax: { headers: API_HEADERS } });
const restAPI = new mw.Rest({ ajax: { headers: API_HEADERS } });
/**
* Guesses when the vote started
* @param {Document} doc the document
* @returns {Date | null}
*/
function voteStart(doc) {
let endDate = new Date(NaN);
let dateMatch = doc
.querySelector(".mw-parser-output")
.textContent.match(
/Vote start(?:s|ed):.*?([0-9]{2}:[0-9]{2}), ([0-9]+ \w+ [0-9]{4})/,
);
if (dateMatch) {
endDate = new Date(`${dateMatch[2]} ${dateMatch[1]} UTC`);
if (!isNaN(endDate)) return endDate;
}
// If parsing fails here, just check the title.
// Votes typically contain the date string in the title (e.g. 2026-04).
// Conservatively assume the 1st of the month (catch false positives, avoid false negatives)
let titleMatch = mw.config.values.wgTitle.match(/[0-9]{4}-[0-9]{2}/);
if (titleMatch) {
endDate = new Date(`${titleMatch[0]}-01`);
if (!isNaN(endDate)) return endDate;
}
return null;
}
let alreadyAddedCss = false;
/**
* Adds a class to an HTML element for the user, which highlights them as being ineligible to vote
* @param {HTMLAnchorElement} elem
* @returns {void}
*/
function highlightIneligible(elem) {
if (!alreadyAddedCss) {
mw.util.addCSS(`
.ineligible-voter {
text-decoration: var(--wikt-palette-darkred) wavy underline 0.15em !important;
}
.ineligible-voter:hover::after {
content: " User is ineligible to vote!";
font-size: 0.8em;
color: var(--wikt-palette-darkred);
text-decoration: none;
font-family: sans-serif;
}
`);
alreadyAddedCss = true;
}
elem.classList.add("ineligible-voter");
}
/**
* Finds a user's current username
* @param {string} username
* @returns {string} the current username
*/
async function resolveRenames(username) {
while (true) {
let resp = await actionAPI.post({
action: "query",
format: "json",
list: "logevents",
letype: "renameuser",
letitle: `User:${username}`,
lelimit: 1,
});
let entries = resp.query.logevents;
if (!entries.length) break;
let newuser = entries[0].params.newuser;
if (newuser.endsWith("(usurped)")) break; // hacky but ok
username = newuser;
}
return username;
}
/**
* Returns true if the given user can vote, otherwise false
* @param {string} username a user or anon user
* @param {Date} endDate when the vote ends
* @returns boolean
*/
async function verifyUser(username, endDate) {
if (userIsAnon(username)) return false; // anons can never vote
username = await resolveRenames(username);
const earliestEditPredates = new Date(endDate);
earliestEditPredates.setDate(
earliestEditPredates.getDate() - FIRST_EDIT_PREDATES_VOTE_BY,
);
// This API call already excludes transwiki contributions.
const resp1 = await actionAPI.post({
action: "query",
format: "json",
list: "usercontribs",
ucuser: username,
uclimit: EDIT_COUNT_AT_LEAST,
ucnamespace: ELIGIBLE_NAMESPACES,
ucdir: "newer",
});
const contribs = resp1.query.usercontribs;
let total = contribs.length;
let oldest = contribs[0] ? new Date(contribs[0].timestamp) : null;
if (total >= EDIT_COUNT_AT_LEAST && oldest <= earliestEditPredates)
return true;
const resp2 = await actionAPI.post({
action: "query",
format: "json",
list: "alldeletedrevisions",
adrlimit: EDIT_COUNT_AT_LEAST,
adruser: username,
adrnamespace: ELIGIBLE_NAMESPACES,
adrdir: "newer",
});
const deleted = resp2.query.alldeletedrevisions;
total += deleted.length;
if (deleted[0]) {
let deletedOldest = new Date(deleted[0].timestamp);
oldest = oldest
? new Date(Math.min(oldest.getTime(), deletedOldest.getTime()))
: deletedOldest;
}
return total >= EDIT_COUNT_AT_LEAST && oldest && oldest <= earliestEditPredates;
}
/**
* Returns true if cidr is undefined or in range [0, maxBits].
* @param {string|undefined} cidr
* @param {number} maxBits
* @returns {boolean}
*/
function isValidCidr(cidr, maxBits) {
if (cidr === undefined) return true;
let cidrNum = Number(cidr);
return Number.isInteger(cidrNum) && cidrNum >= 0 && cidrNum <= maxBits;
}
/**
* Validates IPv4 with optional CIDR suffix.
* @param {string} candidate
* @returns {boolean}
*/
function isValidIPv4(candidate) {
if (!IPV4_REGEX.test(candidate)) return false;
let [addr, cidr] = candidate.split("/");
let octets = addr.split(".").map(Number);
let ipv4Valid =
octets.length === 4 &&
octets.every((n) => Number.isInteger(n) && n >= 0 && n <= 255);
return ipv4Valid && isValidCidr(cidr, 32);
}
/**
* Validates IPv6 (full or compressed) with optional CIDR suffix.
* @param {string} candidate
* @returns {boolean}
*/
function isValidIPv6(candidate) {
if (!IPV6_REGEX.test(candidate) || !candidate.includes(":")) return false;
let [addr, cidr] = candidate.split("/");
if (!isValidCidr(cidr, 128)) return false;
if (addr.includes(":::")) return false;
if (addr.includes("::")) {
if ((addr.match(/::/g) || []).length > 1) return false;
let [left, right] = addr.split("::");
let leftParts = left ? left.split(":").filter(Boolean) : [];
let rightParts = right ? right.split(":").filter(Boolean) : [];
let hextets = leftParts.length + rightParts.length;
let validParts = [...leftParts, ...rightParts].every((part) =>
/^[\da-f]{1,4}$/i.test(part),
);
return validParts && hextets < 8;
}
let parts = addr.split(":");
return parts.length === 8 && parts.every((part) => /^[\da-f]{1,4}$/i.test(part));
}
/**
* Checks if a user is an IP/anon user, returning true if they are
* @param {string} user
* @returns {bool}
*/
function userIsAnon(user) {
const normalized = user.trim();
if (TEMP_ACCT_REGEX.test(normalized)) return true;
return isValidIPv4(normalized) || isValidIPv6(normalized);
}
// only fire on votes pages
let pageName = mw.config.values.wgPageName;
if (
pageName.startsWith("Wiktionary:Votes/") ||
pageName.startsWith("Wiktionary_talk:Votes/")
) {
(async () => {
// Step 1: find out when the vote ends
let endDate;
if (pageName.startsWith("Wiktionary_talk:")) {
let mainTitle = pageName.replace(/^Wiktionary_talk:/, "Wiktionary:");
let mainPageResp = await restAPI.get(
`/v1/page/${encodeURIComponent(mainTitle)}/html`,
);
let mainPageDoc = new DOMParser().parseFromString(
mainPageResp,
"text/html",
);
endDate = voteStart(mainPageDoc);
} else {
endDate = voteStart(document);
}
if (!(endDate instanceof Date) || isNaN(endDate)) {
console.warn(
"[vetvoters] Could not parse vote start date; skipping eligibility checks.",
);
return;
}
// Step 2: collect user elements, including anons
let userlinks = new Map(); // linkElem [HTMLElement] -> acctName [string]
Object.entries(USER_LINK_PATTERNS).forEach(([selector, normalize]) => {
document
.querySelectorAll(selector)
.forEach((link) => userlinks.set(link, normalize(link.title)));
});
for (let link of document.querySelectorAll(
`.mw-body-content [href^="//en.wiktionary.org/wiki/Special:Contributions/"]`,
)) {
let acct = link.title.replace(/^Special:Contributions\//, "");
if (!userIsAnon(acct)) continue;
userlinks.set(link, acct);
}
let usernames = [...new Set(userlinks.values())];
let queries = usernames.map(async (user) => {
try {
return [user, await verifyUser(user, endDate)];
} catch (err) {
console.warn(
`[vetvoters] Failed to verify ${user}; treating as eligible.`,
err,
);
return [user, true];
}
});
let userIsEligible = new Map(await Promise.all(queries));
for (let [elem, user] of userlinks) {
if (userIsEligible.get(user) === false) highlightIneligible(elem);
}
})();
}
9gnom4jfbcsvkwno0gcyo0t8wii1m9k
MediaWiki:Gadget-RhymesAdder.js
8
17998
88999
2025-09-17T22:26:11Z
en>Ioaxxere
0
[[mw:Migrating mw.Uri to URL]]
88999
javascript
text/javascript
/* {{documentation}}<nowiki>
Dependencies: ext.gadget.Editor,ext.gadget.LanguageUtils,mediawiki.util,mediawiki.Uri
To do:
* Make less ugly
* Ability to remove/edit rhymes
* Put rhyme template at the bottom of pronunciation section?
* Add qualifier button
*/
/*jshint maxerr:1048576, strict:true, undef:true, latedef:true, es5:true */
/*global mw, WikiXml, importScript, importScriptURI, $ */
var numberOfRhymesAdded = 0;
// Derived from [[User:Erutuon/scripts/templateParser.js]].
function parseTemplate(templateText) {
var parameters = new Object(null),
parameterRegex = /\|(?:([^=|}]+)=([^|}]+)|([^|}]+))/g,
template = new Object(null);
template.parameters = parameters;
var match,
index = 1,
name = templateText.match(/^{{([^|}]+)/);
if (name === null)
throw new Error("Invalid template: no name.");
template.name = name[1];
while ((match = parameterRegex.exec(templateText)) !== null) {
if (match[1]) {
parameters[match[1]] = match[2];
} else {
parameters[index++] = match[3];
}
}
return template;
}
var englishCardinalNumerals = [null,
'one', 'two', 'three', 'four', 'five',
'six', 'seven', 'eight', 'nine', 'ten',
'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen',
'sixteen', 'seventeen', 'eighteen', 'nineteen', 'twenty'
];
function extractNumberOfSyllables(h3text) {
var m = h3text.match(/(\S+) syllables?/);
if (!m) return null;
var word = m[1];
var index = englishCardinalNumerals.indexOf(word.toLowerCase());
return index > 0 ? index : null;
}
function hasRhymeTemplate(wikitext, languageCode, rhyme) {
// [[Template:rhyme]] redirects to [[Template:rhymes]].
// Simple regex is sufficient because there are no cases of {{rhymes}} with
// nested templates in the dump. (FIXME: Could change in the future.)
var match, rhymeTemplate = /{{rhymes?\|.+?}}/g;
while ((match = rhymeTemplate.exec(wikitext)) !== null) {
var template = parseTemplate(match[0]);
var templateLanguageCode = template.parameters.lang || template.parameters[1];
if (templateLanguageCode == languageCode) {
for (var i = template.parameters.lang ? 1 : 2; template.parameters[i]; ++i) {
if (template.parameters[i].trim() === rhyme)
return true;
}
}
}
return false;
}
function setUpRhymeAdderFor(h3, div) {
var findNumberOfHeaders = parseInt(new URL(h3.find("a").get(0).href).searchParams.get("section"));
var addNewRhymeForm;
var syllableCount = extractNumberOfSyllables(h3.text());
var syllableArg = syllableCount ? "|s=" + syllableCount : "";
editor = new Editor();
new AdderWrapper(editor, {
'createForm': function() {
return addNewRhymeForm =
$('<form>')
.append(
$('<label>+Add new rhyme: </label>')
.append($('<input>').attr("name", 'rhyme'))
)
.append($('<input>').attr('type', 'submit').val('Add'))[0];
},
'fields': {
'rhyme': function(txt, error) {
return txt || error('Please specify a rhyme.');
}
},
'onsubmit': function(values, render) {
var langname = mw.config.get("wgTitle").split('/')[0];
var lutils = new LanguageUtilsAsync();
lutils.GetWiktionaryCodeByCanonicalName(langname).then(function(langcode) {
$(addNewRhymeForm).find('input')[0].value = "";
var newItem = '{{l|' + langcode + '|' + values.rhyme + '}}';
render(newItem, function(newhtml) {
var addedRhymeNode = $("<li>").html(newhtml),
collator = window.Intl && new Intl.Collator( langcode, { "case": false } ),
sortFunction = collator ?
collator.compare :
function compare( a, b ) {
return a.toUpperCase() < b.toUpperCase() ? -1 : 1;
};
editor.addEdit({
'edit': function(wikitext) {
var wikixml = WikiXml.parseWikitext(wikitext);
var hdr = $(wikixml.find("section").get(findNumberOfHeaders - 1));
var headerData = hdr.find("data").get(0).textContent;
var rhymelistbegin_match = headerData.match(/{{(?:rhyme-top|rhyme list begin)\|?[0-9]?}}/);
var rhymelistbegin_index = rhymelistbegin_match.index + rhymelistbegin_match[0].length;
var rhymelistend_match = headerData.match(/{{(?:rhyme-bottom|rhyme list end)}}/);
var rhymelistend_index = rhymelistend_match.index;
var rhymesPrefix = headerData.substring(0, rhymelistbegin_index);
var rhymes = headerData.substring(rhymelistbegin_index, rhymelistend_index).trim().split(/\r?\n/);
var rhymesSuffix = headerData.substring(rhymelistend_index);
rhymes.push("* " + newItem);
var canRearrange = true;
var sortableLines = rhymes.map(function(line) {
var m;
m = line.match(/^\* ?{{l\|[a-zA-Z]{2,3}\|(.+)$/);
if (m && m[1]) return [m[1], line];
m = line.match(/^\* ?\[\[(.+)$/);
if (m && m[1]) return [m[1], line];
canRearrange = false;
return null;
});
console.trace("canRearrange is " + canRearrange);
if (canRearrange) {
sortableLines.sort( function(a,b) { return sortFunction(a[0], b[0]);} );
rhymes = sortableLines.map(function(sortableLine) {
var match = sortableLine[1].match(/^\* ?\[\[(.+)]\]$/);
if (match && match[1]) return "* {{l|" + langcode + "|" + match[1] + "}}";
else return sortableLine[1];
});
}
$(hdr.find("data").get(0)).text(rhymesPrefix + "\n" + rhymes.join("\n") + "\n" + rhymesSuffix);
return WikiXml.toWikitext(wikixml);
},
'redo': function() {
div.find("ul").append(addedRhymeNode);
div.find("li").sort(function(a, b) {
return sortFunction( $(a).text(), $(b).text() );
}).appendTo(div.find("ul"));
},
'undo': function() {
addedRhymeNode.remove();
},
'summary': "+rhyme [[" + values.rhyme + "]]",
'after_save': function() {
numberOfRhymesAdded++;
document.body.style.cursor = 'wait';
new mw.Api().edit(values.rhyme, function(text) {
text = text.content;
var rhymehome = mw.config.get("wgTitle").split("/")[1];
if (hasRhymeTemplate(text, langcode, rhymehome)) {
document.body.style.cursor = --numberOfRhymesAdded ? 'wait' : '';
return $.Deferred().reject('rhyme already on page');
}
if (text.match('==' + langname + '==') && !text.match("\\{\\{rhymes?(\\|" + langcode + "\\|" + mw.util.escapeRegExp(rhymehome) + "|\\|" + mw.util.escapeRegExp(rhymehome) + "\\|" + langcode + "|\\|" + mw.util.escapeRegExp(rhymehome) + ")\\}\\}")) {
// If the current L2 section does not contain a pronunciation section, create the header.
if (!String(text.match("==" + langname + "==[\\s\\S]*?(\n==[^=]|$)")).match("=Pronunciation=")) {
text = text.replace(RegExp("(==" + langname + "==[\\s\\S]*?(?=\n=+(?=[^=])(?!(Etymology=|Alternative))))"), "$1\n===Pronunciation===\n");
}
// Add the rhymes template under the pronunciation header.
text = text.replace(
RegExp('(==' + langname + '==[\\s\\S]*?=+Pronunciation=+[\\s\\S]*?\n(?![\:\*]))'),
'$1* {{rhymes|' + langcode + "|" + rhymehome + syllableArg + '}}\n'
);
}
if (text) return {
text: text,
summary: "+rhyme [[" + mw.config.get("wgPageName") + "|" + "-" + rhymehome + "]]"
};
else {
document.body.style.cursor = --numberOfRhymesAdded ? 'wait' : '';
return $.Deferred().reject('problem text is false');
}
}).then(function() {
document.body.style.cursor = --numberOfRhymesAdded ? 'wait' : '';
});
}
}, addedRhymeNode[ 0 ]);
});
});
}
}, h3.parent()[0], div[0]);
}
function setUpRhymeAdder() {
$(".rhymes-list-begin").each(function() {
var div = $(this);
if (div.prev().text().indexOf("syllable") != -1) {
try {
setUpRhymeAdderFor(div.prev(), $(this));
} catch (e) {
console.log(e);
}
}
});
}
if (mw.config.get('wgNamespaceNumber') === 106 && mw.config.get("wgAction") == "view")
$.getScript("//en.wiktionary.org/w/index.php?title=User:Dixtosa/XMLize.js&action=raw", function() {
$(setUpRhymeAdder);
});
// </nowiki>
bx1tn1mlyjr632ayerjvvzbzv3t34k0
89000
88999
2026-08-06T23:32:47Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-RhymesAdder.js]]
88999
javascript
text/javascript
/* {{documentation}}<nowiki>
Dependencies: ext.gadget.Editor,ext.gadget.LanguageUtils,mediawiki.util,mediawiki.Uri
To do:
* Make less ugly
* Ability to remove/edit rhymes
* Put rhyme template at the bottom of pronunciation section?
* Add qualifier button
*/
/*jshint maxerr:1048576, strict:true, undef:true, latedef:true, es5:true */
/*global mw, WikiXml, importScript, importScriptURI, $ */
var numberOfRhymesAdded = 0;
// Derived from [[User:Erutuon/scripts/templateParser.js]].
function parseTemplate(templateText) {
var parameters = new Object(null),
parameterRegex = /\|(?:([^=|}]+)=([^|}]+)|([^|}]+))/g,
template = new Object(null);
template.parameters = parameters;
var match,
index = 1,
name = templateText.match(/^{{([^|}]+)/);
if (name === null)
throw new Error("Invalid template: no name.");
template.name = name[1];
while ((match = parameterRegex.exec(templateText)) !== null) {
if (match[1]) {
parameters[match[1]] = match[2];
} else {
parameters[index++] = match[3];
}
}
return template;
}
var englishCardinalNumerals = [null,
'one', 'two', 'three', 'four', 'five',
'six', 'seven', 'eight', 'nine', 'ten',
'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen',
'sixteen', 'seventeen', 'eighteen', 'nineteen', 'twenty'
];
function extractNumberOfSyllables(h3text) {
var m = h3text.match(/(\S+) syllables?/);
if (!m) return null;
var word = m[1];
var index = englishCardinalNumerals.indexOf(word.toLowerCase());
return index > 0 ? index : null;
}
function hasRhymeTemplate(wikitext, languageCode, rhyme) {
// [[Template:rhyme]] redirects to [[Template:rhymes]].
// Simple regex is sufficient because there are no cases of {{rhymes}} with
// nested templates in the dump. (FIXME: Could change in the future.)
var match, rhymeTemplate = /{{rhymes?\|.+?}}/g;
while ((match = rhymeTemplate.exec(wikitext)) !== null) {
var template = parseTemplate(match[0]);
var templateLanguageCode = template.parameters.lang || template.parameters[1];
if (templateLanguageCode == languageCode) {
for (var i = template.parameters.lang ? 1 : 2; template.parameters[i]; ++i) {
if (template.parameters[i].trim() === rhyme)
return true;
}
}
}
return false;
}
function setUpRhymeAdderFor(h3, div) {
var findNumberOfHeaders = parseInt(new URL(h3.find("a").get(0).href).searchParams.get("section"));
var addNewRhymeForm;
var syllableCount = extractNumberOfSyllables(h3.text());
var syllableArg = syllableCount ? "|s=" + syllableCount : "";
editor = new Editor();
new AdderWrapper(editor, {
'createForm': function() {
return addNewRhymeForm =
$('<form>')
.append(
$('<label>+Add new rhyme: </label>')
.append($('<input>').attr("name", 'rhyme'))
)
.append($('<input>').attr('type', 'submit').val('Add'))[0];
},
'fields': {
'rhyme': function(txt, error) {
return txt || error('Please specify a rhyme.');
}
},
'onsubmit': function(values, render) {
var langname = mw.config.get("wgTitle").split('/')[0];
var lutils = new LanguageUtilsAsync();
lutils.GetWiktionaryCodeByCanonicalName(langname).then(function(langcode) {
$(addNewRhymeForm).find('input')[0].value = "";
var newItem = '{{l|' + langcode + '|' + values.rhyme + '}}';
render(newItem, function(newhtml) {
var addedRhymeNode = $("<li>").html(newhtml),
collator = window.Intl && new Intl.Collator( langcode, { "case": false } ),
sortFunction = collator ?
collator.compare :
function compare( a, b ) {
return a.toUpperCase() < b.toUpperCase() ? -1 : 1;
};
editor.addEdit({
'edit': function(wikitext) {
var wikixml = WikiXml.parseWikitext(wikitext);
var hdr = $(wikixml.find("section").get(findNumberOfHeaders - 1));
var headerData = hdr.find("data").get(0).textContent;
var rhymelistbegin_match = headerData.match(/{{(?:rhyme-top|rhyme list begin)\|?[0-9]?}}/);
var rhymelistbegin_index = rhymelistbegin_match.index + rhymelistbegin_match[0].length;
var rhymelistend_match = headerData.match(/{{(?:rhyme-bottom|rhyme list end)}}/);
var rhymelistend_index = rhymelistend_match.index;
var rhymesPrefix = headerData.substring(0, rhymelistbegin_index);
var rhymes = headerData.substring(rhymelistbegin_index, rhymelistend_index).trim().split(/\r?\n/);
var rhymesSuffix = headerData.substring(rhymelistend_index);
rhymes.push("* " + newItem);
var canRearrange = true;
var sortableLines = rhymes.map(function(line) {
var m;
m = line.match(/^\* ?{{l\|[a-zA-Z]{2,3}\|(.+)$/);
if (m && m[1]) return [m[1], line];
m = line.match(/^\* ?\[\[(.+)$/);
if (m && m[1]) return [m[1], line];
canRearrange = false;
return null;
});
console.trace("canRearrange is " + canRearrange);
if (canRearrange) {
sortableLines.sort( function(a,b) { return sortFunction(a[0], b[0]);} );
rhymes = sortableLines.map(function(sortableLine) {
var match = sortableLine[1].match(/^\* ?\[\[(.+)]\]$/);
if (match && match[1]) return "* {{l|" + langcode + "|" + match[1] + "}}";
else return sortableLine[1];
});
}
$(hdr.find("data").get(0)).text(rhymesPrefix + "\n" + rhymes.join("\n") + "\n" + rhymesSuffix);
return WikiXml.toWikitext(wikixml);
},
'redo': function() {
div.find("ul").append(addedRhymeNode);
div.find("li").sort(function(a, b) {
return sortFunction( $(a).text(), $(b).text() );
}).appendTo(div.find("ul"));
},
'undo': function() {
addedRhymeNode.remove();
},
'summary': "+rhyme [[" + values.rhyme + "]]",
'after_save': function() {
numberOfRhymesAdded++;
document.body.style.cursor = 'wait';
new mw.Api().edit(values.rhyme, function(text) {
text = text.content;
var rhymehome = mw.config.get("wgTitle").split("/")[1];
if (hasRhymeTemplate(text, langcode, rhymehome)) {
document.body.style.cursor = --numberOfRhymesAdded ? 'wait' : '';
return $.Deferred().reject('rhyme already on page');
}
if (text.match('==' + langname + '==') && !text.match("\\{\\{rhymes?(\\|" + langcode + "\\|" + mw.util.escapeRegExp(rhymehome) + "|\\|" + mw.util.escapeRegExp(rhymehome) + "\\|" + langcode + "|\\|" + mw.util.escapeRegExp(rhymehome) + ")\\}\\}")) {
// If the current L2 section does not contain a pronunciation section, create the header.
if (!String(text.match("==" + langname + "==[\\s\\S]*?(\n==[^=]|$)")).match("=Pronunciation=")) {
text = text.replace(RegExp("(==" + langname + "==[\\s\\S]*?(?=\n=+(?=[^=])(?!(Etymology=|Alternative))))"), "$1\n===Pronunciation===\n");
}
// Add the rhymes template under the pronunciation header.
text = text.replace(
RegExp('(==' + langname + '==[\\s\\S]*?=+Pronunciation=+[\\s\\S]*?\n(?![\:\*]))'),
'$1* {{rhymes|' + langcode + "|" + rhymehome + syllableArg + '}}\n'
);
}
if (text) return {
text: text,
summary: "+rhyme [[" + mw.config.get("wgPageName") + "|" + "-" + rhymehome + "]]"
};
else {
document.body.style.cursor = --numberOfRhymesAdded ? 'wait' : '';
return $.Deferred().reject('problem text is false');
}
}).then(function() {
document.body.style.cursor = --numberOfRhymesAdded ? 'wait' : '';
});
}
}, addedRhymeNode[ 0 ]);
});
});
}
}, h3.parent()[0], div[0]);
}
function setUpRhymeAdder() {
$(".rhymes-list-begin").each(function() {
var div = $(this);
if (div.prev().text().indexOf("syllable") != -1) {
try {
setUpRhymeAdderFor(div.prev(), $(this));
} catch (e) {
console.log(e);
}
}
});
}
if (mw.config.get('wgNamespaceNumber') === 106 && mw.config.get("wgAction") == "view")
$.getScript("//en.wiktionary.org/w/index.php?title=User:Dixtosa/XMLize.js&action=raw", function() {
$(setUpRhymeAdder);
});
// </nowiki>
bx1tn1mlyjr632ayerjvvzbzv3t34k0
MediaWiki:Gadget-PatrollingEnhancements.js
8
17999
89001
2025-09-17T01:04:55Z
en>Ioaxxere
0
Polyfill deprecated function newNode
89001
javascript
text/javascript
// {{documentation}}
// ******** imported from [[MediaWiki:Gadget-LegacyScriptsNewNode.js]] ********
var newNode = window.newNode = function newNode(tagname) {
var node = document.createElement(tagname);
for (var i = 1; i < arguments.length; i++) {
var argument = arguments[i];
if (typeof argument == 'string') { //Text
node.appendChild(document.createTextNode(argument));
} else if (typeof argument == 'object') {
if (argument instanceof Node) { // If it is a DOM Node
node.appendChild(argument);
} else { // Attributes (hopefully)
for (var j in argument) {
if (j === 'class') { // Classname different because...
node.className = argument[j];
} else if (j == 'style') { // Style is special
node.style.cssText = argument[j];
} else if (typeof argument[j] == 'function') { // Basic event handlers
node.addEventListener(j, argument[j], false);
} else {
node.setAttribute(j, argument[j]); //Normal attributes
}
}
}
}
}
return node;
};
// ****************************************************************************
(function PatrollingEnhancements_IIFE() {
window.GPE = typeof window.GPE == 'object' ? window.GPE : {};
const GPE = window.GPE;
/* </pre>
==Configuration options==
<pre> */
// The initial value to put in the "deletion reason" text-field; you can
// override this in your common.js (or vector.js or whatnot).
GPE.initialDeleteReason = GPE.initialDeleteReason == undefined ? '' : GPE.initialDeleteReason;
// The value to use as a deletion reason if you leave the text-field blank; you
// can override it in your common.js (or vector.js or whatnot). If you *don't*
// override this, then MediaWiki will generate an automatic deletion reason that
// indicates the entry's last editor and the beginning of its content.
GPE.deleteReasonIfBlank = GPE.deleteReasonIfBlank == undefined ? '' : GPE.deleteReasonIfBlank;
// By DCDuring's request. If you set this to true, then Special:Watchlist will
// show the deletion-reason text-input, but *not* the deletion-reason dropdown,
// when there's an unpatrolled new-page-creation.
GPE.hideDeleteReasonDropdownOnWatchlist = GPE.hideDeleteReasonDropdownOnWatchlist == undefined ? false : GPE.hideDeleteReasonDropdownOnWatchlist;
/* </pre>
==Automated patrolling (whitelisting)==
<pre> */
// set GPE.currMonth and GPE.lastMonth (in the form of, e.g., '2013/February')
(
function ()
{
var monthNames = [ 'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November',
'December' ];
var now = new Date();
var currYear = now.getFullYear();
var currMonthNum = now.getMonth();
GPE.currMonth = currYear + '/' + monthNames[currMonthNum];
var lastYear = (currMonthNum == 0 ? currYear - 1 : currYear);
var lastMonthNum = (currMonthNum == 0 ? 11 : currMonthNum - 1);
GPE.lastMonth = lastYear + '/' + monthNames[lastMonthNum];
}
)();
GPE.individualWhiteListedPages =
{
"Wiktionary:Requests for cleanup": true,
"Wiktionary:Requests for verification": true,
"Wiktionary:Requests for deletion": true,
"Wiktionary:Requests for deletion/Others": true,
"Wiktionary:Requests for moves, mergers and splits": true,
"Wiktionary:Information desk": true,
"Wiktionary:Tea room": true,
"Wiktionary:Etymology scriptorium": true,
"Wiktionary:Requested entries (English)": true,
"Wiktionary:Requested entries (Spanish)": true,
"Wiktionary:List of protologisms": true,
"Wiktionary:Translation requests": true,
"Wiktionary:Feedback": true,
"Wiktionary:Sandbox": true,
"Wiktionary talk:Sandbox": true,
"Wiktionary:Tutorial (Editing)/sandbox": true,
"Wiktionary:Featured word candidates": true,
"Wiktionary:Word of the day/Nominations": true
};
GPE.individualWhiteListedPages["Wiktionary:Beer parlour/" + GPE.currMonth] =
GPE.individualWhiteListedPages["Wiktionary:Beer parlour/" + GPE.lastMonth] =
GPE.individualWhiteListedPages["Wiktionary:Grease pit/" + GPE.currMonth] =
GPE.individualWhiteListedPages["Wiktionary:Grease pit/" + GPE.lastMonth] =
true;
// per-user white-listed sub-pages (for example, edits by user Foo
// to User:Foo/vector.js should be autopatrolled):
GPE.perUserWhiteListedSubPages =
{
"/Sandbox": true,
"/sandbox": true,
"/chick.js": true,
"/chick.css": true,
"/standard.js": true,
"/standard.css": true,
"/cologneblue.js": true,
"/cologneblue.css": true,
"/modern.js": true,
"/modern.css": true,
"/myskin.js": true,
"/myskin.css": true,
"/nostalgia.js": true,
"/nostalgia.css": true,
"/simple.js": true,
"/simple.css": true,
"/vector.js": true,
"/vector.css": true,
"/common.js": true,
"/common.css": true
};
GPE.individualWhiteListedContributors =
{
};
GPE.shouldAutoPatrol = function(link)
{
var pagename = link.title;
if(pagename.indexOf('User talk:') == 0)
return true;
if(pagename in GPE.individualWhiteListedPages)
return true;
var contributor;
if(mediaWiki.config.get('wgCanonicalSpecialPageName') === 'Contributions')
{
contributor =
document.getElementById('t-contributions')
.getElementsByTagName('a')[0].href.replace(/.*\//, '');
}
else
{
var li = link.parentNode;
if(li.tagName.toUpperCase() == 'SPAN')
li = li.parentNode;
var links = li.getElementsByTagName('a');
for(var i = 0; i < links.length; ++i)
if(links[i].title.indexOf('Special:Contributions/') == 0)
{
contributor = links[i].title.substr('Special:Contributions/'.length);
break;
}
}
if(pagename.indexOf('User:' + contributor + '/') == 0)
if(pagename.substr(contributor.length + 5) in GPE.perUserWhiteListedSubPages)
return true;
if(contributor in GPE.individualWhiteListedContributors)
return true;
return false;
};
/* </pre>
==Utility functions==
<pre> */
GPE.newButton = function(text, color, hoverText)
{
var button = newNode('button', text);
button.style.background = color;
button.style.color = '#FFF';
button.style.border = '0';
button.style.padding = '0';
button.style.cursor = 'pointer';
button.title = hoverText;
return button;
};
GPE.disableButton = function(button, text, hoverText)
{
button.onclick = null;
button.title = (hoverText || '');
button.innerHTML = text;
// clear out explicit styling and disable, so we can get appropriate
// disabled-button styles:
button.style.background = '';
button.style.color = '';
button.style.cursor = '';
button.disabled = 'disabled';
};
/* </pre>
==Individual patrol-buttons==
<pre> */
GPE.addPatrolButton = function(link, rcid)
{
if(link.className.search(/(?:^|\s)gpe-hasPatrolButton(?:\s|$)/) > -1)
return;
link.className = (link.className + ' gpe-hasPatrolButton').trim();
var button = GPE.newButton('M', '#009', 'click to mark as patrolled');
link.parentNode.insertBefore(button, link.nextSibling);
link.parentNode.insertBefore(document.createTextNode(' · '), button);
button.onclick =
function ()
{
var token = mediaWiki.user.tokens.get('patrolToken');
$.post
(
'/w/api.php?format=json&action=patrol&assert=user',
{ token: token, rcid: rcid },
function (data)
{
if(data.patrol)
GPE.disableButton(button, 'm', 'marked as patrolled');
else if(data.error)
{
var msg = data.error.code + ': ' + data.error.info;
if(data.error.code == 'badtoken')
msg += ': "' + token + '"';
alert(msg);
}
},
'json'
);
};
if(GPE.shouldAutoPatrol(link))
button.click();
// remove the exclamation point:
var tmp = link;
while(tmp && tmp.nodeName.toUpperCase() !== 'LI')
tmp = tmp.parentNode;
if(tmp)
tmp = tmp.getElementsByClassName('unpatrolled')[0];
if(tmp)
tmp.parentNode.removeChild(tmp);
};
/* </pre>
==Individual delete-buttons==
<pre> */
GPE.addDeleteButton = function(link, title)
{
if(link.className.search(/(?:^|\s)gpe-hasDeleteButton(?:\s|$)/) > -1)
return;
link.className = (link.className + ' gpe-hasDeleteButton').trim();
var button = GPE.newButton('D', '#900', 'click to delete');
link.parentNode.insertBefore(button, link.nextSibling);
link.parentNode.insertBefore(document.createTextNode(' · '), button);
button.onclick =
function ()
{
var dropdownReason =
document.getElementById('deleteReasonsDropdown')
? document.getElementById('deleteReasonsDropdown').value
: '';
if(dropdownReason == 'other')
dropdownReason = '';
var textInputReason =
document.getElementById('deleteReasonTextInput').value;
var reason;
if(dropdownReason.length && textInputReason.length)
reason = dropdownReason + ': ' + textInputReason;
else if(dropdownReason.length || textInputReason.length)
reason = dropdownReason + textInputReason;
else
reason = GPE.deleteReasonIfBlank;
var token = mediaWiki.user.tokens.get('deleteToken');
$.post
(
'/w/api.php?format=json&action=delete&assert=user',
{ title: title, token: token, reason: reason },
function (data)
{
if(data['delete'])
GPE.disableButton(button, 'd', 'deleted');
else if(data.error)
{
var msg = data.error.code + ': ' + data.error.info;
if(data.error.code == 'badtoken')
msg += ': "' + token + '"';
alert(msg);
}
},
'json'
);
};
};
/* </pre>
==Delete-reasons==
<pre> */
GPE.addDeleteReasonInput = function ()
{ var deleteReasonDiv =
( newNode
( 'div',
{ style:
'background:#900; color:#FFF; ' +
'position:fixed; bottom:0; right:0; margin-bottom:0'
},
'\u00A0Deletion reason:\u00A0'
)
);
deleteReasonDiv.title =
'the deletion reason (message/summary) to use when you click "D"';
var deleteReasonTextInput =
( newNode
( 'input',
{ type: 'text',
size: 80,
id: 'deleteReasonTextInput',
value: GPE.initialDeleteReason,
style: 'position:fixed; right:0; margin-bottom:0'
}
)
);
deleteReasonDiv.appendChild(deleteReasonTextInput);
document.getElementById('bodyContent').appendChild(deleteReasonDiv);
if(GPE.hideDeleteReasonDropdownOnWatchlist)
if(mediaWiki.config.get('wgPageName') == 'Special:Watchlist')
return;
// get canned messages from [[MediaWiki:Deletereason-dropdown]]:
$.getJSON
( '/w/api.php?format=json&action=query&meta=allmessages&ammessages=Deletereason-dropdown',
function (data)
{ var rawDeleteReasons = data.query.allmessages[0]['*'];
var deleteReasonsDropdown =
newNode('select',
{ id: 'deleteReasonsDropdown', style: 'vertical-align: bottom' });
deleteReasonsDropdown.appendChild
(newNode('option', { value: 'other' }, 'Other reason'));
var optGroup = deleteReasonsDropdown;
rawDeleteReasons.replace
( /^(\*\*?) *(.+)$/gm,
function (s, asterisks, text)
{ if(asterisks == '*')
deleteReasonsDropdown.appendChild
(optGroup = newNode('optgroup', { label: text }));
else // '**'
optGroup.appendChild(newNode('option', { value: text }, text));
}
);
deleteReasonDiv.insertBefore(deleteReasonsDropdown, deleteReasonTextInput);
deleteReasonDiv.insertBefore(newNode('br'), deleteReasonTextInput);
deleteReasonDiv.insertBefore(document.createTextNode('\u00A0'), deleteReasonTextInput);
}
);
};
/* </pre>
==Namespaces==
<pre> */
GPE.computeNamespaces = function
(selected, includeAssociated, invertSelection)
{
var associated = Number(selected) + (selected % 2 === 0 ? 1 : -1);
if(invertSelection)
{
var selector = document.getElementById('namespace');
if(! selector)
return [];
var ret = [];
for(var option = selector.firstChild; option; option = option.nextSibling)
if(option.nodeName.toUpperCase() === 'OPTION' && option.value)
if(option.value != selected)
if(! includeAssociated || option.value != associated)
ret.push(option.value);
return ret;
}
else
{
if(includeAssociated)
return [selected, associated];
else
return [selected];
}
};
GPE.generateRcnamespace = function ()
{
var currUrl = document.location.href;
if(! /[?&]namespace=\d+(?:&|$)/.test(currUrl))
return;
var selected = /[?&]namespace=(\d+)(?:&|$)/.exec(currUrl)[1];
var includeAssociated =
mediaWiki.config.get('wgPageName') !== 'Special:NewPages'
&& /[?&]associated=(?!0?&|0?$)/.test(currUrl);
var invertSelection = /[?&]invert=(?!0?&|0?$)/.test(currUrl);
var namespaces =
GPE.computeNamespaces(selected, includeAssociated, invertSelection);
if(namespaces.length > 0)
return namespaces.join('|');
};
/* </pre>
==Find and handle links==
<pre> */
GPE.handleUnpatrolledEdits = function (rcidsByRevid)
{
var links =
document.getElementById('bodyContent').getElementsByTagName('a');
for(var i = links.length - 1; i >= 0; --i)
{
var mapKey = /&diff=(prev&oldid=)?(\d+)(&|$)/.exec(links[i].href);
if(mapKey && rcidsByRevid.hasOwnProperty(mapKey[2]))
GPE.addPatrolButton(links[i], rcidsByRevid[mapKey[2]]);
}
};
GPE.findLinksToUnpatrolledNewPages = function (rcidsByTitle)
{
if(mediaWiki.config.get('wgPageName') === 'Special:NewPages')
{
var ret = [];
$('li.not-patrolled a.mw-newpages-pagename').each(function () {
if (this.title && rcidsByTitle.hasOwnProperty(this.title))
ret.push(this);
});
return ret;
}
else
{
var ret = [];
var abbrs =
document.getElementById('bodyContent').getElementsByTagName('abbr');
for(var i = abbrs.length - 1; i >= 0; --i)
{
if(abbrs[i].className != 'newpage')
continue;
var link = abbrs[i];
while(link && link.nodeName.toUpperCase() != 'A')
if(link.nodeName.toUpperCase() === 'SPAN' && link.className === 'mw-title')
link = link.firstChild;
else
link = link.nextSibling;
if(link && link.title && rcidsByTitle.hasOwnProperty(link.title))
ret.push(link);
}
return ret;
}
};
GPE.handleUnpatrolledNewPages = function (rcidsByTitle)
{
var userIsSysop =
mediaWiki.config.get('wgUserGroups').indexOf('sysop') > -1;
var links = GPE.findLinksToUnpatrolledNewPages(rcidsByTitle);
for(var i = links.length - 1; i >= 0; --i)
{
var link = links[i];
// 2016-04: Equinox removing red D delete button because it hasn't worked for a year.
// if(userIsSysop)
// GPE.addDeleteButton(link, link.title);
GPE.addPatrolButton(link, rcidsByTitle[link.title]);
}
// Remove the rest of the delete features as above
// if(userIsSysop && links.length > 0)
// {
// GPE.getAndStoreDeleteToken();
// GPE.addDeleteReasonInput();
// }
};
GPE.getAndStoreDeleteToken = function ()
{
$.getJSON
(
'/w/api.php?format=json&action=tokens&type=delete',
function (data)
{
var token = data.tokens.deletetoken;
if(! token || token.search(/^[0-9a-f]{32}\+\\$/) != 0)
return;
mediaWiki.user.tokens.set('deleteToken', token);
}
);
};
GPE.main = function (params)
{
var url =
'/w/api.php?format=json&action=query&list=recentchanges' +
'&rcprop=ids|title' +
'&rcshow=!patrolled' + (params.hasOwnProperty('rcshow') ? '|' + params.rcshow : '') +
'&rclimit=' + (params.hasOwnProperty('rclimit') ? params.rclimit : 500) +
'&rctype=' + (params.hasOwnProperty('rctype') ? params.rctype : 'edit|new') +
(params.hasOwnProperty('rcdir') ? '&rcdir=' + params.rcdir : '') +
(params.hasOwnProperty('rcstart') ? '&rcstart=' + params.rcstart : '') +
(params.hasOwnProperty('rcnamespace') ? '&rcnamespace=' + params.rcnamespace : '') +
(params.hasOwnProperty('rcuser') ? '&rcuser=' + params.rcuser : '');
$.getJSON
(
url,
function (data)
{
data = data.query.recentchanges;
var rcidsByRevid = {}; // for unpatrolled edits
var rcidsByTitle = {}; // for unpatrolled new pages
for(var i = 0; i < data.length; ++i)
if(data[i].type == 'edit')
rcidsByRevid[data[i].revid] = data[i].rcid;
else
rcidsByTitle[data[i].title] = data[i].rcid;
GPE.handleUnpatrolledEdits(rcidsByRevid);
GPE.handleUnpatrolledNewPages(rcidsByTitle);
}
);
};
/* </pre>
==Onload-hooks==
<pre> */
$( document ).ready
( function ()
{
if(mediaWiki.config.get('wgPageName') === 'Special:RecentChanges') {
var currUrl = document.location.href;
var params = {};
var rcshow = [];
if(currUrl.search(/[?&]hideliu=(?!0?$|0?&)/) > -1)
rcshow.push('anon');
else if(currUrl.search(/[?&]hideanons=(?!0?$|0?&)/) > -1)
rcshow.push('!anon');
if(document.getElementsByClassName('minoredit').length === 0)
rcshow.push('!minor');
if(rcshow.length > 0)
params.rcshow = rcshow.join('|');
var rcnamespace = GPE.generateRcnamespace();
if(rcnamespace)
params.rcnamespace = rcnamespace;
GPE.main(params);
} else if(mediaWiki.config.get('wgPageName') === 'Special:NewPages') {
var currUrl = document.location.href;
var params = { rctype: 'new' };
var rcshow = [];
if(currUrl.search(/[?&]hideliu=(?!0?$|0?&)/) > -1)
rcshow.push('anon');
if(currUrl.search(/[?&]hideredirs=0?(?:$|&)/) === -1)
rcshow.push('!redirect');
if(rcshow.length > 0)
params.rcshow = rcshow.join('|');
if(currUrl.search(/[?&]dir=prev(?=$|&)/) > -1) {
params.rcdir = 'newer';
if(currUrl.search(/[?&]offset=\d+(?=$|&)/) > -1)
params.rcstart = currUrl.match(/[?&]offset=(\d+)(?=$|&)/)[1];
}
var rcnamespace = GPE.generateRcnamespace();
if(rcnamespace)
params.rcnamespace = rcnamespace;
GPE.main(params);
} else if(mediaWiki.config.get('wgPageName') === 'Special:Watchlist') {
var params = {};
var rcnamespace = GPE.generateRcnamespace();
if(rcnamespace)
params.rcnamespace = rcnamespace;
// TODO is this the best way to find what we need for the watchlist?
GPE.main(params);
} else if(mediaWiki.config.get('wgPageName').search(/^Special:Contributions(\/|$)/) === 0)
GPE.main({ rcuser: document.getElementById('t-contributions').firstChild.href.replace(/^.*?\/Special:Contributions\//, '') });
else if(mediaWiki.config.get('wgAction') === 'markpatrolled'
|| mediaWiki.config.get('wgAction') === 'delete'
|| mediaWiki.config.get('wgAction') === 'rollback')
GPE.main({ rclimit: 15 });
}
);
})();
g9g0tszn8uzicki57h3lrynnuqr2imv
89002
89001
2026-08-06T23:32:55Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-PatrollingEnhancements.js]]
89001
javascript
text/javascript
// {{documentation}}
// ******** imported from [[MediaWiki:Gadget-LegacyScriptsNewNode.js]] ********
var newNode = window.newNode = function newNode(tagname) {
var node = document.createElement(tagname);
for (var i = 1; i < arguments.length; i++) {
var argument = arguments[i];
if (typeof argument == 'string') { //Text
node.appendChild(document.createTextNode(argument));
} else if (typeof argument == 'object') {
if (argument instanceof Node) { // If it is a DOM Node
node.appendChild(argument);
} else { // Attributes (hopefully)
for (var j in argument) {
if (j === 'class') { // Classname different because...
node.className = argument[j];
} else if (j == 'style') { // Style is special
node.style.cssText = argument[j];
} else if (typeof argument[j] == 'function') { // Basic event handlers
node.addEventListener(j, argument[j], false);
} else {
node.setAttribute(j, argument[j]); //Normal attributes
}
}
}
}
}
return node;
};
// ****************************************************************************
(function PatrollingEnhancements_IIFE() {
window.GPE = typeof window.GPE == 'object' ? window.GPE : {};
const GPE = window.GPE;
/* </pre>
==Configuration options==
<pre> */
// The initial value to put in the "deletion reason" text-field; you can
// override this in your common.js (or vector.js or whatnot).
GPE.initialDeleteReason = GPE.initialDeleteReason == undefined ? '' : GPE.initialDeleteReason;
// The value to use as a deletion reason if you leave the text-field blank; you
// can override it in your common.js (or vector.js or whatnot). If you *don't*
// override this, then MediaWiki will generate an automatic deletion reason that
// indicates the entry's last editor and the beginning of its content.
GPE.deleteReasonIfBlank = GPE.deleteReasonIfBlank == undefined ? '' : GPE.deleteReasonIfBlank;
// By DCDuring's request. If you set this to true, then Special:Watchlist will
// show the deletion-reason text-input, but *not* the deletion-reason dropdown,
// when there's an unpatrolled new-page-creation.
GPE.hideDeleteReasonDropdownOnWatchlist = GPE.hideDeleteReasonDropdownOnWatchlist == undefined ? false : GPE.hideDeleteReasonDropdownOnWatchlist;
/* </pre>
==Automated patrolling (whitelisting)==
<pre> */
// set GPE.currMonth and GPE.lastMonth (in the form of, e.g., '2013/February')
(
function ()
{
var monthNames = [ 'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November',
'December' ];
var now = new Date();
var currYear = now.getFullYear();
var currMonthNum = now.getMonth();
GPE.currMonth = currYear + '/' + monthNames[currMonthNum];
var lastYear = (currMonthNum == 0 ? currYear - 1 : currYear);
var lastMonthNum = (currMonthNum == 0 ? 11 : currMonthNum - 1);
GPE.lastMonth = lastYear + '/' + monthNames[lastMonthNum];
}
)();
GPE.individualWhiteListedPages =
{
"Wiktionary:Requests for cleanup": true,
"Wiktionary:Requests for verification": true,
"Wiktionary:Requests for deletion": true,
"Wiktionary:Requests for deletion/Others": true,
"Wiktionary:Requests for moves, mergers and splits": true,
"Wiktionary:Information desk": true,
"Wiktionary:Tea room": true,
"Wiktionary:Etymology scriptorium": true,
"Wiktionary:Requested entries (English)": true,
"Wiktionary:Requested entries (Spanish)": true,
"Wiktionary:List of protologisms": true,
"Wiktionary:Translation requests": true,
"Wiktionary:Feedback": true,
"Wiktionary:Sandbox": true,
"Wiktionary talk:Sandbox": true,
"Wiktionary:Tutorial (Editing)/sandbox": true,
"Wiktionary:Featured word candidates": true,
"Wiktionary:Word of the day/Nominations": true
};
GPE.individualWhiteListedPages["Wiktionary:Beer parlour/" + GPE.currMonth] =
GPE.individualWhiteListedPages["Wiktionary:Beer parlour/" + GPE.lastMonth] =
GPE.individualWhiteListedPages["Wiktionary:Grease pit/" + GPE.currMonth] =
GPE.individualWhiteListedPages["Wiktionary:Grease pit/" + GPE.lastMonth] =
true;
// per-user white-listed sub-pages (for example, edits by user Foo
// to User:Foo/vector.js should be autopatrolled):
GPE.perUserWhiteListedSubPages =
{
"/Sandbox": true,
"/sandbox": true,
"/chick.js": true,
"/chick.css": true,
"/standard.js": true,
"/standard.css": true,
"/cologneblue.js": true,
"/cologneblue.css": true,
"/modern.js": true,
"/modern.css": true,
"/myskin.js": true,
"/myskin.css": true,
"/nostalgia.js": true,
"/nostalgia.css": true,
"/simple.js": true,
"/simple.css": true,
"/vector.js": true,
"/vector.css": true,
"/common.js": true,
"/common.css": true
};
GPE.individualWhiteListedContributors =
{
};
GPE.shouldAutoPatrol = function(link)
{
var pagename = link.title;
if(pagename.indexOf('User talk:') == 0)
return true;
if(pagename in GPE.individualWhiteListedPages)
return true;
var contributor;
if(mediaWiki.config.get('wgCanonicalSpecialPageName') === 'Contributions')
{
contributor =
document.getElementById('t-contributions')
.getElementsByTagName('a')[0].href.replace(/.*\//, '');
}
else
{
var li = link.parentNode;
if(li.tagName.toUpperCase() == 'SPAN')
li = li.parentNode;
var links = li.getElementsByTagName('a');
for(var i = 0; i < links.length; ++i)
if(links[i].title.indexOf('Special:Contributions/') == 0)
{
contributor = links[i].title.substr('Special:Contributions/'.length);
break;
}
}
if(pagename.indexOf('User:' + contributor + '/') == 0)
if(pagename.substr(contributor.length + 5) in GPE.perUserWhiteListedSubPages)
return true;
if(contributor in GPE.individualWhiteListedContributors)
return true;
return false;
};
/* </pre>
==Utility functions==
<pre> */
GPE.newButton = function(text, color, hoverText)
{
var button = newNode('button', text);
button.style.background = color;
button.style.color = '#FFF';
button.style.border = '0';
button.style.padding = '0';
button.style.cursor = 'pointer';
button.title = hoverText;
return button;
};
GPE.disableButton = function(button, text, hoverText)
{
button.onclick = null;
button.title = (hoverText || '');
button.innerHTML = text;
// clear out explicit styling and disable, so we can get appropriate
// disabled-button styles:
button.style.background = '';
button.style.color = '';
button.style.cursor = '';
button.disabled = 'disabled';
};
/* </pre>
==Individual patrol-buttons==
<pre> */
GPE.addPatrolButton = function(link, rcid)
{
if(link.className.search(/(?:^|\s)gpe-hasPatrolButton(?:\s|$)/) > -1)
return;
link.className = (link.className + ' gpe-hasPatrolButton').trim();
var button = GPE.newButton('M', '#009', 'click to mark as patrolled');
link.parentNode.insertBefore(button, link.nextSibling);
link.parentNode.insertBefore(document.createTextNode(' · '), button);
button.onclick =
function ()
{
var token = mediaWiki.user.tokens.get('patrolToken');
$.post
(
'/w/api.php?format=json&action=patrol&assert=user',
{ token: token, rcid: rcid },
function (data)
{
if(data.patrol)
GPE.disableButton(button, 'm', 'marked as patrolled');
else if(data.error)
{
var msg = data.error.code + ': ' + data.error.info;
if(data.error.code == 'badtoken')
msg += ': "' + token + '"';
alert(msg);
}
},
'json'
);
};
if(GPE.shouldAutoPatrol(link))
button.click();
// remove the exclamation point:
var tmp = link;
while(tmp && tmp.nodeName.toUpperCase() !== 'LI')
tmp = tmp.parentNode;
if(tmp)
tmp = tmp.getElementsByClassName('unpatrolled')[0];
if(tmp)
tmp.parentNode.removeChild(tmp);
};
/* </pre>
==Individual delete-buttons==
<pre> */
GPE.addDeleteButton = function(link, title)
{
if(link.className.search(/(?:^|\s)gpe-hasDeleteButton(?:\s|$)/) > -1)
return;
link.className = (link.className + ' gpe-hasDeleteButton').trim();
var button = GPE.newButton('D', '#900', 'click to delete');
link.parentNode.insertBefore(button, link.nextSibling);
link.parentNode.insertBefore(document.createTextNode(' · '), button);
button.onclick =
function ()
{
var dropdownReason =
document.getElementById('deleteReasonsDropdown')
? document.getElementById('deleteReasonsDropdown').value
: '';
if(dropdownReason == 'other')
dropdownReason = '';
var textInputReason =
document.getElementById('deleteReasonTextInput').value;
var reason;
if(dropdownReason.length && textInputReason.length)
reason = dropdownReason + ': ' + textInputReason;
else if(dropdownReason.length || textInputReason.length)
reason = dropdownReason + textInputReason;
else
reason = GPE.deleteReasonIfBlank;
var token = mediaWiki.user.tokens.get('deleteToken');
$.post
(
'/w/api.php?format=json&action=delete&assert=user',
{ title: title, token: token, reason: reason },
function (data)
{
if(data['delete'])
GPE.disableButton(button, 'd', 'deleted');
else if(data.error)
{
var msg = data.error.code + ': ' + data.error.info;
if(data.error.code == 'badtoken')
msg += ': "' + token + '"';
alert(msg);
}
},
'json'
);
};
};
/* </pre>
==Delete-reasons==
<pre> */
GPE.addDeleteReasonInput = function ()
{ var deleteReasonDiv =
( newNode
( 'div',
{ style:
'background:#900; color:#FFF; ' +
'position:fixed; bottom:0; right:0; margin-bottom:0'
},
'\u00A0Deletion reason:\u00A0'
)
);
deleteReasonDiv.title =
'the deletion reason (message/summary) to use when you click "D"';
var deleteReasonTextInput =
( newNode
( 'input',
{ type: 'text',
size: 80,
id: 'deleteReasonTextInput',
value: GPE.initialDeleteReason,
style: 'position:fixed; right:0; margin-bottom:0'
}
)
);
deleteReasonDiv.appendChild(deleteReasonTextInput);
document.getElementById('bodyContent').appendChild(deleteReasonDiv);
if(GPE.hideDeleteReasonDropdownOnWatchlist)
if(mediaWiki.config.get('wgPageName') == 'Special:Watchlist')
return;
// get canned messages from [[MediaWiki:Deletereason-dropdown]]:
$.getJSON
( '/w/api.php?format=json&action=query&meta=allmessages&ammessages=Deletereason-dropdown',
function (data)
{ var rawDeleteReasons = data.query.allmessages[0]['*'];
var deleteReasonsDropdown =
newNode('select',
{ id: 'deleteReasonsDropdown', style: 'vertical-align: bottom' });
deleteReasonsDropdown.appendChild
(newNode('option', { value: 'other' }, 'Other reason'));
var optGroup = deleteReasonsDropdown;
rawDeleteReasons.replace
( /^(\*\*?) *(.+)$/gm,
function (s, asterisks, text)
{ if(asterisks == '*')
deleteReasonsDropdown.appendChild
(optGroup = newNode('optgroup', { label: text }));
else // '**'
optGroup.appendChild(newNode('option', { value: text }, text));
}
);
deleteReasonDiv.insertBefore(deleteReasonsDropdown, deleteReasonTextInput);
deleteReasonDiv.insertBefore(newNode('br'), deleteReasonTextInput);
deleteReasonDiv.insertBefore(document.createTextNode('\u00A0'), deleteReasonTextInput);
}
);
};
/* </pre>
==Namespaces==
<pre> */
GPE.computeNamespaces = function
(selected, includeAssociated, invertSelection)
{
var associated = Number(selected) + (selected % 2 === 0 ? 1 : -1);
if(invertSelection)
{
var selector = document.getElementById('namespace');
if(! selector)
return [];
var ret = [];
for(var option = selector.firstChild; option; option = option.nextSibling)
if(option.nodeName.toUpperCase() === 'OPTION' && option.value)
if(option.value != selected)
if(! includeAssociated || option.value != associated)
ret.push(option.value);
return ret;
}
else
{
if(includeAssociated)
return [selected, associated];
else
return [selected];
}
};
GPE.generateRcnamespace = function ()
{
var currUrl = document.location.href;
if(! /[?&]namespace=\d+(?:&|$)/.test(currUrl))
return;
var selected = /[?&]namespace=(\d+)(?:&|$)/.exec(currUrl)[1];
var includeAssociated =
mediaWiki.config.get('wgPageName') !== 'Special:NewPages'
&& /[?&]associated=(?!0?&|0?$)/.test(currUrl);
var invertSelection = /[?&]invert=(?!0?&|0?$)/.test(currUrl);
var namespaces =
GPE.computeNamespaces(selected, includeAssociated, invertSelection);
if(namespaces.length > 0)
return namespaces.join('|');
};
/* </pre>
==Find and handle links==
<pre> */
GPE.handleUnpatrolledEdits = function (rcidsByRevid)
{
var links =
document.getElementById('bodyContent').getElementsByTagName('a');
for(var i = links.length - 1; i >= 0; --i)
{
var mapKey = /&diff=(prev&oldid=)?(\d+)(&|$)/.exec(links[i].href);
if(mapKey && rcidsByRevid.hasOwnProperty(mapKey[2]))
GPE.addPatrolButton(links[i], rcidsByRevid[mapKey[2]]);
}
};
GPE.findLinksToUnpatrolledNewPages = function (rcidsByTitle)
{
if(mediaWiki.config.get('wgPageName') === 'Special:NewPages')
{
var ret = [];
$('li.not-patrolled a.mw-newpages-pagename').each(function () {
if (this.title && rcidsByTitle.hasOwnProperty(this.title))
ret.push(this);
});
return ret;
}
else
{
var ret = [];
var abbrs =
document.getElementById('bodyContent').getElementsByTagName('abbr');
for(var i = abbrs.length - 1; i >= 0; --i)
{
if(abbrs[i].className != 'newpage')
continue;
var link = abbrs[i];
while(link && link.nodeName.toUpperCase() != 'A')
if(link.nodeName.toUpperCase() === 'SPAN' && link.className === 'mw-title')
link = link.firstChild;
else
link = link.nextSibling;
if(link && link.title && rcidsByTitle.hasOwnProperty(link.title))
ret.push(link);
}
return ret;
}
};
GPE.handleUnpatrolledNewPages = function (rcidsByTitle)
{
var userIsSysop =
mediaWiki.config.get('wgUserGroups').indexOf('sysop') > -1;
var links = GPE.findLinksToUnpatrolledNewPages(rcidsByTitle);
for(var i = links.length - 1; i >= 0; --i)
{
var link = links[i];
// 2016-04: Equinox removing red D delete button because it hasn't worked for a year.
// if(userIsSysop)
// GPE.addDeleteButton(link, link.title);
GPE.addPatrolButton(link, rcidsByTitle[link.title]);
}
// Remove the rest of the delete features as above
// if(userIsSysop && links.length > 0)
// {
// GPE.getAndStoreDeleteToken();
// GPE.addDeleteReasonInput();
// }
};
GPE.getAndStoreDeleteToken = function ()
{
$.getJSON
(
'/w/api.php?format=json&action=tokens&type=delete',
function (data)
{
var token = data.tokens.deletetoken;
if(! token || token.search(/^[0-9a-f]{32}\+\\$/) != 0)
return;
mediaWiki.user.tokens.set('deleteToken', token);
}
);
};
GPE.main = function (params)
{
var url =
'/w/api.php?format=json&action=query&list=recentchanges' +
'&rcprop=ids|title' +
'&rcshow=!patrolled' + (params.hasOwnProperty('rcshow') ? '|' + params.rcshow : '') +
'&rclimit=' + (params.hasOwnProperty('rclimit') ? params.rclimit : 500) +
'&rctype=' + (params.hasOwnProperty('rctype') ? params.rctype : 'edit|new') +
(params.hasOwnProperty('rcdir') ? '&rcdir=' + params.rcdir : '') +
(params.hasOwnProperty('rcstart') ? '&rcstart=' + params.rcstart : '') +
(params.hasOwnProperty('rcnamespace') ? '&rcnamespace=' + params.rcnamespace : '') +
(params.hasOwnProperty('rcuser') ? '&rcuser=' + params.rcuser : '');
$.getJSON
(
url,
function (data)
{
data = data.query.recentchanges;
var rcidsByRevid = {}; // for unpatrolled edits
var rcidsByTitle = {}; // for unpatrolled new pages
for(var i = 0; i < data.length; ++i)
if(data[i].type == 'edit')
rcidsByRevid[data[i].revid] = data[i].rcid;
else
rcidsByTitle[data[i].title] = data[i].rcid;
GPE.handleUnpatrolledEdits(rcidsByRevid);
GPE.handleUnpatrolledNewPages(rcidsByTitle);
}
);
};
/* </pre>
==Onload-hooks==
<pre> */
$( document ).ready
( function ()
{
if(mediaWiki.config.get('wgPageName') === 'Special:RecentChanges') {
var currUrl = document.location.href;
var params = {};
var rcshow = [];
if(currUrl.search(/[?&]hideliu=(?!0?$|0?&)/) > -1)
rcshow.push('anon');
else if(currUrl.search(/[?&]hideanons=(?!0?$|0?&)/) > -1)
rcshow.push('!anon');
if(document.getElementsByClassName('minoredit').length === 0)
rcshow.push('!minor');
if(rcshow.length > 0)
params.rcshow = rcshow.join('|');
var rcnamespace = GPE.generateRcnamespace();
if(rcnamespace)
params.rcnamespace = rcnamespace;
GPE.main(params);
} else if(mediaWiki.config.get('wgPageName') === 'Special:NewPages') {
var currUrl = document.location.href;
var params = { rctype: 'new' };
var rcshow = [];
if(currUrl.search(/[?&]hideliu=(?!0?$|0?&)/) > -1)
rcshow.push('anon');
if(currUrl.search(/[?&]hideredirs=0?(?:$|&)/) === -1)
rcshow.push('!redirect');
if(rcshow.length > 0)
params.rcshow = rcshow.join('|');
if(currUrl.search(/[?&]dir=prev(?=$|&)/) > -1) {
params.rcdir = 'newer';
if(currUrl.search(/[?&]offset=\d+(?=$|&)/) > -1)
params.rcstart = currUrl.match(/[?&]offset=(\d+)(?=$|&)/)[1];
}
var rcnamespace = GPE.generateRcnamespace();
if(rcnamespace)
params.rcnamespace = rcnamespace;
GPE.main(params);
} else if(mediaWiki.config.get('wgPageName') === 'Special:Watchlist') {
var params = {};
var rcnamespace = GPE.generateRcnamespace();
if(rcnamespace)
params.rcnamespace = rcnamespace;
// TODO is this the best way to find what we need for the watchlist?
GPE.main(params);
} else if(mediaWiki.config.get('wgPageName').search(/^Special:Contributions(\/|$)/) === 0)
GPE.main({ rcuser: document.getElementById('t-contributions').firstChild.href.replace(/^.*?\/Special:Contributions\//, '') });
else if(mediaWiki.config.get('wgAction') === 'markpatrolled'
|| mediaWiki.config.get('wgAction') === 'delete'
|| mediaWiki.config.get('wgAction') === 'rollback')
GPE.main({ rclimit: 15 });
}
);
})();
g9g0tszn8uzicki57h3lrynnuqr2imv
MediaWiki:Gadget-RegexMenuFramework.js
8
18000
89003
2017-08-20T18:42:21Z
en>Dixtosa
0
89003
javascript
text/javascript
// {{documentation}}
/**
* TemplateScript adds configurable templates and scripts to the sidebar, and adds an example regex editor.
* @see [[meta:TemplateScript]]
* @author [[User:Pathoschild]]
* @update-token [[meta:File:pathoschild/templatescript.js]]
*/
mw.loader.load('//tools-static.wmflabs.org/meta/scripts/pathoschild.templatescript.js');
7blaltfqd7vx48e4935xh5r92z0lips
89004
89003
2026-08-06T23:33:04Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-RegexMenuFramework.js]]
89003
javascript
text/javascript
// {{documentation}}
/**
* TemplateScript adds configurable templates and scripts to the sidebar, and adds an example regex editor.
* @see [[meta:TemplateScript]]
* @author [[User:Pathoschild]]
* @update-token [[meta:File:pathoschild/templatescript.js]]
*/
mw.loader.load('//tools-static.wmflabs.org/meta/scripts/pathoschild.templatescript.js');
7blaltfqd7vx48e4935xh5r92z0lips
MediaWiki:Gadget-AcceleratedFormCreation.js
8
18001
89005
2025-10-31T09:35:05Z
en>Surjection
0
89005
javascript
text/javascript
// <nowiki>
/* globals mw, $ */
// jshint maxerr:500
// FIXME: This gadget relies to an excessive degree on OrangeLinks to function. These gadgets should be made independent.
/*
* The starting point of the whole script.
*
* This adds a hook to the page load event so that the script runs
* adds the generated text to the edit window once the page is done loading.
*/
"use strict";
mw.loader.using(["mediawiki.util"]).done(function() {
var pageName = mw.config.get("wgPageName");
// Don't do anything unless the current page is in the main namespace.
// Set window.accelEverywhere = true to test this gadget elsewhere.
if (window.accelEverywhere || (mw.config.get("wgAction") === "view" && (mw.config.get("wgNamespaceNumber") === 0 || pageName == "Wiktionary:Sandbox"))) {
// Stores all accelerated data, by language, by target pagename.
// Sub-arrays are in HTML order.
var accelParamsByPagename = {};
var getTargetPagename = function(link) {
var targetPagename = mw.util.getParamValue("title", link.href);
if (targetPagename === null) {
var match = link.href.match(/^(.*)\/wiki\/([^#]+)(?:#.+)?$/);
if (match) {
targetPagename = decodeURIComponent(match[2]);
}
}
return targetPagename;
};
var getLang = function (element) {
if (element.accelLang !== undefined) {
return element.accelLang;
}
var hasLang = $(element).closest("[lang]")[0];
var lang = hasLang ? hasLang.getAttribute("lang") : null;
element.accelLang = lang;
return lang;
};
var getPartOfSpeech = function(link) {
// Acceleration can be added to inflection tables too.
// This tells the search script to skip headers with these names.
var skipheaders = [
"alternative forms",
"antonyms",
"conjugation",
"declension",
"derived terms",
"inflection",
"mutation",
"related terms",
"synonyms",
"translations",
"usage notes"
];
for (var node = link; node !== null; node = node.previousSibling || node.parentNode) {
if (node.nodeType == 1 && (node.nodeName.match(/^H[3-6]$/) || (node.nodeName === 'DIV' && node.className.indexOf('mw-heading') !== -1))) {
var header = $(node).find(".mw-headline, h3, h4, h5, h6");
if (!header) {
continue;
}
header = header.text().replace(/^[1-9.]* /, "").toLowerCase();
if (skipheaders.indexOf(header) == -1) {
return header;
}
}
}
throw new Error("This entry seems to be formatted incorrectly. Does it have a language and part-of-speech header?");
};
var createAccelParam = function(link) {
var classNames = Array.prototype.filter.call($(link).closest(".form-of")[0].classList,
function (className) {
return (/^(gender|origin|origin_transliteration|pos|target|transliteration)-.+|.+-form-of$/.test(className));
});
var accelParam = classNames.join(" ");
var targetPagename = getTargetPagename(link);
var targetHead = (link.innerText || link.textContent).replace(/ /g, "_");
if (targetPagename != targetHead) {
accelParam = "target-" + targetHead + " " + accelParam;
}
return "pos-" + getPartOfSpeech(link).replace(/ /g, "_") + " " + accelParam;
};
var storeAccelParam = function(link) {
// Extract the targeted pagename from the URL,
// and language code from the nearest element with a lang attribute
var lang = getLang(link);
var targetPagename = getTargetPagename(link);
// Add page name to the list
if (accelParamsByPagename[lang] === undefined) {
accelParamsByPagename[lang] = {};
}
if (accelParamsByPagename[lang][targetPagename] === undefined) {
accelParamsByPagename[lang][targetPagename] = [];
}
var accelParam = createAccelParam(link);
if (accelParamsByPagename[lang][targetPagename].indexOf(accelParam) === -1) {
accelParamsByPagename[lang][targetPagename].push(accelParam);
}
};
var processLink = function(link) {
// Extract the targeted pagename from the URL,
// and language code from the nearest element with a lang attribute
var lang = getLang(link);
var targetPagename = getTargetPagename(link);
// Fetch the acceleration parameters from the store
var accelParam = accelParamsByPagename[lang][targetPagename]
.map(function (accel, i) {
return "accel" + (i + 1).toString() + "=" + encodeURIComponent(accel);
})
.join("&");
// Convert a non-edit link into an edit link
if (link.href.indexOf("action=edit") < 0) {
link.href = link.href.replace(/^(.*)\/wiki\/([^#]+)(?:#.+)?$/, "$1/w/index.php?title=$2&action=edit");
}
// use URL to ensure we append to the query string, not e.g. the hash.
var targetUrl = new URL(link.href);
// Now build a new "green link" URL to replace the original red link with
targetUrl.search +=
"&editintro=MediaWiki:Gadget-AcceleratedFormCreation.js/intro" +
"&accel_lang=" + encodeURIComponent(lang) +
"&accel_lemma=" + encodeURIComponent(pageName.replace(/_/g, " ")) +
"&" + accelParam +
"&veswitched=1";
link.href = targetUrl.href;
link.classList.add("accelerated");
link.processedLink = true;
};
// Mutation observer to respond when OrangeLinks modifies links
var mutobs = new MutationObserver(function(mutations, observer) {
mutations.forEach(function(mutation) {
var link = mutation.target;
if (!(mutation.attributeName == "class" && link.tagName === "A")) {
return;
}
// Don't process a link we've already been to
if (link.processedLink) {
return;
}
if (!$(link).hasClass("orange-link")) {
return;
}
// Process
processLink(link);
});
});
// First generate and store all the parameters
var oldtable = null; // Were we previously inside a table?
var columns = [];
$(".form-of a").each(function() {
// Are we currently inside a table?
var $this = $(this);
var table = $this.closest("table");
if (table.length > 0) {
table = table[0];
} else {
table = null;
}
// Was a column number specified on the current table cell?
// jQuery.fn.data automatically converts an integer-like string
// to a number.
var col = $this.closest("td[data-accel-col]").first().data("accel-col");
if (typeof col !== "number") {
col = null;
}
// If we were in a table, and we changed to another table or are no longer in one,
// or if there is no column number attribute, flush the column lists.
if (oldtable && (oldtable !== table || col === null)) {
for (var i = 0; i < columns.length; ++i) {
for (var j = 0; j < columns[i].length; ++j) {
storeAccelParam(columns[i][j]);
}
}
columns = [];
}
oldtable = table;
// The nostore parameter causes the link to not be stored,
// but it is processed later. The effect is that this link has no
// effect on the ordering of forms.
if ($(this).closest(".form-of").first().hasClass("form-of-nostore")) {
return;
}
// If there is a column number attribute, defer storing the link,
// put it in the columns array instead.
if (col !== null) {
--col; // Column attributes are 1-based, JS arrays are 0-based
// Expand the columns list to fit the number of columns
while (columns.length <= col) {
columns.push([]);
}
// Save the link in the columns list
columns[col].push(this);
} else {
// Store the link directly
storeAccelParam(this);
}
});
// Flush column lists
for (var i = 0; i < columns.length; ++i) {
for (var j = 0; j < columns[i].length; ++j) {
storeAccelParam(columns[i][j]);
}
}
// Then add them onto the links, or add a mutation observer
$(".form-of a").each(function() {
var $this = $(this);
if ($this.hasClass("new") || $this.hasClass("orange-link")) {
processLink(this);
} else {
// FIXME: There's a small window for a race condition here.
// If the "orange-link" class is added by OrangeLinks after the above if-statement is evaluated,
// but before the observer is added, then the link won't be processed.
mutobs.observe(this, {attributes : true});
}
});
// A function that force-processes all links and adds a class to those that would not have otherwise been processed.
var forceProcessAllLinks = function() {
$(".form-of a").each(function() {
var $this = $(this);
if (!($this.hasClass("new") || $this.hasClass("orange-link"))) {
this.classList.add("accelerated-forced");
}
processLink(this);
});
};
window.accelForceProcessAllLinks = forceProcessAllLinks;
} else if (mw.config.get("wgAction") === "edit") {
// Get the parameters from the URL
var getAccelParams = function() {
var accelParams = [];
var i = 1;
while (true) {
var acceldata = mw.util.getParamValue("accel" + i.toString());
if (!acceldata) {
break;
}
// Default values
var params = {
pos: null,
form: null,
gender: null,
transliteration: null,
origin: mw.util.getParamValue("accel_lemma"),
origin_transliteration: null,
target: pageName,
};
// Go over each part and add it
var parts = acceldata.split(" ");
for (var j = 0; j < parts.length; ++j) {
var part = parts[j];
var paramMatch = part.match(/^(gender|origin|origin_transliteration|pos|target|transliteration)-(.+)$/);
if (paramMatch) {
params[paramMatch[1]] = paramMatch[2].replace(/_/g, " ").replace(/\uFFF0/g, "_");
} else {
var formMatch = part.match(/^(.+)-form-of$/);
if (formMatch) {
params.form = formMatch[1].replace(/_/g, " ").replace(/\uFFF0/g, "_");
}
}
}
accelParams.push(params);
++i;
}
return accelParams;
};
// Generates entries from the information
var printArgs = function(accelParams) {
var args = [
"lang=" + mw.util.getParamValue("accel_lang"),
"origin_pagename=" + mw.util.getParamValue("accel_lemma"),
"target_pagename=" + pageName,
"num=" + accelParams.length,
];
for (var i = 0; i < accelParams.length; ++i) {
for (var key in accelParams[i]) {
if (accelParams[i][key] !== null) {
args.push(key + (i + 1) + "=" + accelParams[i][key].replace(/\|/g, "|"));
}
}
}
return args.join("|");
};
var showModuleError = function(errorText) {
// Attempt to link to the line of the module in which the error occurred.
errorText = errorText.replace(
/(Module:[^#<>\[\]|{}_]+)(?: at line |:)(\d+)/,
function (wholeMatch, moduleName, lineNumber) {
var link = document.createElement('a');
link.href = mw.util.getUrl(moduleName, {action: "edit"}) + "#mw-ce-l" + lineNumber;
link.innerHTML = moduleName + " at line " + lineNumber;
return "Lua error in " + link.outerHTML;
});
var errorBox =
"<div id=\"accel-error\">" +
"<p><big>An error occurred while generating the entry:</big></p>" +
"<p>" + errorText + "</p>" +
"</div>";
wikipreview.insertAdjacentHTML("beforebegin", errorBox);
};
var receiveModuleResponse = function(response) {
var newtext, result;
try {
result = JSON.parse(response.expandtemplates.wikitext);
} catch (err) { // JSON parse error should not happen.
mw.notify(err.msg);
return;
}
if (result.error) { // module error
showModuleError(result.error);
} else { // successfully generated entries
newtext = result.entries;
}
for (i = 0; i < result.messages.length; ++i) {
mw.notify(result.messages[i]);
}
if (!newtext) {
return;
}
var newValue;
var langsection_regex = /^==([^=\n]+)==$/mg;
var match = langsection_regex.exec(newtext);
if (!match) {
showModuleError("No language section was found in the returned text.");
}
var langname = match[1];
// Does the page already exist?
if (textbox.value) {
var resultInBox = false;
// Reset position at which regex starts its search.
// Otherwise, regex starts matching after the index where it
// found the language header in newtext.
langsection_regex.lastIndex = 0;
// Go over language sections to find where to insert our new one
while ((match = langsection_regex.exec(textbox.value)) !== null) {
if (match[1] == langname) {
// There already exists a section for our language, display text in a separate box.
resultInBox = true;
break;
} else if (match[1] == "Translingual" || match[1] == "English" || (langname != "English" && match[1] < langname)) {
// Skip past English and Translingual, or if the language sorts higher
continue;
} else {
// We found the first match that sorts lower than our language, great.
break;
}
}
var scrollIndex;
newValue = textbox.value;
if (resultInBox) {
// Display the result in a separate box.
var insertTextBoxIn = document.getElementById("accel-form-conflict-textbox-here");
if (insertTextBoxIn) {
var newElement = document.createElement("div");
newElement.id = insertTextBoxIn.id;
var warning = document.createElement("p");
warning.textContent = "A section for this language already exists. Please combine the new text manually:";
var textBox = document.createElement("textarea");
textBox.setAttribute("readonly", true);
textBox.setAttribute("rows", 10);
textBox.textContent = newtext;
newElement.appendChild(warning);
newElement.appendChild(textBox);
insertTextBoxIn.replaceWith(newElement);
}
scrollIndex = match !== null ? match.index : newValue.length;
} else if (match === null) {
// We found no language that our section should go before, so insert it at the end.
newValue = newValue.trimEnd() + "\n\n";
scrollIndex = newValue.length;
newValue = newValue + newtext;
} else {
// We found a language to insert before, so do that.
newValue = newValue.substring(0, match.index) + newtext + "\n\n" + newValue.substring(match.index);
scrollIndex = match.index;
}
// Scroll the textbox to the newly added section. First scroll all the way down,
// then set the cursor to the start of the new section, which scrolls back up
// to the new section's language header.
textbox.scrollTop = textbox.scrollHeight;
textbox.selectionStart = scrollIndex;
textbox.selectionEnd = scrollIndex;
summary.value = "Adding forms of " + langname + " [[" + lemma + "]] ([[WT:ACCEL|Accelerated]])";
} else {
newValue = newtext;
summary.value = "Creating forms of " + langname + " [[" + lemma + "]] ([[WT:ACCEL|Accelerated]])";
}
// Set textbox text. Setting textbox.value is unreliable.
$(textbox).val(newValue);
};
var wikipreview = document.getElementById("wikiPreview");
var textbox = document.getElementById("wpTextbox1");
var summary = document.getElementById("wpSummary");
var lang = mw.util.getParamValue("accel_lang");
var lemma = mw.util.getParamValue("accel_lemma");
if (!(wikipreview && textbox && summary && lang && lemma)) {
return;
}
// Gather all the information that was given in the URL
var accelParams = getAccelParams();
if (!accelParams) {
return;
}
var module = "accel", funcName = "generate_JSON";
mw.loader.using("mediawiki.api", function() {
new mw.Api().get({
"action": "expandtemplates",
"format": "json",
"text": "{{#invoke:" + module + "|" + funcName + "|" + printArgs(accelParams) + "}}",
"prop": "wikitext"
}).done(receiveModuleResponse);
});
}
});
// </nowiki>
rswaz1eynco0hjmjyu7gmofe9bjtahv
89006
89005
2026-08-06T23:33:12Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-AcceleratedFormCreation.js]]
89005
javascript
text/javascript
// <nowiki>
/* globals mw, $ */
// jshint maxerr:500
// FIXME: This gadget relies to an excessive degree on OrangeLinks to function. These gadgets should be made independent.
/*
* The starting point of the whole script.
*
* This adds a hook to the page load event so that the script runs
* adds the generated text to the edit window once the page is done loading.
*/
"use strict";
mw.loader.using(["mediawiki.util"]).done(function() {
var pageName = mw.config.get("wgPageName");
// Don't do anything unless the current page is in the main namespace.
// Set window.accelEverywhere = true to test this gadget elsewhere.
if (window.accelEverywhere || (mw.config.get("wgAction") === "view" && (mw.config.get("wgNamespaceNumber") === 0 || pageName == "Wiktionary:Sandbox"))) {
// Stores all accelerated data, by language, by target pagename.
// Sub-arrays are in HTML order.
var accelParamsByPagename = {};
var getTargetPagename = function(link) {
var targetPagename = mw.util.getParamValue("title", link.href);
if (targetPagename === null) {
var match = link.href.match(/^(.*)\/wiki\/([^#]+)(?:#.+)?$/);
if (match) {
targetPagename = decodeURIComponent(match[2]);
}
}
return targetPagename;
};
var getLang = function (element) {
if (element.accelLang !== undefined) {
return element.accelLang;
}
var hasLang = $(element).closest("[lang]")[0];
var lang = hasLang ? hasLang.getAttribute("lang") : null;
element.accelLang = lang;
return lang;
};
var getPartOfSpeech = function(link) {
// Acceleration can be added to inflection tables too.
// This tells the search script to skip headers with these names.
var skipheaders = [
"alternative forms",
"antonyms",
"conjugation",
"declension",
"derived terms",
"inflection",
"mutation",
"related terms",
"synonyms",
"translations",
"usage notes"
];
for (var node = link; node !== null; node = node.previousSibling || node.parentNode) {
if (node.nodeType == 1 && (node.nodeName.match(/^H[3-6]$/) || (node.nodeName === 'DIV' && node.className.indexOf('mw-heading') !== -1))) {
var header = $(node).find(".mw-headline, h3, h4, h5, h6");
if (!header) {
continue;
}
header = header.text().replace(/^[1-9.]* /, "").toLowerCase();
if (skipheaders.indexOf(header) == -1) {
return header;
}
}
}
throw new Error("This entry seems to be formatted incorrectly. Does it have a language and part-of-speech header?");
};
var createAccelParam = function(link) {
var classNames = Array.prototype.filter.call($(link).closest(".form-of")[0].classList,
function (className) {
return (/^(gender|origin|origin_transliteration|pos|target|transliteration)-.+|.+-form-of$/.test(className));
});
var accelParam = classNames.join(" ");
var targetPagename = getTargetPagename(link);
var targetHead = (link.innerText || link.textContent).replace(/ /g, "_");
if (targetPagename != targetHead) {
accelParam = "target-" + targetHead + " " + accelParam;
}
return "pos-" + getPartOfSpeech(link).replace(/ /g, "_") + " " + accelParam;
};
var storeAccelParam = function(link) {
// Extract the targeted pagename from the URL,
// and language code from the nearest element with a lang attribute
var lang = getLang(link);
var targetPagename = getTargetPagename(link);
// Add page name to the list
if (accelParamsByPagename[lang] === undefined) {
accelParamsByPagename[lang] = {};
}
if (accelParamsByPagename[lang][targetPagename] === undefined) {
accelParamsByPagename[lang][targetPagename] = [];
}
var accelParam = createAccelParam(link);
if (accelParamsByPagename[lang][targetPagename].indexOf(accelParam) === -1) {
accelParamsByPagename[lang][targetPagename].push(accelParam);
}
};
var processLink = function(link) {
// Extract the targeted pagename from the URL,
// and language code from the nearest element with a lang attribute
var lang = getLang(link);
var targetPagename = getTargetPagename(link);
// Fetch the acceleration parameters from the store
var accelParam = accelParamsByPagename[lang][targetPagename]
.map(function (accel, i) {
return "accel" + (i + 1).toString() + "=" + encodeURIComponent(accel);
})
.join("&");
// Convert a non-edit link into an edit link
if (link.href.indexOf("action=edit") < 0) {
link.href = link.href.replace(/^(.*)\/wiki\/([^#]+)(?:#.+)?$/, "$1/w/index.php?title=$2&action=edit");
}
// use URL to ensure we append to the query string, not e.g. the hash.
var targetUrl = new URL(link.href);
// Now build a new "green link" URL to replace the original red link with
targetUrl.search +=
"&editintro=MediaWiki:Gadget-AcceleratedFormCreation.js/intro" +
"&accel_lang=" + encodeURIComponent(lang) +
"&accel_lemma=" + encodeURIComponent(pageName.replace(/_/g, " ")) +
"&" + accelParam +
"&veswitched=1";
link.href = targetUrl.href;
link.classList.add("accelerated");
link.processedLink = true;
};
// Mutation observer to respond when OrangeLinks modifies links
var mutobs = new MutationObserver(function(mutations, observer) {
mutations.forEach(function(mutation) {
var link = mutation.target;
if (!(mutation.attributeName == "class" && link.tagName === "A")) {
return;
}
// Don't process a link we've already been to
if (link.processedLink) {
return;
}
if (!$(link).hasClass("orange-link")) {
return;
}
// Process
processLink(link);
});
});
// First generate and store all the parameters
var oldtable = null; // Were we previously inside a table?
var columns = [];
$(".form-of a").each(function() {
// Are we currently inside a table?
var $this = $(this);
var table = $this.closest("table");
if (table.length > 0) {
table = table[0];
} else {
table = null;
}
// Was a column number specified on the current table cell?
// jQuery.fn.data automatically converts an integer-like string
// to a number.
var col = $this.closest("td[data-accel-col]").first().data("accel-col");
if (typeof col !== "number") {
col = null;
}
// If we were in a table, and we changed to another table or are no longer in one,
// or if there is no column number attribute, flush the column lists.
if (oldtable && (oldtable !== table || col === null)) {
for (var i = 0; i < columns.length; ++i) {
for (var j = 0; j < columns[i].length; ++j) {
storeAccelParam(columns[i][j]);
}
}
columns = [];
}
oldtable = table;
// The nostore parameter causes the link to not be stored,
// but it is processed later. The effect is that this link has no
// effect on the ordering of forms.
if ($(this).closest(".form-of").first().hasClass("form-of-nostore")) {
return;
}
// If there is a column number attribute, defer storing the link,
// put it in the columns array instead.
if (col !== null) {
--col; // Column attributes are 1-based, JS arrays are 0-based
// Expand the columns list to fit the number of columns
while (columns.length <= col) {
columns.push([]);
}
// Save the link in the columns list
columns[col].push(this);
} else {
// Store the link directly
storeAccelParam(this);
}
});
// Flush column lists
for (var i = 0; i < columns.length; ++i) {
for (var j = 0; j < columns[i].length; ++j) {
storeAccelParam(columns[i][j]);
}
}
// Then add them onto the links, or add a mutation observer
$(".form-of a").each(function() {
var $this = $(this);
if ($this.hasClass("new") || $this.hasClass("orange-link")) {
processLink(this);
} else {
// FIXME: There's a small window for a race condition here.
// If the "orange-link" class is added by OrangeLinks after the above if-statement is evaluated,
// but before the observer is added, then the link won't be processed.
mutobs.observe(this, {attributes : true});
}
});
// A function that force-processes all links and adds a class to those that would not have otherwise been processed.
var forceProcessAllLinks = function() {
$(".form-of a").each(function() {
var $this = $(this);
if (!($this.hasClass("new") || $this.hasClass("orange-link"))) {
this.classList.add("accelerated-forced");
}
processLink(this);
});
};
window.accelForceProcessAllLinks = forceProcessAllLinks;
} else if (mw.config.get("wgAction") === "edit") {
// Get the parameters from the URL
var getAccelParams = function() {
var accelParams = [];
var i = 1;
while (true) {
var acceldata = mw.util.getParamValue("accel" + i.toString());
if (!acceldata) {
break;
}
// Default values
var params = {
pos: null,
form: null,
gender: null,
transliteration: null,
origin: mw.util.getParamValue("accel_lemma"),
origin_transliteration: null,
target: pageName,
};
// Go over each part and add it
var parts = acceldata.split(" ");
for (var j = 0; j < parts.length; ++j) {
var part = parts[j];
var paramMatch = part.match(/^(gender|origin|origin_transliteration|pos|target|transliteration)-(.+)$/);
if (paramMatch) {
params[paramMatch[1]] = paramMatch[2].replace(/_/g, " ").replace(/\uFFF0/g, "_");
} else {
var formMatch = part.match(/^(.+)-form-of$/);
if (formMatch) {
params.form = formMatch[1].replace(/_/g, " ").replace(/\uFFF0/g, "_");
}
}
}
accelParams.push(params);
++i;
}
return accelParams;
};
// Generates entries from the information
var printArgs = function(accelParams) {
var args = [
"lang=" + mw.util.getParamValue("accel_lang"),
"origin_pagename=" + mw.util.getParamValue("accel_lemma"),
"target_pagename=" + pageName,
"num=" + accelParams.length,
];
for (var i = 0; i < accelParams.length; ++i) {
for (var key in accelParams[i]) {
if (accelParams[i][key] !== null) {
args.push(key + (i + 1) + "=" + accelParams[i][key].replace(/\|/g, "|"));
}
}
}
return args.join("|");
};
var showModuleError = function(errorText) {
// Attempt to link to the line of the module in which the error occurred.
errorText = errorText.replace(
/(Module:[^#<>\[\]|{}_]+)(?: at line |:)(\d+)/,
function (wholeMatch, moduleName, lineNumber) {
var link = document.createElement('a');
link.href = mw.util.getUrl(moduleName, {action: "edit"}) + "#mw-ce-l" + lineNumber;
link.innerHTML = moduleName + " at line " + lineNumber;
return "Lua error in " + link.outerHTML;
});
var errorBox =
"<div id=\"accel-error\">" +
"<p><big>An error occurred while generating the entry:</big></p>" +
"<p>" + errorText + "</p>" +
"</div>";
wikipreview.insertAdjacentHTML("beforebegin", errorBox);
};
var receiveModuleResponse = function(response) {
var newtext, result;
try {
result = JSON.parse(response.expandtemplates.wikitext);
} catch (err) { // JSON parse error should not happen.
mw.notify(err.msg);
return;
}
if (result.error) { // module error
showModuleError(result.error);
} else { // successfully generated entries
newtext = result.entries;
}
for (i = 0; i < result.messages.length; ++i) {
mw.notify(result.messages[i]);
}
if (!newtext) {
return;
}
var newValue;
var langsection_regex = /^==([^=\n]+)==$/mg;
var match = langsection_regex.exec(newtext);
if (!match) {
showModuleError("No language section was found in the returned text.");
}
var langname = match[1];
// Does the page already exist?
if (textbox.value) {
var resultInBox = false;
// Reset position at which regex starts its search.
// Otherwise, regex starts matching after the index where it
// found the language header in newtext.
langsection_regex.lastIndex = 0;
// Go over language sections to find where to insert our new one
while ((match = langsection_regex.exec(textbox.value)) !== null) {
if (match[1] == langname) {
// There already exists a section for our language, display text in a separate box.
resultInBox = true;
break;
} else if (match[1] == "Translingual" || match[1] == "English" || (langname != "English" && match[1] < langname)) {
// Skip past English and Translingual, or if the language sorts higher
continue;
} else {
// We found the first match that sorts lower than our language, great.
break;
}
}
var scrollIndex;
newValue = textbox.value;
if (resultInBox) {
// Display the result in a separate box.
var insertTextBoxIn = document.getElementById("accel-form-conflict-textbox-here");
if (insertTextBoxIn) {
var newElement = document.createElement("div");
newElement.id = insertTextBoxIn.id;
var warning = document.createElement("p");
warning.textContent = "A section for this language already exists. Please combine the new text manually:";
var textBox = document.createElement("textarea");
textBox.setAttribute("readonly", true);
textBox.setAttribute("rows", 10);
textBox.textContent = newtext;
newElement.appendChild(warning);
newElement.appendChild(textBox);
insertTextBoxIn.replaceWith(newElement);
}
scrollIndex = match !== null ? match.index : newValue.length;
} else if (match === null) {
// We found no language that our section should go before, so insert it at the end.
newValue = newValue.trimEnd() + "\n\n";
scrollIndex = newValue.length;
newValue = newValue + newtext;
} else {
// We found a language to insert before, so do that.
newValue = newValue.substring(0, match.index) + newtext + "\n\n" + newValue.substring(match.index);
scrollIndex = match.index;
}
// Scroll the textbox to the newly added section. First scroll all the way down,
// then set the cursor to the start of the new section, which scrolls back up
// to the new section's language header.
textbox.scrollTop = textbox.scrollHeight;
textbox.selectionStart = scrollIndex;
textbox.selectionEnd = scrollIndex;
summary.value = "Adding forms of " + langname + " [[" + lemma + "]] ([[WT:ACCEL|Accelerated]])";
} else {
newValue = newtext;
summary.value = "Creating forms of " + langname + " [[" + lemma + "]] ([[WT:ACCEL|Accelerated]])";
}
// Set textbox text. Setting textbox.value is unreliable.
$(textbox).val(newValue);
};
var wikipreview = document.getElementById("wikiPreview");
var textbox = document.getElementById("wpTextbox1");
var summary = document.getElementById("wpSummary");
var lang = mw.util.getParamValue("accel_lang");
var lemma = mw.util.getParamValue("accel_lemma");
if (!(wikipreview && textbox && summary && lang && lemma)) {
return;
}
// Gather all the information that was given in the URL
var accelParams = getAccelParams();
if (!accelParams) {
return;
}
var module = "accel", funcName = "generate_JSON";
mw.loader.using("mediawiki.api", function() {
new mw.Api().get({
"action": "expandtemplates",
"format": "json",
"text": "{{#invoke:" + module + "|" + funcName + "|" + printArgs(accelParams) + "}}",
"prop": "wikitext"
}).done(receiveModuleResponse);
});
}
});
// </nowiki>
rswaz1eynco0hjmjyu7gmofe9bjtahv
MediaWiki:Gadget-AcceleratedFormCreation.css
8
18002
89007
2023-11-01T14:42:22Z
en>Erutuon
0
add yet another selector to override [[MediaWiki:Common.css]] loading its class="new" link style after this and thus taking precedence
89007
css
text/css
a.accelerated:link,
a.partlynew.accelerated:link,
a.new.accelerated:link {
color: #1fba00;
}
a.accelerated:visited,
a.partlynew.accelerated:visited,
a.new.accelerated:visited {
color: #66a759;
}
suyssctv76gv9bekacr2utmfmhwgdtp
89008
89007
2026-08-06T23:33:15Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-AcceleratedFormCreation.css]]
89007
css
text/css
a.accelerated:link,
a.partlynew.accelerated:link,
a.new.accelerated:link {
color: #1fba00;
}
a.accelerated:visited,
a.partlynew.accelerated:visited,
a.new.accelerated:visited {
color: #66a759;
}
suyssctv76gv9bekacr2utmfmhwgdtp
MediaWiki:Gadget-HotCat.js
8
18003
89009
2025-09-12T09:13:01Z
en>Surjection
0
89009
javascript
text/javascript
// {{documentation}}
// See also: MediaWiki:Gadget-HotCat.js/local defaults
//window.hotcat_translations_from_commons = true; // Make HotCat load its interface from the Commons (the default setting so no need to set it here)
/**
HotCat V2.43
en.wiktionary fork (with better support for en.wiktionary templates)
upstream: https://commons.wikimedia.org/w/index.php?title=MediaWiki:Gadget-HotCat.js&oldid=818790002
The original script is hosted at [https://commons.wikimedia.org/wiki/MediaWiki:Gadget-HotCat.js].
See it for more information.
License: Quadruple licensed, any of the following:
GFDL
GPL
LGPL
Creative Commons Attribution 3.0 (CC-BY-3.0)
Requires MediaWiki >= MW 1.27.
*/
// <nowiki>
/* eslint-disable vars-on-top, one-var, camelcase, no-alert, curly */
/* global jQuery, mediaWiki, UFUI, JSconfig, UploadForm */
/* jslint strict:false, nonew:false, bitwise:true */
( function ( $, mw ) {
// BEGINNING OF en.wiktionary EXTRA CODE
var EN_WIKTIONARY_CONTENT_NAMESPACE = [0, 100, 118].indexOf(mw.config.get("wgNamespaceNumber")) >= 0;
var EN_WIKTIONARY_TOPICS = ["C", "c", "topics", "top", "topic", "catlangcode"];
var EN_WIKTIONARY_CATLANGNAME = ["cln", "catlangname", "Cln"];
var EN_WIKTIONARY_CATEGORIZE = ["categorize", "cat"];
var EN_WIKTIONARY_PREFER_TOPICS_FOR_NEW_ADDITIONS = true;
var EN_WIKTIONARY_PREFER_CATLANGNAME_FOR_NEW_ADDITIONS = true;
function regexpEscape(string) {
return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}
function cleanUpText(wikitext) {
return wikitext
.replace( /<!--(\s|\S)*?-->/g, replaceByBlanks )
.replace( /<nowiki>(\s|\S)*?<\/nowiki>/g, replaceByBlanks );
}
function en_wiktionary_find_category_allMatches(regexp, wikitext, data_key, data) {
var copiedtext = cleanUpText(wikitext);
var result = [];
var curr_match = null;
while ((curr_match = regexp.exec(copiedtext)) !== null) {
var obj = { match: curr_match };
obj[data_key] = data;
result.push(obj);
}
result.re = regexp;
return result.length ? result : null;
}
function en_wiktionary_codeToCanonicalName(callback) {
$.get({
"url": mw.util.wikiScript('index'),
"data": {
"action": "raw",
"title": "Module:languages/code to canonical name.json"
},
"cache": true
}).then(function(r) {
var parsedData;
try {
parsedData = JSON.parse(r);
callback({ ok: true, data: parsedData });
} catch (e) {
console.error(e);
callback({ ok: false });
}
});
}
function en_wiktionary_populate_langdata(data) {
data.langnames = [];
data.name_to_code = {};
for (var key in data.code_to_name) {
if (data.code_to_name.hasOwnProperty(key)) {
var value = data.code_to_name[key];
data.langnames.push(value);
data.name_to_code[value] = key;
}
}
var escaped = data.langnames.map(regexpEscape);
// sort from longest to shortest to ensure greedy alternation
escaped.sort(function (a, b) { return b.length - a.length; });
data.catlangnameRegexp = "^(" + escaped.join("|") + ") (.+)";
}
var EN_WIKTIONARY_LANGUAGE_DATA_STORAGE_KEY = "HotCat-en-wiktionary-language-data";
function en_wiktionary_cache_langdata(callback) {
if (!window.sessionStorage) {
callback();
return;
}
if (window.sessionStorage.hasOwnProperty(EN_WIKTIONARY_LANGUAGE_DATA_STORAGE_KEY)) {
try {
var timeNow = new Date().getTime();
var data = JSON.parse(window.sessionStorage.getItem(EN_WIKTIONARY_LANGUAGE_DATA_STORAGE_KEY));
if (timeNow < data.expiry) {
EN_WIKTIONARY_LANGDATA = data.data;
EN_WIKTIONARY_LANGDATA_READY = true;
EN_WIKTIONARY_LANGDATA.catlangnameRegexpCompiled = null;
callback();
return;
}
} catch (e) { }
}
en_wiktionary_codeToCanonicalName(function (r) {
if (r.ok) {
EN_WIKTIONARY_LANGDATA = {};
EN_WIKTIONARY_LANGDATA.code_to_name = r.data;
en_wiktionary_populate_langdata(EN_WIKTIONARY_LANGDATA);
}
EN_WIKTIONARY_LANGDATA_READY = true;
if (window.sessionStorage)
window.sessionStorage.setItem(EN_WIKTIONARY_LANGUAGE_DATA_STORAGE_KEY, JSON.stringify({
expiry: new Date().getTime() + 30 * 60 * 1000, // expire in 30 minutes
data: EN_WIKTIONARY_LANGDATA
}));
callback();
});
}
var EN_WIKTIONARY_LANGDATA = null;
var EN_WIKTIONARY_LANGDATA_READY = false;
var EN_WIKTIONARY_LANGDATA_LOCK = false;
var EN_WIKTIONARY_LANGDATA_QUEUE = [];
function en_wiktionary_get_langdata(callback) {
if (EN_WIKTIONARY_LANGDATA_READY) {
callback();
} else {
if (!EN_WIKTIONARY_LANGDATA_LOCK) {
EN_WIKTIONARY_LANGDATA_LOCK = true;
en_wiktionary_cache_langdata(function() {
callback();
EN_WIKTIONARY_LANGDATA_QUEUE.forEach(function(fn) { fn(); });
});
return;
}
EN_WIKTIONARY_LANGDATA_QUEUE.push(callback);
}
}
function EN_WIKTIONARY_TOPICS_START(lang, bar_required) {
return "\\{\\{\\s*(?:" + EN_WIKTIONARY_TOPICS.join("|") + ")\\s*\\|\\s*" + lang + "\\s*\\|" + (bar_required ? "" : "?");
}
function EN_WIKTIONARY_CATLANGNAME_START(lang, bar_required) {
return "\\{\\{\\s*(?:" + EN_WIKTIONARY_CATLANGNAME.join("|") + ")\\s*\\|\\s*" + lang + "\\s*\\|" + (bar_required ? "" : "?");
}
function EN_WIKTIONARY_CATEGORIZE_START(lang, bar_required) {
return "\\{\\{\\s*(?:" + EN_WIKTIONARY_CATEGORIZE.join("|") + ")\\s*\\|\\s*" + (lang == true ? "(" : "") + "[a-z0-9-]+" + (lang == true ? ")" : "") + "\\s*\\|" + (bar_required ? "" : "?");
}
var EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS = "[^}]*";
var EN_WIKTIONARY_TEMPLATE_START_OF_PARAMETER = "?(?<=\\|)";
var EN_WIKTIONARY_TEMPLATE_END_OF_PARAMETER = "(?=\\s*[\|\}])";
var EN_WIKTIONARY_TEMPLATE_END = "\\}\\}";
var EN_WIKTIONARY_TEMPLATE_END_WITH_WHITESPACE = "\\s*\\}\\}\n?";
function en_wiktionary_category_name_detect(category) {
var topicsMatch = category.match(/^([a-z-]+):(.+)$/);
if (topicsMatch) {
return { type: "topics", lang: topicsMatch[1], topic: topicsMatch[2] };
}
if (EN_WIKTIONARY_LANGDATA) {
var data = EN_WIKTIONARY_LANGDATA;
if (!data.catlangnameRegexpCompiled) {
data.catlangnameRegexpCompiled = new RegExp(data.catlangnameRegexp);
}
var catlangnameMatch = category.match(data.catlangnameRegexpCompiled);
if (catlangnameMatch && data.name_to_code[catlangnameMatch[1]]) {
return { type: "catlangname",
lang: data.name_to_code[catlangnameMatch[1]],
topic: catlangnameMatch[2] };
}
}
return null;
}
function en_wiktionary_find_category(wikitext, category, once) {
if (!EN_WIKTIONARY_CONTENT_NAMESPACE) return null;
var catname = en_wiktionary_category_name_detect(category);
var data, matched;
if (catname) {
if (catname.type == "topics") {
var topicTemplateRegex = new RegExp(
EN_WIKTIONARY_TOPICS_START(catname.lang, true) +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
EN_WIKTIONARY_TEMPLATE_START_OF_PARAMETER +
regexpEscape(catname.topic) +
EN_WIKTIONARY_TEMPLATE_END_OF_PARAMETER +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
EN_WIKTIONARY_TEMPLATE_END, "g");
data = { lang: catname.lang, name: catname.topic };
if (once) {
matched = topicTemplateRegex.exec(wikitext);
if (matched) {
matched.enWiktTopics = data;
}
return matched;
}
return en_wiktionary_find_category_allMatches(topicTemplateRegex, wikitext, "enWiktTopics", data);
} else if (catname.type == "catlangname") {
var clnTemplateRegex = new RegExp(
EN_WIKTIONARY_CATLANGNAME_START(catname.lang, true) +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
EN_WIKTIONARY_TEMPLATE_START_OF_PARAMETER +
regexpEscape(catname.topic) +
EN_WIKTIONARY_TEMPLATE_END_OF_PARAMETER +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
EN_WIKTIONARY_TEMPLATE_END, "g");
data = { lang: catname.lang, name: catname.topic };
if (once) {
matched = clnTemplateRegex.exec(wikitext);
if (matched) {
matched.enWiktCatlangname = data;
}
return matched;
}
return en_wiktionary_find_category_allMatches(clnTemplateRegex, wikitext, "enWiktCatlangname", data);
}
}
var catTemplateRegex = new RegExp(EN_WIKTIONARY_CATEGORIZE_START(true, true) +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
EN_WIKTIONARY_TEMPLATE_START_OF_PARAMETER +
regexpEscape(category) +
EN_WIKTIONARY_TEMPLATE_END_OF_PARAMETER +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
EN_WIKTIONARY_TEMPLATE_END, "g");
var catTemplateMatches = wikitext.match(catTemplateRegex);
if (catTemplateMatches) {
data = { lang: catTemplateMatches[1], name: category };
if (once) {
if (catTemplateMatches) {
catTemplateMatches.enWiktCategorize = data;
}
return catTemplateMatches;
}
catTemplateRegex.lastIndex = 0;
return en_wiktionary_find_category_allMatches(catTemplateRegex, wikitext, "enWiktCategorize", data);
}
return null;
}
function en_wiktionary_is_special_match(match) {
return !!(match.enWiktTopics || match.enWiktCatlangname || match.enWiktCategorize);
}
function en_wiktionary_three_to_two_split(wikitext, match) {
var before, after, data, removeTopic;
if (match.enWiktTopics) { // {{topics}}?
data = match.enWiktTopics;
before = wikitext.substring(0, match.match.index);
after = wikitext.substring(match.match.index);
var topicTemplateRegex = new RegExp(
EN_WIKTIONARY_TOPICS_START(data.lang, true) +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
EN_WIKTIONARY_TEMPLATE_START_OF_PARAMETER +
regexpEscape(data.name) +
EN_WIKTIONARY_TEMPLATE_END_OF_PARAMETER +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
EN_WIKTIONARY_TEMPLATE_END + "\\s*?\n?");
removeTopic = function(inner) {
// remove parameter from {{topics}}
var remaining = inner.replace(new RegExp("\\|" +
regexpEscape(data.name) +
EN_WIKTIONARY_TEMPLATE_END_OF_PARAMETER), "");
// if it is now empty, remove it
remaining = remaining.replace(new RegExp(
EN_WIKTIONARY_TOPICS_START(data.lang, false) +
EN_WIKTIONARY_TEMPLATE_END_WITH_WHITESPACE), "");
return remaining;
};
return [before, after.replace(topicTemplateRegex, removeTopic)];
} else if (match.enWiktCatlangname) { // {{cln}}?
data = match.enWiktCatlangname;
before = wikitext.substring(0, match.match.index);
after = wikitext.substring(match.match.index);
var clnTemplateRegex = new RegExp(
EN_WIKTIONARY_CATLANGNAME_START(data.lang, true) +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
EN_WIKTIONARY_TEMPLATE_START_OF_PARAMETER +
regexpEscape(data.name) +
EN_WIKTIONARY_TEMPLATE_END_OF_PARAMETER +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
EN_WIKTIONARY_TEMPLATE_END + "\\s*?\n?");
removeTopic = function(inner) {
// remove parameter from {{cln}}
var remaining = inner.replace(new RegExp("\\|" +
regexpEscape(data.name) +
EN_WIKTIONARY_TEMPLATE_END_OF_PARAMETER), "");
// if it is now empty, remove it
remaining = remaining.replace(new RegExp(
EN_WIKTIONARY_CATLANGNAME_START(data.lang, false) +
EN_WIKTIONARY_TEMPLATE_END_WITH_WHITESPACE), "");
return remaining;
};
return [before, after.replace(clnTemplateRegex, removeTopic)];
} else if (match.enWiktCategorize) { // {{cat}}?
data = match.enWiktCategorize;
before = wikitext.substring(0, match.match.index);
after = wikitext.substring(match.match.index);
var catTemplateRegex = new RegExp(
EN_WIKTIONARY_CATEGORIZE_START(data.lang, true) +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
EN_WIKTIONARY_TEMPLATE_START_OF_PARAMETER +
regexpEscape(data.name) +
EN_WIKTIONARY_TEMPLATE_END_OF_PARAMETER +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
EN_WIKTIONARY_TEMPLATE_END + "\\s*?\n?");
removeTopic = function(inner) {
// remove parameter from {{cln}}
var remaining = inner.replace(new RegExp("\\|" +
regexpEscape(data.name) +
EN_WIKTIONARY_TEMPLATE_END_OF_PARAMETER), "");
// if it is now empty, remove it
remaining = remaining.replace(new RegExp(
EN_WIKTIONARY_CATEGORIZE_START(data.lang, false) +
EN_WIKTIONARY_TEMPLATE_END_WITH_WHITESPACE), "");
return remaining;
};
return [before, after.replace(catTemplateRegex, removeTopic)];
}
before = wikitext.substring(0, match.match.index);
after = wikitext.substring(match.match.index + match.match[0].length);
return [before, after];
}
function en_wiktionary_remove_category(wikitext, match) {
var before_after = en_wiktionary_three_to_two_split(wikitext, match);
return before_after[0] + before_after[1];
}
function en_wiktionary_find_insertionpoint(wikitext, category) {
var catname = en_wiktionary_category_name_detect(category);
if (catname) {
if (catname.lang) {
var langname = catname.langname || (EN_WIKTIONARY_LANGDATA ? EN_WIKTIONARY_LANGDATA.code_to_name[catname.lang] : null);
if (langname) {
// try to put category under the language L2
var l2_heading_this = "^==\\s*" + regexpEscape(langname) + "\\s*==$";
var end_of_l2 = "\\n*(?:^----$)?\\n*^==(?!=)\\s*.*\\s*==$";
var clean_wikitext = cleanUpText(wikitext);
var finalRegexp = "(" + l2_heading_this + "[\\s\\S]+?)" + end_of_l2;
var match = new RegExp(finalRegexp, "m").exec(clean_wikitext);
if (match) {
var rawPoint = match.index + match[1].length;
var noPreLine = false;
if (wikitext.substring(rawPoint - 2, rawPoint) == "]]") {
return {
idx: rawPoint,
onCat: true
};
}
var cookedPoint = rawPoint;
if (wikitext.substring(cookedPoint - 3, cookedPoint) == "}}\n") {
// if the preceding line has a topics/cln template, add noPreLine
var i = cookedPoint - 2;
while (i >= 0 && wikitext[i] !== '\n') {
i--;
}
i += 2;
var previousLine = wikitext.substring(i, cookedPoint - 1);
if (previousLine.match(new RegExp("^\\{\\{(?:" + EN_WIKTIONARY_TOPICS.join("|") + "|" + EN_WIKTIONARY_CATLANGNAME.join("|") + ")\\|"))) {
noPreLine = true;
}
}
// hotfix: fix newline placement
if (!noPreLine && wikitext.charAt(cookedPoint - 1) != "\n" && wikitext.substring(cookedPoint, cookedPoint + 2) == "\n\n") {
++cookedPoint;
}
return {
idx: cookedPoint,
onCat: false,
noPreLine: noPreLine
};
} else {
var l2match = new RegExp(l2_heading_this, "m").exec(clean_wikitext);
if (l2match) {
// assuming there are no more L2s...
var l2_regex = new RegExp("^==(?!=)\\s*.*\\s*==$", "gm");
l2_regex.lastIndex = l2match.index + l2match[0].length;
var l2nextmatch = l2_regex.exec(clean_wikitext);
if (!l2nextmatch) {
// no more L2s
return {
idx: -1,
onCat: false
};
}
}
}
}
}
}
return null; // fallback
}
function en_wiktionary_add_category_hook(wikitext, category, cat_point) {
var catname = en_wiktionary_category_name_detect(category);
if (catname) {
var match, splitPoint;
if (catname.type == "topics") {
// try to find the first language template that matches and
// add the category there
var topicTemplateRegex = new RegExp("(?:^|(?<=\\n))(" +
EN_WIKTIONARY_TOPICS_START(catname.lang, true) +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
")" + EN_WIKTIONARY_TEMPLATE_END);
match = topicTemplateRegex.exec(cleanUpText(wikitext));
if (match) {
splitPoint = match.index + match[1].length;
return wikitext.substring(0, splitPoint) + "|" + catname.topic + wikitext.substring(splitPoint);
}
}
if (catname.type == "catlangname") {
// try to find the first language template that matches and
// add the category there
var clnTemplateRegex = new RegExp("(?:^|(?<=\\n))(" +
EN_WIKTIONARY_CATLANGNAME_START(catname.lang, true) +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
")" + EN_WIKTIONARY_TEMPLATE_END);
match = clnTemplateRegex.exec(cleanUpText(wikitext));
if (match) {
splitPoint = match.index + match[1].length;
return wikitext.substring(0, splitPoint) + "|" + catname.topic + wikitext.substring(splitPoint);
}
}
}
return null; // fallback
}
function en_wiktionary_add_category_string(category, onCat) {
var catname = en_wiktionary_category_name_detect(category);
if (catname) {
if (catname.type == "topics" && EN_WIKTIONARY_PREFER_TOPICS_FOR_NEW_ADDITIONS && !onCat) {
return "{{" + EN_WIKTIONARY_TOPICS[0] + "|" + catname.lang + "|" + catname.topic + "}}";
}
if (catname.type == "catlangname" && EN_WIKTIONARY_PREFER_CATLANGNAME_FOR_NEW_ADDITIONS && !onCat) {
return "{{" + EN_WIKTIONARY_CATLANGNAME[0] + "|" + catname.lang + "|" + catname.topic + "}}";
}
}
return null; // fallback
}
// END OF en.wiktionary EXTRA CODE
// Don't use mw.config.get() as that takes a copy of the config, and so doesn't
// account for values changing, e.g. wgCurRevisionId after a VE edit
var
conf = mw.config.values,
// when running on mobile domain - do not use wgServer.
wgServer = window.location.host.indexOf('.m.') > -1 ?
'//' + window.location.host : mw.config.get( 'wgServer' );
// Guard against double inclusions (in old IE/Opera element ids become window properties)
if ( ( window.HotCat && !window.HotCat.nodeName ) ||
conf.wgAction === 'edit' ) // Not on edit mode
return;
// Configuration stuff.
var HC = window.HotCat = {
// Localize these messages to the main language of your wiki.
messages: {
cat_removed: 'removed [[Category:$1]]',
template_removed: 'removed {{[[Category:$1]]}}',
cat_added: 'added [[Category:$1]]',
cat_keychange: 'new key for [[Category:$1]]: "$2"', // $2 is the new key
cat_notFound: 'Category "$1" not found',
cat_exists: 'Category "$1" already exists; not added.',
cat_resolved: ' (redirect [[Category:$1]] resolved)',
uncat_removed: 'removed {{uncategorized}}',
separator: '; ',
// Some text to prefix to the edit summary.
prefix: '',
// Some text to append to the edit summary. Named 'using' for historical reasons. If you prefer
// to have a marker at the front, use prefix and set this to the empty string.
using: ' using [[Help:Gadget-HotCat|HotCat]]',
// $1 is replaced by a number. If your language has several plural forms (c.f. [[:en:Dual (grammatical form)]]),
// you can set this to an array of strings suitable for passing to mw.language.configPlural().
// If that function doesn't exist, HotCat will simply fall back to using the last
// entry in the array.
multi_change: '$1 categories',
// Button text. Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
// see localization hook below.
commit: 'Save',
// Button text. Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
// see localization hook below.
ok: 'OK',
// Button text. Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
// see localization hook below.
cancel: 'Cancel',
// Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
// see localization hook below.
multi_error: 'Could not retrieve the page text from the server. Therefore, your category changes ' +
'cannot be saved. We apologize for the inconvenience.',
// Defaults to '[[' + category_canonical + ':$1]]'. Can be overridden if in the short edit summaries
// not the standard category name should be used but, say, a shorter namespace alias. $1 is replaced
// by a category name.
short_catchange: null
},
// Plural of category_canonical.
categories: 'Categories',
// Any category in this category is deemed a disambiguation category; i.e., a category that should not contain
// any items, but that contains links to other categories where stuff should be categorized. If you don't have
// that concept on your wiki, set it to null. Use blanks, not underscores.
disambig_category: 'Disambiguation',
// Any category in this category is deemed a (soft) redirect to some other category defined by a link
// to another non-blacklisted category. If your wiki doesn't have soft category redirects, set this to null.
// If a soft-redirected category contains more than one link to another non-blacklisted category, it's considered
// a disambiguation category instead.
redir_category: 'Category redirects',
// The little modification links displayed after category names. U+2212 is a minus sign; U+2193 and U+2191 are
// downward and upward pointing arrows. Do not use ↓ and ↑ in the code!
links: {
change: '(±)',
remove: '(\u2212)',
add: '(+)',
restore: '(×)',
undo: '(×)',
down: '(\u2193)',
up: '(\u2191)'
},
changeTag: conf.wgUserName ? 'HotCat' : '', // if tag is missing, edit is rejected
// The tooltips for the above links
tooltips: {
change: 'Modify',
remove: 'Remove',
add: 'Add a new category',
restore: 'Undo changes',
undo: 'Undo changes',
down: 'Open for modifying and display subcategories',
up: 'Open for modifying and display parent categories'
},
// The HTML content of the "enter multi-mode" link at the front.
addmulti: '<span>+<sup>+</sup></span>',
// Tooltip for the "enter multi-mode" link
multi_tooltip: 'Modify several categories',
// Return true to disable HotCat.
disable: function () {
var ns = conf.wgNamespaceNumber;
var nsIds = conf.wgNamespaceIds;
return (
ns < 0 || // Special pages; Special:Upload is handled differently
ns === 10 || // Templates
ns === 828 || // Module (Lua)
ns === 8 || // MediaWiki
ns === 6 && !conf.wgArticleId || // Non-existing file pages
ns === 2 && /\.(js|css)$/.test( conf.wgTitle ) || // User scripts
nsIds &&
( ns === nsIds.creator ||
ns === nsIds.timedtext ||
ns === nsIds.institution ) );
},
// A regexp matching a templates used to mark uncategorized pages, if your wiki does have that.
// If not, set it to null.
uncat_regexp: /\{\{\s*[Uu]ncategorized\s*[^}]*\}\}\s*(<!--.*?-->\s*)?/g,
// The images used for the little indication icon. Should not need changing.
existsYes: '//upload.wikimedia.org/wikipedia/commons/thumb/b/be/P_yes.svg/20px-P_yes.svg.png',
existsNo: '//upload.wikimedia.org/wikipedia/commons/thumb/4/42/P_no.svg/20px-P_no.svg.png',
// a list of categories which can be removed by removing a template
// key: the category without namespace
// value: A regexp matching the template name, again without namespace
// If you don't have this at your wiki, or don't want this, set it to an empty object {}.
template_categories: {},
// Names for the search engines
engine_names: {
searchindex: 'Search index',
pagelist: 'Page list',
combined: 'Combined search',
subcat: 'Subcategories',
parentcat: 'Parent categories'
},
// Override the decision of whether HotCat should help users by automatically
// capitalising the title in the user input text if the wiki has case-sensitive page names.
// Basically, this will make an API query to check the MediaWiki configuration and HotCat then sets
// this to true for most wikis, and to false on Wiktionary.
//
// You can set this directly if there is a problem with it. For example, Georgian Wikipedia (kawiki),
// is known to have different capitalisation logic between MediaWiki PHP and JavaScript. As such, automatic
// case changes in JavaScript by HotCat would be wrong.
capitalizePageNames: null,
// If upload_disabled is true, HotCat will not be used on the Upload form.
upload_disabled: false,
// Single regular expression matching blacklisted categories that cannot be changed or
// added using HotCat. For instance /\bstubs?$/ (any category ending with the word "stub"
// or "stubs"), or /(\bstubs?$)|\bmaintenance\b/ (stub categories and any category with the
// word "maintenance" in its title.
blacklist: null,
// Stuff changeable by users:
// Background for changed categories in multi-edit mode. Default is a very light salmon pink.
bg_changed: '#FCA',
// If true, HotCat will never automatically submit changes. HotCat will only open an edit page with
// the changes; users must always save explicitly.
no_autocommit: false,
// If true, the "category deletion" link "(-)" will never save automatically but always show an
// edit page where the user has to save the edit manually. Is false by default because that's the
// traditional behavior. This setting overrides no_autocommit for "(-)" links.
del_needs_diff: false,
// Time, in milliseconds, that HotCat waits after a keystroke before making a request to the
// server to get suggestions.
suggest_delay: 100,
// Default width, in characters, of the text input field.
editbox_width: 40,
// One of the engine_names above, to be used as the default suggestion engine.
suggestions: 'combined',
// If true, always use the default engine, and never display a selector.
fixed_search: false,
// If false, do not display the "up" and "down" links
use_up_down: true,
// Default list size
listSize: 10,
// If true, single category changes are marked as minor edits. If false, they're not.
single_minor: true,
// If true, never add a page to the user's watchlist. If false, pages get added to the watchlist if
// the user has the "Add pages I edit to my watchlist" or the "Add pages I create to my watchlist"
// options in his or her preferences set.
dont_add_to_watchlist: false,
shortcuts: null,
addShortcuts: function ( map ) {
if ( !map ) return;
window.HotCat.shortcuts = window.HotCat.shortcuts || {};
for ( var k in map ) {
if ( !map.hasOwnProperty( k ) || typeof k !== 'string' ) continue;
var v = map[ k ];
if ( typeof v !== 'string' ) continue;
k = k.replace( /^\s+|\s+$/g, '' );
v = v.replace( /^\s+|\s+$/g, '' );
if ( !k.length || !v.length ) continue;
window.HotCat.shortcuts[ k ] = v;
}
}
};
// More backwards compatibility. We have a few places where we test for the browser: once for
// Safari < 3.0, and twice for WebKit (Chrome or Safari, any versions)
var ua = navigator.userAgent.toLowerCase();
var is_webkit = /applewebkit\/\d+/.test( ua ) && ua.indexOf( 'spoofer' ) < 0;
var cat_prefix = null;
var noSuggestions = false;
function LoadTrigger( needed ) {
// Define methods in a closure so that self reference is available,
// also allows method calls to be detached.
var self = this;
self.queue = [];
self.needed = needed;
self.register = function ( callback ) {
if ( self.needed <= 0 ) callback(); // Execute directly
else self.queue.push( callback );
};
self.loaded = function () {
self.needed--;
if ( self.needed === 0 ) {
// Run queued callbacks once
for ( var i = 0; i < self.queue.length; i++ ) self.queue[ i ]();
self.queue = [];
}
};
}
// Used to delay running the HotCat setup until /local_defaults and localizations have been loaded.
var loadTrigger = new LoadTrigger( 2 );
function load( uri, callback ) {
var s = document.createElement( 'script' );
s.src = uri;
var called = false;
s.onload = s.onerror = function () {
if ( !called && callback ) {
called = true;
callback();
}
if ( s.parentNode ) {
s.parentNode.removeChild( s );
}
};
document.head.appendChild( s );
}
function loadJS( page, callback ) {
load( wgServer + conf.wgScript + '?title=' + encodeURIComponent( page ) + '&action=raw&ctype=text/javascript', callback );
}
function loadURI( href, callback ) {
var url = href;
if ( url.substring( 0, 2 ) === '//' ) url = window.location.protocol + url; else if ( url.substring( 0, 1 ) === '/' ) url = wgServer + url;
load( url, callback );
}
// Load local configurations, overriding the pre-set default values in the HotCat object above. This is always loaded
// from the wiki where this script is executing, even if this script itself is hotlinked from Commons. This can
// be used to change the default settings, or to provide localized interface texts for edit summaries and so on.
loadJS( 'MediaWiki:Gadget-HotCat.js/local_defaults', loadTrigger.loaded );
// Load localized UI texts. These are the texts that HotCat displays on the page itself. Texts shown in edit summaries
// should be localized in /local_defaults above.
if ( conf.wgUserLanguage !== 'en' ) {
// Lupo: somebody thought it would be a good idea to add this. So the default is true, and you have to set it to false
// explicitly if you're not on Commons and don't want that.
if ( window.hotcat_translations_from_commons === undefined ) window.hotcat_translations_from_commons = true;
// Localization hook to localize HotCat messages, tooltips, and engine names for wgUserLanguage.
if ( window.hotcat_translations_from_commons && wgServer.indexOf( '//commons' ) < 0 ) {
loadURI( '//commons.wikimedia.org/w/index.php?title=' +
'MediaWiki:Gadget-HotCat.js/' + conf.wgUserLanguage +
'&action=raw&ctype=text/javascript', loadTrigger.loaded );
} else {
// Load translations locally
loadJS( 'MediaWiki:Gadget-HotCat.js/' + conf.wgUserLanguage, loadTrigger.loaded );
}
} else {
loadTrigger.loaded();
}
// No further changes should be necessary here.
// The following regular expression strings are used when searching for categories in wikitext.
var wikiTextBlank = '[\\t _\\xA0\\u1680\\u180E\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000]+';
var wikiTextBlankRE = new RegExp( wikiTextBlank, 'g' );
// Regexp for handling blanks inside a category title or namespace name.
// See http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/includes/Title.php?revision=104051&view=markup#l2722
// See also http://www.fileformat.info/info/unicode/category/Zs/list.htm
// MediaWiki collapses several contiguous blanks inside a page title to one single blank. It also replace a
// number of special whitespace characters by simple blanks. And finally, blanks are treated as underscores.
// Therefore, when looking for page titles in wikitext, we must handle all these cases.
// Note: we _do_ include the horizontal tab in the above list, even though the MediaWiki software for some reason
// appears to not handle it. The zero-width space \u200B is _not_ handled as a space inside titles by MW.
var wikiTextBlankOrBidi = '[\\t _\\xA0\\u1680\\u180E\\u2000-\\u200B\\u200E\\u200F\\u2028-\\u202F\\u205F\\u3000]*';
// Whitespace regexp for handling whitespace between link components. Including the horizontal tab, but not \n\r\f\v:
// a link must be on one single line.
// MediaWiki also removes Unicode bidi override characters in page titles (and namespace names) completely.
// This is *not* handled, as it would require us to allow any of [\u200E\u200F\u202A-\u202E] between any two
// characters inside a category link. It _could_ be done though... We _do_ handle strange spaces, including the
// zero-width space \u200B, and bidi overrides between the components of a category link (adjacent to the colon,
// or adjacent to and inside of "[[" and "]]").
// First auto-localize the regexps for the category and the template namespaces.
var formattedNamespaces = conf.wgFormattedNamespaces;
var namespaceIds = conf.wgNamespaceIds;
function autoLocalize( namespaceNumber, fallback ) {
function createRegexpStr( name ) {
if ( !name || !name.length ) return '';
var regex_name = '';
for ( var i = 0; i < name.length; i++ ) {
var initial = name.charAt( i ),
ll = initial.toLowerCase(),
ul = initial.toUpperCase();
if ( ll === ul ) regex_name += initial; else regex_name += '[' + ll + ul + ']';
}
return regex_name
.replace( /([\\^$.?*+()])/g, '\\$1' )
.replace( wikiTextBlankRE, wikiTextBlank );
}
fallback = fallback.toLowerCase();
var canonical = formattedNamespaces[ String( namespaceNumber ) ].toLowerCase();
var regexp = createRegexpStr( canonical );
if ( fallback && canonical !== fallback ) regexp += '|' + createRegexpStr( fallback );
if ( namespaceIds ) {
for ( var cat_name in namespaceIds ) {
if (
typeof cat_name === 'string' &&
cat_name.toLowerCase() !== canonical &&
cat_name.toLowerCase() !== fallback &&
namespaceIds[ cat_name ] === namespaceNumber
) {
regexp += '|' + createRegexpStr( cat_name );
}
}
}
return regexp;
}
HC.category_canonical = formattedNamespaces[ '14' ];
HC.category_regexp = autoLocalize( 14, 'category' );
if ( formattedNamespaces[ '10' ] ) HC.template_regexp = autoLocalize( 10, 'template' );
// Utility functions. Yes, this duplicates some functionality that also exists in other places, but
// to keep this whole stuff in a single file not depending on any other on-wiki JavaScripts, we re-do
// these few operations here.
function make( arg, literal ) {
if ( !arg ) return null;
return literal ? document.createTextNode( arg ) : document.createElement( arg );
}
function param( name, uri ) {
uri = uri || document.location.href;
var re = new RegExp( '[&?]' + name + '=([^&#]*)' );
var m = re.exec( uri );
if ( m && m.length > 1 ) return decodeURIComponent( m[ 1 ] );
return null;
}
function title( href ) {
if ( !href ) return null;
var script = conf.wgScript + '?';
if ( href.indexOf( script ) === 0 || href.indexOf( wgServer + script ) === 0 || wgServer.substring( 0, 2 ) === '//' && href.indexOf( document.location.protocol + wgServer + script ) === 0 ) {
// href="/w/index.php?title=..."
return param( 'title', href );
} else {
// href="/wiki/..."
var prefix = conf.wgArticlePath.replace( '$1', '' );
if ( href.indexOf( prefix ) ) prefix = wgServer + prefix; // Fully expanded URL?
if ( href.indexOf( prefix ) && prefix.substring( 0, 2 ) === '//' ) prefix = document.location.protocol + prefix; // Protocol-relative wgServer?
if ( href.indexOf( prefix ) === 0 ) return decodeURIComponent( href.substring( prefix.length ) );
}
return null;
}
function hasClass( elem, name ) {
return ( ' ' + elem.className + ' ' ).indexOf( ' ' + name + ' ' ) >= 0;
}
function capitalize( str ) {
if ( !str || !str.length ) return str;
return str.substr( 0, 1 ).toUpperCase() + str.substr( 1 );
}
function wikiPagePath( pageName ) {
// Note: do not simply use encodeURI, it doesn't encode '&', which might break if wgArticlePath actually has the $1 in
// a query parameter.
return conf.wgArticlePath.replace( '$1', encodeURIComponent( pageName ).replace( /%3A/g, ':' ).replace( /%2F/g, '/' ) );
}
function escapeRE( str ) {
return str.replace( /([\\^$.?*+()[\]])/g, '\\$1' );
}
function substituteFactory( options ) {
options = options || {};
var lead = options.indicator || '$';
var indicator = escapeRE( lead );
var lbrace = escapeRE( options.lbrace || '{' );
var rbrace = escapeRE( options.rbrace || '}' );
var re;
re = new RegExp(
// $$
'(?:' + indicator + '(' + indicator + '))|' +
// $0, $1
'(?:' + indicator + '(\\d+))|' +
// ${key}
'(?:' + indicator + '(?:' + lbrace + '([^' + lbrace + rbrace + ']+)' + rbrace + '))|' +
// $key (only if first char after $ is not $, digit, or { )
'(?:' + indicator + '(?!(?:[' + indicator + lbrace + ']|\\d))(\\S+?)\\b)',
'g'
);
// Replace $1, $2, or ${key1}, ${key2}, or $key1, $key2 by values from map. $$ is replaced by a single $.
return function ( str, map ) {
if ( !map ) return str;
return str.replace( re, function ( match, prefix, idx, key, alpha ) {
if ( prefix === lead ) return lead;
var k = alpha || key || idx;
var replacement = typeof map[ k ] === 'function' ? map[ k ]( match, k ) : map[ k ];
return typeof replacement === 'string' ? replacement : ( replacement || match );
} );
};
}
var substitute = substituteFactory();
var replaceShortcuts = ( function () {
var replaceHash = substituteFactory( {
indicator: '#',
lbrace: '[',
rbrace: ']'
} );
return function ( str, map ) {
var s = replaceHash( str, map );
return HC.capitalizePageNames ? capitalize( s ) : s;
};
}() );
// Text modification
var findCatsRE =
new RegExp( '\\[\\[' + wikiTextBlankOrBidi + '(?:' + HC.category_regexp + ')' + wikiTextBlankOrBidi + ':[^\\]]+\\]\\]', 'g' );
function replaceByBlanks( match ) {
return match.replace( /(\s|\S)/g, ' ' ); // /./ doesn't match linebreaks. /(\s|\S)/ does.
}
function find_category( wikitext, category, once ) {
var cat_regex = null;
var result_hook = en_wiktionary_find_category(wikitext, category, once);
if (result_hook) {
return result_hook;
}
if ( HC.template_categories[ category ] ) {
cat_regex = new RegExp(
'\\{\\{' + wikiTextBlankOrBidi + '(' + HC.template_regexp + '(?=' + wikiTextBlankOrBidi + ':))?' + wikiTextBlankOrBidi +
'(?:' + HC.template_categories[ category ] + ')' +
wikiTextBlankOrBidi + '(\\|.*?)?\\}\\}',
'g'
);
} else {
var cat_name = escapeRE( category );
var initial = cat_name.substr( 0, 1 );
cat_regex = new RegExp(
'\\[\\[' + wikiTextBlankOrBidi + '(' + HC.category_regexp + ')' + wikiTextBlankOrBidi + ':' + wikiTextBlankOrBidi +
( initial === '\\' || !HC.capitalizePageNames ?
initial :
'[' + initial.toUpperCase() + initial.toLowerCase() + ']' ) +
cat_name.substring( 1 ).replace( wikiTextBlankRE, wikiTextBlank ) +
wikiTextBlankOrBidi + '(\\|.*?)?\\]\\]',
'g'
);
}
if ( once ) return cat_regex.exec( wikitext );
var copiedtext = wikitext
.replace( /<!--(\s|\S)*?-->/g, replaceByBlanks )
.replace( /<nowiki>(\s|\S)*?<\/nowiki>/g, replaceByBlanks );
var result = [];
var curr_match = null;
while ( ( curr_match = cat_regex.exec( copiedtext ) ) !== null ) {
result.push( {
match: curr_match
} );
}
result.re = cat_regex;
return result; // An array containing all matches, with positions, in result[ i ].match
}
var interlanguageRE = null;
function change_category( wikitext, toRemove, toAdd, key, is_hidden ) {
function find_insertionpoint( wikitext, category ) {
var copiedtext = wikitext
.replace( /<!--(\s|\S)*?-->/g, replaceByBlanks )
.replace( /<nowiki>(\s|\S)*?<\/nowiki>/g, replaceByBlanks );
// Search in copiedtext to avoid that we insert inside an HTML comment or a nowiki "element".
var result = en_wiktionary_find_insertionpoint( wikitext, category );
if (result) {
return result;
}
var index = -1;
findCatsRE.lastIndex = 0;
while ( findCatsRE.exec( copiedtext ) !== null ) index = findCatsRE.lastIndex;
if ( index < 0 ) {
// Find the index of the first interlanguage link...
var match = null;
if ( !interlanguageRE ) {
// Approximation without API: interlanguage links start with 2 to 3 lower case letters, optionally followed by
// a sequence of groups consisting of a dash followed by one or more lower case letters. Exceptions are "simple"
// and "tokipona".
match = /((^|\n\r?)(\[\[\s*(([a-z]{2,3}(-[a-z]+)*)|simple|tokipona)\s*:[^\]]+\]\]\s*))+$/.exec( copiedtext );
} else {
match = interlanguageRE.exec( copiedtext );
}
if ( match ) index = match.index;
return {
idx: index,
onCat: false
};
}
return {
idx: index,
onCat: index >= 0
};
}
var summary = [],
nameSpace = HC.category_canonical,
cat_point = -1, // Position of removed category;
keyChange = ( toRemove && toAdd && toRemove === toAdd && toAdd.length ),
matches,
noOnCatOverride = false;
if ( key ) key = '|' + key;
// Remove
if ( toRemove && toRemove.length ) {
matches = find_category( wikitext, toRemove );
if ( !matches || !matches.length ) {
return {
text: wikitext,
summary: summary,
error: HC.messages.cat_notFound.replace( /\$1/g, toRemove )
};
} else {
var after = wikitext;
for (var i = matches.length - 1; i > 0; i--) {
after = en_wiktionary_remove_category(after, matches[i]);
}
var before_after = en_wiktionary_three_to_two_split(after, matches[0]);
noOnCatOverride = en_wiktionary_is_special_match(matches[0]);
var before = before_after[0];
after = before_after[1];
if ( toAdd ) {
// nameSpace = matches[ 0 ].match[ 1 ] || nameSpace; Canonical namespace should be always preferred
if ( key === null ) key = matches[ 0 ].match[ 2 ];
// Remember the category key, if any.
}
// Remove whitespace (properly): strip whitespace, but only up to the next line feed.
// If we then have two linefeeds in a row, remove one. Otherwise, if we have two non-
// whitespace characters, insert a blank.
var i = before.length - 1;
while ( i >= 0 && before.charAt( i ) !== '\n' && before.substr( i, 1 ).search( /\s/ ) >= 0 ) i--;
var j = 0;
while ( j < after.length && after.charAt( j ) !== '\n' && after.substr( j, 1 ).search( /\s/ ) >= 0 ) j++;
if ( i >= 0 && before.charAt( i ) === '\n' && ( !after.length || j < after.length && after.charAt( j ) === '\n' ) ) i--;
if ( i >= 0 ) before = before.substring( 0, i + 1 ); else before = '';
if ( j < after.length ) after = after.substring( j ); else after = '';
if (
before.length && before.substring( before.length - 1 ).search( /\S/ ) >= 0 &&
after.length && after.substr( 0, 1 ).search( /\S/ ) >= 0
) {
before += ' ';
}
cat_point = before.length;
if ( cat_point === 0 && after.length && after.substr( 0, 1 ) === '\n' ) after = after.substr( 1 );
wikitext = before + after;
if ( !keyChange ) {
if ( HC.template_categories[ toRemove ] ) { summary.push( HC.messages.template_removed.replace( /\$1/g, toRemove ) ); } else { summary.push( HC.messages.cat_removed.replace( /\$1/g, toRemove ) ); }
}
}
}
// Add
if ( toAdd && toAdd.length ) {
matches = find_category( wikitext, toAdd );
if ( matches && matches.length ) {
// Already exists
return {
text: wikitext,
summary: summary,
error: HC.messages.cat_exists.replace( /\$1/g, toAdd )
};
} else {
var hooked = en_wiktionary_add_category_hook( wikitext, toAdd, cat_point );
if (hooked) {
wikitext = hooked;
summary.push( HC.messages.cat_added.replace( /\$1/g, toAdd ) );
} else {
var onCat = false;
var noPreLine = false;
if ( cat_point < 0 ) {
var point = find_insertionpoint ( wikitext, toAdd );
cat_point = point.idx;
onCat = point.onCat;
noPreLine = point.noPreLine;
} else {
onCat = true;
}
var newcatstring = en_wiktionary_add_category_string(toAdd, onCat && !noOnCatOverride) || ('[[' + nameSpace + ':' + toAdd + ( key || '' ) + ']]');
if ( cat_point >= 0 ) {
var suffix = wikitext.substring( cat_point );
wikitext = wikitext.substring( 0, cat_point ) + ( cat_point > 0 && !noPreLine ? '\n' : '' ) + newcatstring + ( !onCat ? '\n' : '' );
if ( suffix.length && suffix.substr( 0, 1 ) !== '\n' ) wikitext += '\n' + suffix; else wikitext += suffix;
} else {
if ( wikitext.length && wikitext.substr( wikitext.length - 1, 1 ) !== '\n' ) wikitext += '\n';
wikitext += ( wikitext.length ? '\n' : '' ) + newcatstring;
}
if ( keyChange ) {
var k = key || '';
if ( k.length ) k = k.substr( 1 );
summary.push( substitute( HC.messages.cat_keychange, [ null, toAdd, k ] ) );
} else {
summary.push( HC.messages.cat_added.replace( /\$1/g, toAdd ) );
}
if ( HC.uncat_regexp && !is_hidden ) {
var txt = wikitext.replace( HC.uncat_regexp, '' ); // Remove "uncat" templates
if ( txt.length !== wikitext.length ) {
wikitext = txt;
summary.push( HC.messages.uncat_removed );
}
}
}
}
}
return {
text: wikitext,
summary: summary,
error: null
};
}
// The real HotCat UI
function evtKeys( e ) {
/* eslint-disable no-bitwise */
var code = 0;
if ( e.ctrlKey ) { // All modern browsers
// Ctrl-click seems to be overloaded in FF/Mac (it opens a pop-up menu), so treat cmd-click
// as a ctrl-click, too.
if ( e.ctrlKey || e.metaKey ) code |= 1;
if ( e.shiftKey ) code |= 2;
}
return code;
}
function evtKill( e ) {
if ( e.preventDefault ) {
e.preventDefault();
e.stopPropagation();
} else {
e.cancelBubble = true;
}
return false;
}
var catLine = null,
onUpload = false,
editors = [],
commitButton = null,
commitForm = null,
multiSpan = null,
pageText = null,
pageTime = null,
pageWatched = false,
watchCreate = false,
watchEdit = false,
minorEdits = false,
editToken = null,
is_rtl = false,
serverTime = null,
lastRevId = null,
pageTextRevId = null,
conflictingUser = null,
newDOM = false; // true if MediaWiki serves the new UL-LI DOM for categories
function CategoryEditor() {
this.initialize.apply( this, arguments );
}
function setPage( json ) {
var startTime = null;
if ( json && json.query ) {
if ( json.query.pages ) {
var page = json.query.pages[ !conf.wgArticleId ? '-1' : String( conf.wgArticleId ) ];
if ( page ) {
if ( page.revisions && page.revisions.length ) {
// Revisions are sorted by revision ID, hence [ 0 ] is the one we asked for, and possibly there's a [ 1 ] if we're
// not on the latest revision (edit conflicts and such).
pageText = page.revisions[ 0 ][ '*' ];
if ( page.revisions[ 0 ].timestamp ) pageTime = page.revisions[ 0 ].timestamp.replace( /\D/g, '' );
if ( page.revisions[ 0 ].revid ) pageTextRevId = page.revisions[ 0 ].revid;
if ( page.revisions.length > 1 ) conflictingUser = page.revisions[ 1 ].user;
}
if ( page.lastrevid ) lastRevId = page.lastrevid;
if ( page.starttimestamp ) startTime = page.starttimestamp.replace( /\D/g, '' );
pageWatched = typeof page.watched === 'string';
if ( json.query.tokens ) editToken = json.query.tokens.csrftoken;
if ( page.langlinks && ( !json[ 'query-continue' ] || !json[ 'query-continue' ].langlinks ) ) {
// We have interlanguage links, and we got them all.
var re = '';
for ( var i = 0; i < page.langlinks.length; i++ ) re += ( i > 0 ? '|' : '' ) + page.langlinks[ i ].lang.replace( /([\\^$.?*+()])/g, '\\$1' );
if ( re.length ) interlanguageRE = new RegExp( '((^|\\n\\r?)(\\[\\[\\s*(' + re + ')\\s*:[^\\]]+\\]\\]\\s*))+$' );
}
}
}
// Siteinfo
if ( json.query.general ) {
if ( json.query.general.time && !startTime ) startTime = json.query.general.time.replace( /\D/g, '' );
if ( HC.capitalizePageNames === null ) {
// ResourceLoader's JSParser doesn't like .case, so override eslint.
// eslint-disable-next-line dot-notation
HC.capitalizePageNames = ( json.query.general[ 'case' ] === 'first-letter' );
}
}
serverTime = startTime;
// Userinfo
if ( json.query.userinfo && json.query.userinfo.options ) {
watchCreate = !HC.dont_add_to_watchlist && json.query.userinfo.options.watchcreations === '1';
watchEdit = !HC.dont_add_to_watchlist && json.query.userinfo.options.watchdefault === '1';
minorEdits = json.query.userinfo.options.minordefault === 1;
// If the user has the "All edits are minor" preference enabled, we should honor that
// for single category changes, no matter what the site configuration is.
if ( minorEdits ) HC.single_minor = true;
}
}
}
var saveInProgress = false;
function initiateEdit( doEdit, failure ) {
if ( saveInProgress ) return;
saveInProgress = true;
var oldButtonState;
if ( commitButton ) {
oldButtonState = commitButton.disabled;
commitButton.disabled = true;
}
function fail() {
saveInProgress = false;
if ( commitButton ) commitButton.disabled = oldButtonState;
failure.apply( this, arguments );
}
// Must use Ajax here to get the user options and the edit token.
$.getJSON(
wgServer + conf.wgScriptPath + '/api.php?' +
'format=json&action=query&rawcontinue=&titles=' + encodeURIComponent( conf.wgPageName ) +
'&prop=info%7Crevisions%7Clanglinks&inprop=watched&rvprop=content%7Ctimestamp%7Cids%7Cuser&lllimit=500' +
'&rvlimit=2&rvdir=newer&rvstartid=' + conf.wgCurRevisionId + '&meta=siteinfo%7Cuserinfo%7Ctokens&type=csrf&uiprop=options',
function ( json ) {
setPage( json );
doEdit( fail );
}
).fail( function ( req ) {
fail( req.status + ' ' + req.statusText );
} );
}
function multiChangeMsg( count ) {
var msg = HC.messages.multi_change;
if ( typeof msg !== 'string' && msg.length )
if ( mw.language && mw.language.convertPlural ) { msg = mw.language.convertPlural( count, msg ); } else { msg = msg[ msg.length - 1 ]; }
return substitute( msg, [ null, String( count ) ] );
}
function currentTimestamp() {
var now = new Date();
var ts = String( now.getUTCFullYear() );
function two( s ) {
return s.substr( s.length - 2 );
}
ts +=
two( '0' + ( now.getUTCMonth() + 1 ) ) +
two( '0' + now.getUTCDate() ) +
two( '00' + now.getUTCHours() ) +
two( '00' + now.getUTCMinutes() ) +
two( '00' + now.getUTCSeconds() );
return ts;
}
function performChanges( failure, singleEditor ) {
if ( pageText === null ) {
failure( HC.messages.multi_error );
return;
}
// Backwards compatibility after message change (added $2 to cat_keychange)
if ( HC.messages.cat_keychange.indexOf( '$2' ) < 0 ) HC.messages.cat_keychange += '"$2"';
// More backwards-compatibility with earlier HotCat versions:
if ( !HC.messages.short_catchange ) HC.messages.short_catchange = '[[' + HC.category_canonical + ':$1]]';
// Create a form and submit it. We don't use the edit API (api.php?action=edit) because
// (a) sensibly reporting back errors like edit conflicts is always a hassle, and
// (b) we want to show a diff for multi-edits anyway, and
// (c) we want to trigger onsubmit events, allowing user code to intercept the edit.
// Using the form, we can do (b) and (c), and we get (a) for free. And, of course, using the form
// automatically reloads the page with the updated categories on a successful submit, which
// we would have to do explicitly if we used the edit API.
var action;
// Normally, we don't have to care about edit conflicts. If some other user edited the page in the meantime, the
// server will take care of it and merge the edit automatically or present an edit conflict screen. However, the
// server suppresses edit conflicts with oneself. Hence, if we have a conflict, and the conflicting user is the
// current user, then we set the "oldid" value and switch to diff, which gives the "you are editing an old version;
// if you save, any more recent changes will be lost" screen.
var selfEditConflict = ( lastRevId !== null && lastRevId !== conf.wgCurRevisionId || pageTextRevId !== null &&
pageTextRevId !== conf.wgCurRevisionId ) && conflictingUser && conflictingUser === conf.wgUserName;
if ( singleEditor && !singleEditor.noCommit && !HC.no_autocommit && editToken && !selfEditConflict ) {
// If we do have an edit conflict, but not with ourself, that's no reason not to attempt to save: the server side may actually be able to
// merge the changes. We just need to make sure that we do present a diff view if it's a self edit conflict.
commitForm.wpEditToken.value = editToken;
action = commitForm.wpDiff;
if ( action ) action.name = action.value = 'wpSave';
} else {
action = commitForm.wpSave;
if ( action ) action.name = action.value = 'wpDiff';
}
var result = {
text: pageText
},
changed = [],
added = [],
deleted = [],
changes = 0,
toEdit = singleEditor ? [ singleEditor ] : editors,
error = null,
edit,
i;
for ( i = 0; i < toEdit.length; i++ ) {
edit = toEdit[ i ];
if ( edit.state === CategoryEditor.CHANGED ) {
result = change_category(
result.text,
edit.originalCategory,
edit.currentCategory,
edit.currentKey,
edit.currentHidden );
if ( !result.error ) {
changes++;
if ( !edit.originalCategory || !edit.originalCategory.length ) {
added.push( edit.currentCategory );
} else {
changed.push( {
from: edit.originalCategory,
to: edit.currentCategory
} );
}
} else if ( error === null ) {
error = result.error;
}
} else if (
edit.state === CategoryEditor.DELETED && edit.originalCategory && edit.originalCategory.length ) {
result = change_category(
result.text,
edit.originalCategory,
null, null, false );
if ( !result.error ) {
changes++;
deleted.push( edit.originalCategory );
} else if ( error === null ) {
error = result.error;
}
}
}
if ( error !== null ) { // Do not commit if there were errors
action = commitForm.wpSave;
if ( action ) action.name = action.value = 'wpDiff';
}
// Fill in the form and submit it
commitForm.wpMinoredit.checked = minorEdits;
commitForm.wpWatchthis.checked = !conf.wgArticleId && watchCreate || watchEdit || pageWatched;
if ( conf.wgArticleId || !!singleEditor ) {
// Prepare change-tag save
if ( action && action.value === 'wpSave' ) {
if ( HC.changeTag ) {
commitForm.wpChangeTags.value = HC.changeTag;
HC.messages.using = '';
HC.messages.prefix = '';
}
} else {
commitForm.wpAutoSummary.value = HC.changeTag;
}
if ( changes === 1 ) {
if ( result.summary && result.summary.length ) commitForm.wpSummary.value = HC.messages.prefix + result.summary.join( HC.messages.separator ) + HC.messages.using;
commitForm.wpMinoredit.checked = HC.single_minor || minorEdits;
} else if ( changes ) {
var summary = [];
var shortSummary = [];
// Deleted
for ( i = 0; i < deleted.length; i++ ) summary.push( '−' + substitute( HC.messages.short_catchange, [ null, deleted[ i ] ] ) );
if ( deleted.length === 1 ) shortSummary.push( '−' + substitute( HC.messages.short_catchange, [ null, deleted[ 0 ] ] ) ); else if ( deleted.length ) shortSummary.push( '− ' + multiChangeMsg( deleted.length ) );
// Added
for ( i = 0; i < added.length; i++ ) summary.push( '+' + substitute( HC.messages.short_catchange, [ null, added[ i ] ] ) );
if ( added.length === 1 ) shortSummary.push( '+' + substitute( HC.messages.short_catchange, [ null, added[ 0 ] ] ) ); else if ( added.length ) shortSummary.push( '+ ' + multiChangeMsg( added.length ) );
// Changed
var arrow = is_rtl ? '\u2190' : '\u2192'; // left and right arrows. Don't use ← and → in the code.
for ( i = 0; i < changed.length; i++ ) {
if ( changed[ i ].from !== changed[ i ].to ) {
summary.push(
'±' + substitute( HC.messages.short_catchange, [ null, changed[ i ].from ] ) + arrow +
substitute( HC.messages.short_catchange, [ null, changed[ i ].to ] )
);
} else {
summary.push( '±' + substitute( HC.messages.short_catchange, [ null, changed[ i ].from ] ) );
}
}
if ( changed.length === 1 ) {
if ( changed[ 0 ].from !== changed[ 0 ].to ) {
shortSummary.push(
'±' + substitute( HC.messages.short_catchange, [ null, changed[ 0 ].from ] ) + arrow +
substitute( HC.messages.short_catchange, [ null, changed[ 0 ].to ] )
);
} else {
shortSummary.push( '±' + substitute( HC.messages.short_catchange, [ null, changed[ 0 ].from ] ) );
}
} else if ( changed.length ) {
shortSummary.push( '± ' + multiChangeMsg( changed.length ) );
}
if ( summary.length ) {
summary = summary.join( HC.messages.separator );
if ( summary.length > 200 - HC.messages.prefix.length - HC.messages.using.length ) summary = shortSummary.join( HC.messages.separator );
commitForm.wpSummary.value = HC.messages.prefix + summary + HC.messages.using;
}
}
}
commitForm.wpTextbox1.value = result.text;
commitForm.wpStarttime.value = serverTime || currentTimestamp();
commitForm.wpEdittime.value = pageTime || commitForm.wpStarttime.value;
if ( selfEditConflict ) commitForm.oldid.value = String( pageTextRevId || conf.wgCurRevisionId );
// Submit the form in a way that triggers onsubmit events: commitForm.submit() doesn't.
commitForm.hcCommit.click();
}
function resolveOne( page, toResolve ) {
var cats = page.categories,
lks = page.links,
is_dab = false,
is_redir = typeof page.redirect === 'string', // Hard redirect?
is_hidden = page.categoryinfo && typeof page.categoryinfo.hidden === 'string',
is_missing = typeof page.missing === 'string',
i;
for ( i = 0; i < toResolve.length; i++ ) {
if ( i && toResolve[ i ].dabInputCleaned !== page.title.substring( page.title.indexOf( ':' ) + 1 ) ) continue;
// Note: the server returns in page an NFC normalized Unicode title. If our input was not NFC normalized, we may not find
// any entry here. If we have only one editor to resolve (the most common case, I presume), we may simply skip the check.
toResolve[ i ].currentHidden = is_hidden;
toResolve[ i ].inputExists = !is_missing;
toResolve[ i ].icon.src = ( is_missing ? HC.existsNo : HC.existsYes );
}
if ( is_missing ) return;
if ( !is_redir && cats && ( HC.disambig_category || HC.redir_category ) ) {
for ( var c = 0; c < cats.length; c++ ) {
var cat = cats[ c ].title;
// Strip namespace prefix
if ( cat ) {
cat = cat.substring( cat.indexOf( ':' ) + 1 ).replace( /_/g, ' ' );
if ( cat === HC.disambig_category ) {
is_dab = true;
break;
} else if ( cat === HC.redir_category ) {
is_redir = true;
break;
}
}
}
}
if ( !is_redir && !is_dab ) return;
if ( !lks || !lks.length ) return;
var titles = [];
for ( i = 0; i < lks.length; i++ ) {
if (
// Category namespace -- always true since we ask only for the category links
lks[ i ].ns === 14 &&
// Name not empty
lks[ i ].title && lks[ i ].title.length
) {
// Internal link to existing thingy. Extract the page name and remove the namespace.
var match = lks[ i ].title;
match = match.substring( match.indexOf( ':' ) + 1 );
// Exclude blacklisted categories.
if ( !HC.blacklist || !HC.blacklist.test( match ) ) titles.push( match );
}
}
if ( !titles.length ) return;
for ( i = 0; i < toResolve.length; i++ ) {
if ( i && toResolve[ i ].dabInputCleaned !== page.title.substring( page.title.indexOf( ':' ) + 1 ) ) continue;
toResolve[ i ].inputExists = true; // Might actually be wrong if it's a redirect pointing to a non-existing category
toResolve[ i ].icon.src = HC.existsYes;
if ( titles.length > 1 ) {
toResolve[ i ].dab = titles;
} else {
toResolve[ i ].text.value =
titles[ 0 ] + ( toResolve[ i ].currentKey !== null ? '|' + toResolve[ i ].currentKey : '' );
}
}
}
function resolveRedirects( toResolve, params ) {
if ( !params || !params.query || !params.query.pages ) return;
for ( var p in params.query.pages ) resolveOne( params.query.pages[ p ], toResolve );
}
function resolveMulti( toResolve, callback ) {
var i;
for ( i = 0; i < toResolve.length; i++ ) {
toResolve[ i ].dab = null;
toResolve[ i ].dabInput = toResolve[ i ].lastInput;
}
if ( noSuggestions ) {
callback( toResolve );
return;
}
// Use %7C instead of |, otherwise Konqueror insists on re-encoding the arguments, resulting in doubly encoded
// category names. (That is a bug in Konqueror. Other browsers don't have this problem.)
var args = 'action=query&prop=info%7Clinks%7Ccategories%7Ccategoryinfo&plnamespace=14' +
'&pllimit=' + ( toResolve.length * 10 ) +
'&cllimit=' + ( toResolve.length * 10 ) +
'&format=json&titles=';
for ( i = 0; i < toResolve.length; i++ ) {
var v = toResolve[ i ].dabInput;
v = replaceShortcuts( v, HC.shortcuts );
toResolve[ i ].dabInputCleaned = v;
args += encodeURIComponent( 'Category:' + v );
if ( i + 1 < toResolve.length ) args += '%7C';
}
$.getJSON( wgServer + conf.wgScriptPath + '/api.php?' + args,
function ( json ) {
resolveRedirects( toResolve, json );
callback( toResolve );
} ).fail( function ( req ) {
if ( !req ) noSuggestions = true;
callback( toResolve );
} );
}
function makeActive( which ) {
if ( which.is_active ) return;
for ( var i = 0; i < editors.length; i++ )
if ( editors[ i ] !== which ) editors[ i ].inactivate();
which.is_active = true;
if ( which.dab ) {
// eslint-disable-next-line no-use-before-define
showDab( which );
} else {
// Check for programmatic value changes.
var expectedInput = which.lastRealInput || which.lastInput || '';
var actualValue = which.text.value || '';
if ( !expectedInput.length && actualValue.length || expectedInput.length && actualValue.indexOf( expectedInput ) ) {
// Somehow the field's value appears to have changed, and which.lastSelection therefore is no longer valid. Try to set the
// cursor at the end of the category, and do not display the old suggestion list.
which.showsList = false;
var v = actualValue.split( '|' );
which.lastRealInput = which.lastInput = v[ 0 ];
if ( v.length > 1 ) which.currentKey = v[ 1 ];
if ( which.lastSelection ) {
which.lastSelection = {
start: v[ 0 ].length,
end: v[ 0 ].length
};
}
}
if ( which.showsList ) which.displayList();
if ( which.lastSelection ) {
if ( is_webkit ) {
// WebKit (Safari, Chrome) has problems selecting inside focus()
// See http://code.google.com/p/chromium/issues/detail?id=32865#c6
window.setTimeout(
function () {
which.setSelection( which.lastSelection.start, which.lastSelection.end );
},
1 );
} else {
which.setSelection( which.lastSelection.start, which.lastSelection.end );
}
}
}
}
function showDab( which ) {
if ( !which.is_active ) {
makeActive( which );
} else {
which.showSuggestions( which.dab, false, null, null ); // do autocompletion, no key, no engine selector
which.dab = null;
}
}
function multiSubmit() {
var toResolve = [];
for ( var i = 0; i < editors.length; i++ )
if ( editors[ i ].state === CategoryEditor.CHANGE_PENDING || editors[ i ].state === CategoryEditor.OPEN ) toResolve.push( editors[ i ] );
if ( !toResolve.length ) {
initiateEdit( function ( failure ) {
performChanges( failure );
}, function ( msg ) {
alert( msg );
} );
return;
}
resolveMulti( toResolve, function ( resolved ) {
var firstDab = null;
var dontChange = false;
for ( var i = 0; i < resolved.length; i++ ) {
if ( resolved[ i ].lastInput !== resolved[ i ].dabInput ) {
// We didn't disable all the open editors, but we did asynchronous calls. It is
// theoretically possible that the user changed something...
dontChange = true;
} else {
if ( resolved[ i ].dab ) {
if ( !firstDab ) firstDab = resolved[ i ];
} else {
if ( resolved[ i ].acceptCheck( true ) ) resolved[ i ].commit();
}
}
}
if ( firstDab ) {
showDab( firstDab );
} else if ( !dontChange ) {
initiateEdit( function ( failure ) {
performChanges( failure );
}, function ( msg ) {
alert( msg );
} );
}
} );
}
function setMultiInput() {
if ( commitButton || onUpload ) return;
commitButton = make( 'input' );
commitButton.type = 'button';
commitButton.value = HC.messages.commit;
commitButton.onclick = multiSubmit;
if ( multiSpan ) multiSpan.parentNode.replaceChild( commitButton, multiSpan ); else catLine.appendChild( commitButton );
}
function checkMultiInput() {
if ( !commitButton ) return;
var hasChanges = false;
for ( var i = 0; i < editors.length; i++ ) {
if ( editors[ i ].state !== CategoryEditor.UNCHANGED ) {
hasChanges = true;
break;
}
}
commitButton.disabled = !hasChanges;
}
var suggestionEngines = {
opensearch: {
uri: '/api.php?format=json&action=opensearch&namespace=14&limit=30&search=Category:$1', // $1 = search term
// Function to convert result of uri into an array of category names
handler: function ( queryResult, queryKey ) {
if ( queryResult && queryResult.length >= 2 ) {
var key = queryResult[ 0 ].substring( queryResult[ 0 ].indexOf( ':' ) + 1 );
var titles = queryResult[ 1 ];
var exists = false;
if ( !cat_prefix ) cat_prefix = new RegExp( '^(' + HC.category_regexp + '):' );
for ( var i = 0; i < titles.length; i++ ) {
cat_prefix.lastIndex = 0;
var m = cat_prefix.exec( titles[ i ] );
if ( m && m.length > 1 ) {
titles[ i ] = titles[ i ].substring( titles[ i ].indexOf( ':' ) + 1 ); // rm namespace
if ( key === titles[ i ] ) exists = true;
} else {
titles.splice( i, 1 ); // Nope, it's not a category after all.
i--;
}
}
titles.exists = exists;
if ( queryKey !== key ) titles.normalized = key;
// Remember the NFC normalized key we got back from the server
return titles;
}
return null;
}
},
internalsearch: {
uri: '/api.php?format=json&action=query&list=allpages&apnamespace=14&aplimit=30&apfrom=$1&apprefix=$1',
handler: function ( queryResult ) {
if ( queryResult && queryResult.query && queryResult.query.allpages ) {
var titles = queryResult.query.allpages;
for ( var i = 0; i < titles.length; i++ ) titles[ i ] = titles[ i ].title.substring( titles[ i ].title.indexOf( ':' ) + 1 ); // rm namespace
return titles;
}
return null;
}
},
exists: {
uri: '/api.php?format=json&action=query&prop=info&titles=Category:$1',
handler: function ( queryResult, queryKey ) {
if ( queryResult && queryResult.query && queryResult.query.pages && !queryResult.query.pages[ -1 ] ) {
// Should have exactly 1
for ( var p in queryResult.query.pages ) {
var title = queryResult.query.pages[ p ].title;
title = title.substring( title.indexOf( ':' ) + 1 );
var titles = [ title ];
titles.exists = true;
if ( queryKey !== title ) titles.normalized = title;
// NFC
return titles;
}
}
return null;
}
},
subcategories: {
uri: '/api.php?format=json&action=query&list=categorymembers&cmtype=subcat&cmlimit=max&cmtitle=Category:$1',
handler: function ( queryResult ) {
if ( queryResult && queryResult.query && queryResult.query.categorymembers ) {
var titles = queryResult.query.categorymembers;
for ( var i = 0; i < titles.length; i++ ) titles[ i ] = titles[ i ].title.substring( titles[ i ].title.indexOf( ':' ) + 1 ); // rm namespace
return titles;
}
return null;
}
},
parentcategories: {
uri: '/api.php?format=json&action=query&prop=categories&titles=Category:$1&cllimit=max',
handler: function ( queryResult ) {
if ( queryResult && queryResult.query && queryResult.query.pages ) {
for ( var p in queryResult.query.pages ) {
if ( queryResult.query.pages[ p ].categories ) {
var titles = queryResult.query.pages[ p ].categories;
for ( var i = 0; i < titles.length; i++ ) titles[ i ] = titles[ i ].title.substring( titles[ i ].title.indexOf( ':' ) + 1 ); // rm namespace
return titles;
}
}
}
return null;
}
}
};
var suggestionConfigs = {
searchindex: {
name: 'Search index',
engines: [ 'opensearch' ],
cache: {},
show: true,
temp: false,
noCompletion: false
},
pagelist: {
name: 'Page list',
engines: [ 'internalsearch', 'exists' ],
cache: {},
show: true,
temp: false,
noCompletion: false
},
combined: {
name: 'Combined search',
engines: [ 'opensearch', 'internalsearch' ],
cache: {},
show: true,
temp: false,
noCompletion: false
},
subcat: {
name: 'Subcategories',
engines: [ 'subcategories' ],
cache: {},
show: true,
temp: true,
noCompletion: true
},
parentcat: {
name: 'Parent categories',
engines: [ 'parentcategories' ],
cache: {},
show: true,
temp: true,
noCompletion: true
}
};
CategoryEditor.UNCHANGED = 0;
CategoryEditor.OPEN = 1; // Open, but no input yet
CategoryEditor.CHANGE_PENDING = 2; // Open, some input made
CategoryEditor.CHANGED = 3;
CategoryEditor.DELETED = 4;
// Support: IE6
// IE6 sometimes forgets to redraw the list when editors are opened or closed.
// Adding/removing a dummy element helps, at least when opening editors.
var dummyElement = make( '\xa0', true );
function forceRedraw() {
if ( dummyElement.parentNode ) document.body.removeChild( dummyElement ); else document.body.appendChild( dummyElement );
}
// Event keyCodes that we handle in the text input field/suggestion list.
var BS = 8,
TAB = 9,
RET = 13,
ESC = 27,
SPACE = 32,
PGUP = 33,
PGDOWN = 34,
UP = 38,
DOWN = 40,
DEL = 46,
IME = 229;
CategoryEditor.prototype = {
initialize: function ( line, span, after, key, is_hidden ) {
// If a span is given, 'after' is the category title, otherwise it may be an element after which to
// insert the new span. 'key' is likewise overloaded; if a span is given, it is the category key (if
// known), otherwise it is a boolean indicating whether a bar shall be prepended.
if ( !span ) {
this.isAddCategory = true;
// Create add span and append to catLinks
this.originalCategory = '';
this.originalKey = null;
this.originalExists = false;
if ( !newDOM ) {
span = make( 'span' );
span.className = 'noprint';
if ( key ) {
span.appendChild( make( ' | ', true ) );
if ( after ) {
after.parentNode.insertBefore( span, after.nextSibling );
after = after.nextSibling;
} else if (line) {
line.appendChild( span );
}
} else if ( line && line.firstChild ) {
span.appendChild( make( ' ', true ) );
line.appendChild( span );
}
}
this.linkSpan = make( 'span' );
this.linkSpan.className = 'noprint nopopups hotcatlink';
var lk = make( 'a' );
lk.href = '#catlinks';
lk.onclick = this.open.bind( this );
lk.appendChild( make( HC.links.add, true ) );
lk.title = HC.tooltips.add;
this.linkSpan.appendChild( lk );
span = make( newDOM ? 'li' : 'span' );
span.className = 'noprint';
if ( is_rtl ) span.dir = 'rtl';
span.appendChild( this.linkSpan );
if ( after ) {
after.parentNode.insertBefore( span, after.nextSibling );
} else if ( line ) {
line.appendChild( span );
}
this.normalLinks = null;
this.undelLink = null;
this.catLink = null;
} else {
if ( is_rtl ) span.dir = 'rtl';
this.isAddCategory = false;
this.catLink = span.firstChild;
this.originalCategory = after;
this.originalKey = ( key && key.length > 1 ) ? key.substr( 1 ) : null; // > 1 because it includes the leading bar
this.originalExists = !hasClass( this.catLink, 'new' );
// Create change and del links
this.makeLinkSpan();
if ( !this.originalExists && this.upDownLinks ) this.upDownLinks.style.display = 'none';
span.appendChild( this.linkSpan );
}
this.originalHidden = is_hidden;
this.line = line;
this.engine = HC.suggestions;
this.span = span;
this.currentCategory = this.originalCategory;
this.currentExists = this.originalExists;
this.currentHidden = this.originalHidden;
this.currentKey = this.originalKey;
this.state = CategoryEditor.UNCHANGED;
this.lastSavedState = CategoryEditor.UNCHANGED;
this.lastSavedCategory = this.originalCategory;
this.lastSavedKey = this.originalKey;
this.lastSavedExists = this.originalExists;
this.lastSavedHidden = this.originalHidden;
if ( this.catLink && this.currentKey ) this.catLink.title = this.currentKey;
editors[ editors.length ] = this;
},
makeLinkSpan: function () {
this.normalLinks = make( 'span' );
var lk = null;
if ( this.originalCategory && this.originalCategory.length ) {
lk = make( 'a' );
lk.href = '#catlinks';
lk.onclick = this.remove.bind( this );
lk.appendChild( make( HC.links.remove, true ) );
lk.title = HC.tooltips.remove;
this.normalLinks.appendChild( make( ' ', true ) );
this.normalLinks.appendChild( lk );
}
if ( !HC.template_categories[ this.originalCategory ] ) {
lk = make( 'a' );
lk.href = '#catlinks';
lk.onclick = this.open.bind( this );
lk.appendChild( make( HC.links.change, true ) );
lk.title = HC.tooltips.change;
this.normalLinks.appendChild( make( ' ', true ) );
this.normalLinks.appendChild( lk );
if ( !noSuggestions && HC.use_up_down ) {
this.upDownLinks = make( 'span' );
lk = make( 'a' );
lk.href = '#catlinks';
lk.onclick = this.down.bind( this );
lk.appendChild( make( HC.links.down, true ) );
lk.title = HC.tooltips.down;
this.upDownLinks.appendChild( make( ' ', true ) );
this.upDownLinks.appendChild( lk );
lk = make( 'a' );
lk.href = '#catlinks';
lk.onclick = this.up.bind( this );
lk.appendChild( make( HC.links.up, true ) );
lk.title = HC.tooltips.up;
this.upDownLinks.appendChild( make( ' ', true ) );
this.upDownLinks.appendChild( lk );
this.normalLinks.appendChild( this.upDownLinks );
}
}
this.linkSpan = make( 'span' );
this.linkSpan.className = 'noprint nopopups hotcatlink';
this.linkSpan.appendChild( this.normalLinks );
this.undelLink = make( 'span' );
this.undelLink.className = 'nopopups hotcatlink';
this.undelLink.style.display = 'none';
lk = make( 'a' );
lk.href = '#catlinks';
lk.onclick = this.restore.bind( this );
lk.appendChild( make( HC.links.restore, true ) );
lk.title = HC.tooltips.restore;
this.undelLink.appendChild( make( ' ', true ) );
this.undelLink.appendChild( lk );
this.linkSpan.appendChild( this.undelLink );
},
invokeSuggestions: function ( dont_autocomplete ) {
if ( this.engine && suggestionConfigs[ this.engine ] && suggestionConfigs[ this.engine ].temp && !dont_autocomplete ) this.engine = HC.suggestions; // Reset to a search upon input
this.state = CategoryEditor.CHANGE_PENDING;
var self = this;
window.setTimeout( function () {
self.textchange( dont_autocomplete );
}, HC.suggest_delay );
},
makeForm: function () {
var form = make( 'form' );
form.method = 'POST';
form.onsubmit = this.accept.bind( this );
this.form = form;
var self = this;
var text = make( 'input' );
text.type = 'text';
text.size = HC.editbox_width;
if ( !noSuggestions ) {
// Be careful here to handle IME input. This is browser/OS/IME dependent, but basically there are two mechanisms:
// - Modern (DOM Level 3) browsers use compositionstart/compositionend events to signal composition; if the
// composition is not canceled, there'll be a textInput event following. During a composition key events are
// either all suppressed (FF/Gecko), or otherwise have keyDown === IME for all keys (Webkit).
// - Webkit sends a textInput followed by keyDown === IME and a keyUp with the key that ended composition.
// - Gecko doesn't send textInput but just a keyUp with the key that ended composition, without sending keyDown
// first. Gecko doesn't send any keydown while IME is active.
// - Older browsers signal composition by keyDown === IME for the first and subsequent keys for a composition. The
// first keyDown !== IME is certainly after the end of the composition. Typically, composition end can also be
// detected by a keyDown IME with a keyUp of space, tab, escape, or return.
text.onkeyup = function ( evt ) {
var key = evt.keyCode || 0;
if ( self.ime && self.lastKey === IME && !self.usesComposition && ( key === TAB || key === RET || key === ESC || key === SPACE ) ) self.ime = false;
if ( self.ime ) return true;
if ( key === UP || key === DOWN || key === PGUP || key === PGDOWN ) {
// In case a browser doesn't generate keypress events for arrow keys...
if ( self.keyCount === 0 ) return self.processKey( evt );
} else {
if ( key === ESC && self.lastKey !== IME ) {
if ( !self.resetKeySelection() ) {
// No undo of key selection: treat ESC as "cancel".
self.cancel();
return;
}
}
// Also do this for ESC as a workaround for Firefox bug 524360
// https://bugzilla.mozilla.org/show_bug.cgi?id=524360
self.invokeSuggestions( key === BS || key === DEL || key === ESC );
}
return true;
};
text.onkeydown = function ( evt ) {
var key = evt.keyCode || 0;
self.lastKey = key;
self.keyCount = 0;
// DOM Level < 3 IME input
if ( !self.ime && key === IME && !self.usesComposition ) {
// self.usesComposition catches browsers that may emit spurious keydown IME after a composition has ended
self.ime = true;
} else if ( self.ime && key !== IME && !( key >= 16 && key <= 20 || key >= 91 && key <= 93 || key === 144 ) ) {
// Ignore control keys: ctrl, shift, alt, alt gr, caps lock, windows/apple cmd keys, num lock. Only the windows keys
// terminate IME (apple cmd doesn't), but they also cause a blur, so it's OK to ignore them here.
// Note: Safari 4 (530.17) propagates ESC out of an IME composition (observed at least on Win XP).
self.ime = false;
}
if ( self.ime ) return true;
// Handle return explicitly, to override the default form submission to be able to check for ctrl
if ( key === RET ) return self.accept( evt );
// Inhibit default behavior of ESC (revert to last real input in FF: we do that ourselves)
return ( key === ESC ) ? evtKill( evt ) : true;
};
// And handle continued pressing of arrow keys
text.onkeypress = function ( evt ) {
self.keyCount++;
return self.processKey( evt );
};
$( text ).on( 'focus', function () {
makeActive( self );
} );
// On IE, blur events are asynchronous, and may thus arrive after the element has lost the focus. Since IE
// can get the selection only while the element is active (has the focus), we may not always get the selection.
// Therefore, use an IE-specific synchronous event on IE...
// Don't test for text.selectionStart being defined;
$( text ).on(
( text.onbeforedeactivate !== undefined && text.createTextRange ) ? 'beforedeactivate' : 'blur',
this.saveView.bind( this ) );
// DOM Level 3 IME handling
try {
// Setting lastKey = IME provides a fake keyDown for Gecko's single keyUp after a cmposition. If we didn't do this,
// cancelling a composition via ESC would also cancel and close the whole category input editor.
$( text ).on( 'compositionstart', function () {
self.lastKey = IME;
self.usesComposition = true;
self.ime = true;
} );
$( text ).on( 'compositionend', function () {
self.lastKey = IME;
self.usesComposition = true;
self.ime = false;
} );
$( text ).on( 'textInput', function () {
self.ime = false;
self.invokeSuggestions( false );
} );
} catch ( any ) {
// Just in case some browsers might produce exceptions with these DOM Level 3 events
}
$( text ).on( 'blur', function () {
self.usesComposition = false;
self.ime = false;
} );
}
this.text = text;
this.icon = make( 'img' );
var list = null;
if ( !noSuggestions ) {
list = make( 'select' );
list.onclick = function () {
if ( self.highlightSuggestion( 0 ) ) self.textchange( false, true );
};
list.ondblclick = function ( e ) {
if ( self.highlightSuggestion( 0 ) ) self.accept( e );
};
list.onchange = function () {
self.highlightSuggestion( 0 );
self.text.focus();
};
list.onkeyup = function ( evt ) {
if ( evt.keyCode === ESC ) {
self.resetKeySelection();
self.text.focus();
window.setTimeout( function () {
self.textchange( true );
}, HC.suggest_delay );
} else if ( evt.keyCode === RET ) {
self.accept( evt );
}
};
if ( !HC.fixed_search ) {
var engineSelector = make( 'select' );
for ( var key in suggestionConfigs ) {
if ( suggestionConfigs[ key ].show ) {
var opt = make( 'option' );
opt.value = key;
if ( key === this.engine ) opt.selected = true;
opt.appendChild( make( suggestionConfigs[ key ].name, true ) );
engineSelector.appendChild( opt );
}
}
engineSelector.onchange = function () {
self.engine = self.engineSelector.options[ self.engineSelector.selectedIndex ].value;
self.text.focus();
self.textchange( true, true ); // Don't autocomplete, force re-display of list
};
this.engineSelector = engineSelector;
}
}
this.list = list;
function button_label( id, defaultText ) {
var label = null;
if (
onUpload &&
window.UFUI !== undefined &&
window.UIElements !== undefined &&
UFUI.getLabel instanceof Function
) {
try {
label = UFUI.getLabel( id, true );
// Extract the plain text. IE doesn't know that Node.TEXT_NODE === 3
while ( label && label.nodeType !== 3 ) label = label.firstChild;
} catch ( ex ) {
label = null;
}
}
if ( !label || !label.data ) return defaultText;
return label.data;
}
// Do not use type 'submit'; we cannot detect modifier keys if we do
var OK = make( 'input' );
OK.type = 'button';
OK.value = button_label( 'wpOkUploadLbl', HC.messages.ok );
OK.onclick = this.accept.bind( this );
this.ok = OK;
var cancel = make( 'input' );
cancel.type = 'button';
cancel.value = button_label( 'wpCancelUploadLbl', HC.messages.cancel );
cancel.onclick = this.cancel.bind( this );
this.cancelButton = cancel;
var span = make( 'span' );
span.className = 'hotcatinput';
span.style.position = 'relative';
span.appendChild( text );
// Support: IE8, IE9
// Put some text into this span (a0 is nbsp) and make sure it always stays on the same
// line as the input field, otherwise, IE8/9 miscalculates the height of the span and
// then the engine selector may overlap the input field.
span.appendChild( make( '\xa0', true ) );
span.style.whiteSpace = 'nowrap';
if ( list ) span.appendChild( list );
if ( this.engineSelector ) span.appendChild( this.engineSelector );
if ( !noSuggestions ) span.appendChild( this.icon );
span.appendChild( OK );
span.appendChild( cancel );
form.appendChild( span );
form.style.display = 'none';
this.span.appendChild( form );
},
display: function ( evt ) {
if ( this.isAddCategory && !onUpload && this.line ) {
// eslint-disable-next-line no-new
new CategoryEditor( this.line, null, this.span, true ); // Create a new one
}
if ( !commitButton && !onUpload ) {
for ( var i = 0; i < editors.length; i++ ) {
if ( editors[ i ].state !== CategoryEditor.UNCHANGED ) {
setMultiInput();
break;
}
}
}
if ( !this.form ) this.makeForm();
if ( this.list ) this.list.style.display = 'none';
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
this.currentCategory = this.lastSavedCategory;
this.currentExists = this.lastSavedExists;
this.currentHidden = this.lastSavedHidden;
this.currentKey = this.lastSavedKey;
this.icon.src = ( this.currentExists ? HC.existsYes : HC.existsNo );
this.text.value = this.currentCategory + ( this.currentKey !== null ? '|' + this.currentKey : '' );
this.originalState = this.state;
this.lastInput = this.currentCategory;
this.inputExists = this.currentExists;
this.state = this.state === CategoryEditor.UNCHANGED ? CategoryEditor.OPEN : CategoryEditor.CHANGE_PENDING;
this.lastSelection = {
start: this.currentCategory.length,
end: this.currentCategory.length
};
this.showsList = false;
// Display the form
if ( this.catLink ) this.catLink.style.display = 'none';
this.linkSpan.style.display = 'none';
this.form.style.display = 'inline';
this.ok.disabled = false;
// Kill the event before focussing, otherwise IE will kill the onfocus event!
var result = evtKill( evt );
this.text.focus();
this.text.readOnly = false;
checkMultiInput();
return result;
},
show: function ( evt, engine, readOnly ) {
var result = this.display( evt );
var v = this.lastSavedCategory;
if ( !v.length ) return result;
this.text.readOnly = !!readOnly;
this.engine = engine;
this.textchange( false, true ); // do autocompletion, force display of suggestions
forceRedraw();
return result;
},
open: function ( evt ) {
return this.show( evt, ( this.engine && suggestionConfigs[ this.engine ].temp ) ? HC.suggestions : this.engine );
},
down: function ( evt ) {
return this.show( evt, 'subcat', true );
},
up: function ( evt ) {
return this.show( evt, 'parentcat' );
},
cancel: function () {
if ( this.isAddCategory && !onUpload ) {
this.removeEditor(); // We added a new adder when opening
return;
}
// Close, re-display link
this.inactivate();
this.form.style.display = 'none';
if ( this.catLink ) this.catLink.style.display = '';
this.linkSpan.style.display = '';
this.state = this.originalState;
this.currentCategory = this.lastSavedCategory;
this.currentKey = this.lastSavedKey;
this.currentExists = this.lastSavedExists;
this.currentHidden = this.lastSavedHidden;
if ( this.catLink )
if ( this.currentKey && this.currentKey.length ) { this.catLink.title = this.currentKey; } else { this.catLink.title = ''; }
if ( this.state === CategoryEditor.UNCHANGED ) {
if ( this.catLink ) this.catLink.style.backgroundColor = 'transparent';
} else {
if ( !onUpload ) {
try {
this.catLink.style.backgroundColor = HC.bg_changed;
} catch ( ex ) {}
}
}
checkMultiInput();
forceRedraw();
},
removeEditor: function () {
if ( !newDOM ) {
var next = this.span.nextSibling;
if ( next ) next.parentNode.removeChild( next );
}
if (this.span && this.span.parentNode) {
this.span.parentNode.removeChild( this.span );
}
for ( var i = 0; i < editors.length; i++ ) {
if ( editors[ i ] === this ) {
editors.splice( i, 1 );
break;
}
}
checkMultiInput();
},
rollback: function ( evt ) {
this.undoLink.parentNode.removeChild( this.undoLink );
this.undoLink = null;
this.currentCategory = this.originalCategory;
this.currentKey = this.originalKey;
this.currentExists = this.originalExists;
this.currentHidden = this.originalHidden;
this.lastSavedCategory = this.originalCategory;
this.lastSavedKey = this.originalKey;
this.lastSavedExists = this.originalExists;
this.lastSavedHidden = this.originalHidden;
this.state = CategoryEditor.UNCHANGED;
if ( !this.currentCategory || !this.currentCategory.length ) {
// It was a newly added category. Remove the whole editor.
this.removeEditor();
} else {
// Redisplay the link...
this.catLink.removeChild( this.catLink.firstChild );
this.catLink.appendChild( make( this.currentCategory, true ) );
this.catLink.href = wikiPagePath( HC.category_canonical + ':' + this.currentCategory );
this.catLink.title = this.currentKey || '';
this.catLink.className = this.currentExists ? '' : 'new';
this.catLink.style.backgroundColor = 'transparent';
if ( this.upDownLinks ) this.upDownLinks.style.display = this.currentExists ? '' : 'none';
checkMultiInput();
}
return evtKill( evt );
},
inactivate: function () {
if ( this.list ) this.list.style.display = 'none';
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
this.is_active = false;
},
acceptCheck: function ( dontCheck ) {
this.sanitizeInput();
var value = this.text.value.split( '|' );
var key = null;
if ( value.length > 1 ) key = value[ 1 ];
var v = value[ 0 ].replace( /_/g, ' ' ).replace( /^\s+|\s+$/g, '' );
if ( HC.capitalizePageNames ) v = capitalize( v );
this.lastInput = v;
v = replaceShortcuts( v, HC.shortcuts );
if ( !v.length ) {
this.cancel();
return false;
}
if ( !dontCheck && (
conf.wgNamespaceNumber === 14 && v === conf.wgTitle || HC.blacklist && HC.blacklist.test( v ) ) ) {
this.cancel();
return false;
}
this.currentCategory = v;
this.currentKey = key;
this.currentExists = this.inputExists;
return true;
},
accept: function ( evt ) {
// eslint-disable-next-line no-bitwise
this.noCommit = ( evtKeys( evt ) & 1 ) !== 0;
var result = evtKill( evt );
if ( this.acceptCheck() ) {
var toResolve = [ this ];
var original = this.currentCategory;
resolveMulti( toResolve, function ( resolved ) {
if ( resolved[ 0 ].dab ) {
showDab( resolved[ 0 ] );
} else {
if ( resolved[ 0 ].acceptCheck( true ) ) {
resolved[ 0 ].commit(
( resolved[ 0 ].currentCategory !== original ) ?
HC.messages.cat_resolved.replace( /\$1/g, original ) :
null );
}
}
} );
}
return result;
},
close: function () {
if ( !this.catLink ) {
// Create a catLink
this.catLink = make( 'a' );
this.catLink.appendChild( make( 'foo', true ) );
this.catLink.style.display = 'none';
this.span.insertBefore( this.catLink, this.span.firstChild.nextSibling );
}
this.catLink.removeChild( this.catLink.firstChild );
this.catLink.appendChild( make( this.currentCategory, true ) );
this.catLink.href = wikiPagePath( HC.category_canonical + ':' + this.currentCategory );
this.catLink.className = this.currentExists ? '' : 'new';
this.lastSavedCategory = this.currentCategory;
this.lastSavedKey = this.currentKey;
this.lastSavedExists = this.currentExists;
this.lastSavedHidden = this.currentHidden;
// Close form and redisplay category
this.inactivate();
this.form.style.display = 'none';
this.catLink.title = this.currentKey || '';
this.catLink.style.display = '';
if ( this.isAddCategory ) {
if ( onUpload && this.line ) {
// eslint-disable-next-line no-new
new CategoryEditor( this.line, null, this.span, true ); // Create a new one
}
this.isAddCategory = false;
this.linkSpan.parentNode.removeChild( this.linkSpan );
this.makeLinkSpan();
this.span.appendChild( this.linkSpan );
}
if ( !this.undoLink ) {
// Append an undo link.
var span = make( 'span' );
var lk = make( 'a' );
lk.href = '#catlinks';
lk.onclick = this.rollback.bind( this );
lk.appendChild( make( HC.links.undo, true ) );
lk.title = HC.tooltips.undo;
span.appendChild( make( ' ', true ) );
span.appendChild( lk );
this.normalLinks.appendChild( span );
this.undoLink = span;
if ( !onUpload ) {
try {
this.catLink.style.backgroundColor = HC.bg_changed;
} catch ( ex ) {}
}
}
if ( this.upDownLinks ) this.upDownLinks.style.display = this.lastSavedExists ? '' : 'none';
this.linkSpan.style.display = '';
this.state = CategoryEditor.CHANGED;
checkMultiInput();
forceRedraw();
},
commit: function () {
// Check again to catch problem cases after redirect resolution
if (
(
this.currentCategory === this.originalCategory &&
(
this.currentKey === this.originalKey ||
this.currentKey === null && !this.originalKey.length
)
) ||
conf.wgNamespaceNumber === 14 && this.currentCategory === conf.wgTitle ||
HC.blacklist && HC.blacklist.test( this.currentCategory )
) {
this.cancel();
return;
}
this.close();
if ( !commitButton && !onUpload ) {
var self = this;
initiateEdit( function ( failure ) {
performChanges( failure, self );
}, function ( msg ) {
alert( msg );
} );
}
},
remove: function ( evt ) {
// eslint-disable-next-line no-bitwise
this.doRemove( evtKeys( evt ) & 1 );
return evtKill( evt );
},
doRemove: function ( noCommit ) {
if ( this.isAddCategory ) { // Empty input on adding a new category
this.cancel();
return;
}
if ( !commitButton && !onUpload ) {
for ( var i = 0; i < editors.length; i++ ) {
if ( editors[ i ].state !== CategoryEditor.UNCHANGED ) {
setMultiInput();
break;
}
}
}
if ( commitButton ) {
this.catLink.title = '';
this.catLink.style.cssText += '; text-decoration : line-through !important;';
try {
this.catLink.style.backgroundColor = HC.bg_changed;
} catch ( ex ) {}
this.originalState = this.state;
this.state = CategoryEditor.DELETED;
this.normalLinks.style.display = 'none';
this.undelLink.style.display = '';
checkMultiInput();
} else {
if ( onUpload ) {
// Remove this editor completely
this.removeEditor();
} else {
this.originalState = this.state;
this.state = CategoryEditor.DELETED;
this.noCommit = noCommit || HC.del_needs_diff;
var self = this;
initiateEdit(
function ( failure ) {
performChanges( failure, self );
},
function ( msg ) {
self.state = self.originalState;
alert( msg );
} );
}
}
},
restore: function ( evt ) {
// Can occur only if we do have a commit button and are not on the upload form
this.catLink.title = this.currentKey || '';
this.catLink.style.textDecoration = '';
this.state = this.originalState;
if ( this.state === CategoryEditor.UNCHANGED ) {
this.catLink.style.backgroundColor = 'transparent';
} else {
try {
this.catLink.style.backgroundColor = HC.bg_changed;
} catch ( ex ) {}
}
this.normalLinks.style.display = '';
this.undelLink.style.display = 'none';
checkMultiInput();
return evtKill( evt );
},
// Internal operations
selectEngine: function ( engineName ) {
if ( !this.engineSelector ) return;
for ( var i = 0; i < this.engineSelector.options.length; i++ ) this.engineSelector.options[ i ].selected = this.engineSelector.options[ i ].value === engineName;
},
sanitizeInput: function () {
var v = this.text.value || '';
v = v.replace( /^(\s|_)+/, '' ); // Trim leading blanks and underscores
var re = new RegExp( '^(' + HC.category_regexp + '):' );
if ( re.test( v ) ) v = v.substring( v.indexOf( ':' ) + 1 ).replace( /^(\s|_)+/, '' );
v = v.replace(/\u200E$/, ''); // Trim ending left-to-right mark
if ( HC.capitalizePageNames ) v = capitalize( v );
// Update the input field if needed, manually restoring the cursor afterwards
// (but not the whole selection, so that further typing does not overwrite anything)
if ( this.text.value !== null && this.text.value !== v ) {
var cursorPos = this.text.selectionStart;
this.text.value = v;
this.text.setSelectionRange( cursorPos, cursorPos );
}
},
makeCall: function ( url, callbackObj, engine, queryKey, cleanKey ) {
var cb = callbackObj,
e = engine,
v = queryKey,
z = cleanKey,
thisObj = this;
function done() {
cb.callsMade++;
if ( cb.callsMade === cb.nofCalls ) {
if ( cb.exists ) cb.allTitles.exists = true;
if ( cb.normalized ) cb.allTitles.normalized = cb.normalized;
if ( !cb.dontCache && !suggestionConfigs[ cb.engineName ].cache[ z ] ) suggestionConfigs[ cb.engineName ].cache[ z ] = cb.allTitles;
thisObj.text.readOnly = false;
if ( !cb.cancelled ) thisObj.showSuggestions( cb.allTitles, cb.noCompletion, v, cb.engineName );
if ( cb === thisObj.callbackObj ) thisObj.callbackObj = null;
cb = undefined;
}
}
$.getJSON( url, function ( json ) {
var titles = e.handler( json, z );
if ( titles && titles.length ) {
if ( cb.allTitles === null ) cb.allTitles = titles; else cb.allTitles = cb.allTitles.concat( titles );
if ( titles.exists ) cb.exists = true;
if ( titles.normalized ) cb.normalized = titles.normalized;
}
done();
} ).fail( function ( req ) {
if ( !req ) noSuggestions = true;
cb.dontCache = true;
done();
} );
},
callbackObj: null,
textchange: function ( dont_autocomplete, force ) {
// Hide all other lists
makeActive( this );
// Get input value, omit sort key, if any
this.sanitizeInput();
var v = this.text.value;
// Disregard anything after a pipe.
var pipe = v.indexOf( '|' );
if ( pipe >= 0 ) {
this.currentKey = v.substring( pipe + 1 );
v = v.substring( 0, pipe );
} else {
this.currentKey = null;
}
if ( this.lastInput === v && !force ) return; // No change
if ( this.lastInput !== v ) checkMultiInput();
this.lastInput = v;
this.lastRealInput = v;
// Mark blacklisted inputs.
this.ok.disabled = v.length && HC.blacklist && HC.blacklist.test( v );
if ( noSuggestions ) {
// No Ajax: just make sure the list is hidden
if ( this.list ) this.list.style.display = 'none';
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
if ( this.icon ) this.icon.style.display = 'none';
return;
}
if ( !v.length ) {
this.showSuggestions( [] );
return;
}
var cleanKey = v.replace( /[\u200E\u200F\u202A-\u202E]/g, '' ).replace( wikiTextBlankRE, ' ' );
cleanKey = replaceShortcuts( cleanKey, HC.shortcuts );
cleanKey = cleanKey.replace( /^\s+|\s+$/g, '' );
if ( !cleanKey.length ) {
this.showSuggestions( [] );
return;
}
if ( this.callbackObj ) this.callbackObj.cancelled = true;
var engineName = suggestionConfigs[ this.engine ] ? this.engine : 'combined';
dont_autocomplete = dont_autocomplete || suggestionConfigs[ engineName ].noCompletion;
if ( suggestionConfigs[ engineName ].cache[ cleanKey ] ) {
this.showSuggestions( suggestionConfigs[ engineName ].cache[ cleanKey ], dont_autocomplete, v, engineName );
return;
}
var engines = suggestionConfigs[ engineName ].engines;
this.callbackObj = {
allTitles: null,
callsMade: 0,
nofCalls: engines.length,
noCompletion: dont_autocomplete,
engineName: engineName
};
this.makeCalls( engines, this.callbackObj, v, cleanKey );
},
makeCalls: function ( engines, cb, v, cleanKey ) {
for ( var j = 0; j < engines.length; j++ ) {
var engine = suggestionEngines[ engines[ j ] ];
var url = wgServer + conf.wgScriptPath + engine.uri.replace( /\$1/g, encodeURIComponent( cleanKey ) );
this.makeCall( url, cb, engine, v, cleanKey );
}
},
showSuggestions: function ( titles, dontAutocomplete, queryKey, engineName ) {
this.text.readOnly = false;
this.dab = null;
this.showsList = false;
if ( !this.list ) return;
if ( noSuggestions ) {
if ( this.list ) this.list.style.display = 'none';
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
if ( this.icon ) this.icon.style.display = 'none';
this.inputExists = true; // Default...
return;
}
this.engineName = engineName;
if ( engineName ) {
if ( !this.engineSelector ) this.engineName = null;
} else {
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
}
if ( queryKey ) {
if ( this.lastInput.indexOf( queryKey ) ) return;
if ( this.lastQuery && this.lastInput.indexOf( this.lastQuery ) === 0 && this.lastQuery.length > queryKey.length ) return;
}
this.lastQuery = queryKey;
// Get current input text
var v = this.text.value.split( '|' );
var key = v.length > 1 ? '|' + v[ 1 ] : '';
v = ( HC.capitalizePageNames ? capitalize( v[ 0 ] ) : v[ 0 ] );
var vNormalized = v;
var knownToExist = titles && titles.exists;
var i;
if ( titles ) {
if ( titles.normalized && v.indexOf( queryKey ) === 0 ) {
// We got back a different normalization than what is in the input field
vNormalized = titles.normalized + v.substring( queryKey.length );
}
var vLow = vNormalized.toLowerCase();
// Strip blacklisted categories
if ( HC.blacklist ) {
for ( i = 0; i < titles.length; i++ ) {
if ( HC.blacklist.test( titles[ i ] ) ) {
titles.splice( i, 1 );
i--;
}
}
}
titles.sort(
function ( a, b ) {
if ( a === b ) return 0;
if ( a.indexOf( b ) === 0 ) return 1;
// a begins with b: a > b
if ( b.indexOf( a ) === 0 ) return -1;
// b begins with a: a < b
// Opensearch may return stuff not beginning with the search prefix!
var prefixMatchA = ( a.indexOf( vNormalized ) === 0 ? 1 : 0 );
var prefixMatchB = ( b.indexOf( vNormalized ) === 0 ? 1 : 0 );
if ( prefixMatchA !== prefixMatchB ) return prefixMatchB - prefixMatchA;
// Case-insensitive prefix match!
var aLow = a.toLowerCase(),
bLow = b.toLowerCase();
prefixMatchA = ( aLow.indexOf( vLow ) === 0 ? 1 : 0 );
prefixMatchB = ( bLow.indexOf( vLow ) === 0 ? 1 : 0 );
if ( prefixMatchA !== prefixMatchB ) return prefixMatchB - prefixMatchA;
if ( a < b ) return -1;
if ( b < a ) return 1;
return 0;
} );
// Remove duplicates and self-references
for ( i = 0; i < titles.length; i++ ) {
if (
i + 1 < titles.length && titles[ i ] === titles[ i + 1 ] ||
conf.wgNamespaceNumber === 14 && titles[ i ] === conf.wgTitle
) {
titles.splice( i, 1 );
i--;
}
}
}
if ( !titles || !titles.length ) {
if ( this.list ) this.list.style.display = 'none';
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
if ( engineName && suggestionConfigs[ engineName ] && !suggestionConfigs[ engineName ].temp ) {
if ( this.icon ) this.icon.src = HC.existsNo;
this.inputExists = false;
}
return;
}
var firstTitle = titles[ 0 ];
var completed = this.autoComplete( firstTitle, v, vNormalized, key, dontAutocomplete );
var existing = completed || knownToExist || firstTitle === replaceShortcuts( v, HC.shortcuts );
if ( engineName && suggestionConfigs[ engineName ] && !suggestionConfigs[ engineName ].temp ) {
this.icon.src = ( existing ? HC.existsYes : HC.existsNo );
this.inputExists = existing;
}
if ( completed ) {
this.lastInput = firstTitle;
if ( titles.length === 1 ) {
this.list.style.display = 'none';
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
return;
}
}
// (Re-)fill the list
while ( this.list.firstChild ) this.list.removeChild( this.list.firstChild );
for ( i = 0; i < titles.length; i++ ) {
var opt = make( 'option' );
opt.appendChild( make( titles[ i ], true ) );
opt.selected = completed && ( i === 0 );
this.list.appendChild( opt );
}
this.displayList();
},
displayList: function () {
this.showsList = true;
if ( !this.is_active ) {
this.list.style.display = 'none';
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
return;
}
var nofItems = ( this.list.options.length > HC.listSize ? HC.listSize : this.list.options.length );
if ( nofItems <= 1 ) nofItems = 2;
this.list.size = nofItems;
this.list.style.align = is_rtl ? 'right' : 'left';
this.list.style.zIndex = 5;
this.list.style.position = 'absolute';
// Compute initial list position. First the height.
var anchor = is_rtl ? 'right' : 'left';
var listh = 0;
if ( this.list.style.display === 'none' ) {
// Off-screen display to get the height
this.list.style.top = this.text.offsetTop + 'px';
this.list.style[ anchor ] = '-10000px';
this.list.style.display = '';
listh = this.list.offsetHeight;
this.list.style.display = 'none';
} else {
listh = this.list.offsetHeight;
}
// Approximate calculation of maximum list size
var maxListHeight = listh;
if ( nofItems < HC.listSize ) maxListHeight = ( listh / nofItems ) * HC.listSize;
function viewport( what ) {
if ( is_webkit && !document.evaluate ) {
// Safari < 3.0
return window[ 'inner' + what ];
}
var s = 'client' + what;
if ( window.opera ) return document.body[ s ];
return ( document.documentElement ? document.documentElement[ s ] : 0 ) || document.body[ s ] || 0;
}
function scroll_offset( what ) {
var s = 'scroll' + what;
var result = ( document.documentElement ? document.documentElement[ s ] : 0 ) || document.body[ s ] || 0;
if ( is_rtl && what === 'Left' ) {
// RTL inconsistencies.
// FF: 0 at the far right, then increasingly negative values.
// IE >= 8: 0 at the far right, then increasingly positive values.
// Webkit: scrollWidth - clientWidth at the far right, then down to zero.
// Opera: don't know...
if ( result < 0 ) result = -result;
if ( !is_webkit ) result = scroll_offset( 'Width' ) - viewport( 'Width' ) - result;
// Now all have webkit behavior, i.e. zero if at the leftmost edge.
}
return result;
}
function position( node ) {
// Stripped-down simplified position function. It's good enough for our purposes.
if ( node.getBoundingClientRect ) {
var box = node.getBoundingClientRect();
return {
x: Math.round( box.left + scroll_offset( 'Left' ) ),
y: Math.round( box.top + scroll_offset( 'Top' ) )
};
}
var t = 0,
l = 0;
do {
t += ( node.offsetTop || 0 );
l += ( node.offsetLeft || 0 );
node = node.offsetParent;
} while ( node );
return {
x: l,
y: t
};
}
var textPos = position( this.text ),
nl = 0,
nt = 0,
offset = 0,
// Opera 9.5 somehow has offsetWidth = 0 here?? Use the next best value...
textBoxWidth = this.text.offsetWidth || this.text.clientWidth;
if ( this.engineName ) {
this.engineSelector.style.zIndex = 5;
this.engineSelector.style.position = 'absolute';
this.engineSelector.style.width = textBoxWidth + 'px';
// Figure out the height of this selector: display it off-screen, then hide it again.
if ( this.engineSelector.style.display === 'none' ) {
this.engineSelector.style[ anchor ] = '-10000px';
this.engineSelector.style.top = '0';
this.engineSelector.style.display = '';
offset = this.engineSelector.offsetHeight;
this.engineSelector.style.display = 'none';
} else {
offset = this.engineSelector.offsetHeight;
}
this.engineSelector.style[ anchor ] = nl + 'px';
}
if ( textPos.y < maxListHeight + offset + 1 ) {
// The list might extend beyond the upper border of the page. Let's avoid that by placing it
// below the input text field.
nt = this.text.offsetHeight + offset + 1;
if ( this.engineName ) this.engineSelector.style.top = this.text.offsetHeight + 'px';
} else {
nt = -listh - offset - 1;
if ( this.engineName ) this.engineSelector.style.top = -( offset + 1 ) + 'px';
}
this.list.style.top = nt + 'px';
this.list.style.width = ''; // No fixed width (yet)
this.list.style[ anchor ] = nl + 'px';
if ( this.engineName ) {
this.selectEngine( this.engineName );
this.engineSelector.style.display = '';
}
this.list.style.display = 'block';
// Set the width of the list
if ( this.list.offsetWidth < textBoxWidth ) {
this.list.style.width = textBoxWidth + 'px';
return;
}
// If the list is wider than the textbox: make sure it fits horizontally into the browser window
var scroll = scroll_offset( 'Left' );
var view_w = viewport( 'Width' );
var w = this.list.offsetWidth;
var l_pos = position( this.list );
var left = l_pos.x;
var right = left + w;
if ( left < scroll || right > scroll + view_w ) {
if ( w > view_w ) {
w = view_w;
this.list.style.width = w + 'px';
if ( is_rtl ) left = right - w; else right = left + w;
}
var relative_offset = 0;
if ( left < scroll ) relative_offset = scroll - left; else if ( right > scroll + view_w ) relative_offset = -( right - scroll - view_w );
if ( is_rtl ) relative_offset = -relative_offset;
if ( relative_offset ) this.list.style[ anchor ] = ( nl + relative_offset ) + 'px';
}
},
autoComplete: function ( newVal, actVal, normalizedActVal, key, dontModify ) {
if ( newVal === actVal ) return true;
if ( dontModify || this.ime || !this.canSelect() ) return false;
// If we can't select properly or an IME composition is ongoing, autocompletion would be a major annoyance to the user.
if ( newVal.indexOf( actVal ) ) {
// Maybe it'll work with the normalized value (NFC)?
if ( normalizedActVal && newVal.indexOf( normalizedActVal ) === 0 ) {
if ( this.lastRealInput === actVal ) this.lastRealInput = normalizedActVal;
actVal = normalizedActVal;
} else {
return false;
}
}
// Actual input is a prefix of the new text. Fill in new text, selecting the newly added suffix
// such that it can be easily removed by typing backspace if the suggestion is unwanted.
this.text.focus();
this.text.value = newVal + key;
this.setSelection( actVal.length, newVal.length );
return true;
},
canSelect: function () {
return this.text.setSelectionRange ||
this.text.createTextRange ||
this.text.selectionStart !== undefined &&
this.text.selectionEnd !== undefined;
},
setSelection: function ( from, to ) {
// this.text must be focused (at least on IE)
if ( !this.text.value ) return;
if ( this.text.setSelectionRange ) { // e.g. khtml
this.text.setSelectionRange( from, to );
} else if ( this.text.selectionStart !== undefined ) {
if ( from > this.text.selectionStart ) {
this.text.selectionEnd = to;
this.text.selectionStart = from;
} else {
this.text.selectionStart = from;
this.text.selectionEnd = to;
}
} else if ( this.text.createTextRange ) { // IE
var new_selection = this.text.createTextRange();
new_selection.move( 'character', from );
new_selection.moveEnd( 'character', to - from );
new_selection.select();
}
},
getSelection: function () {
var from = 0,
to = 0;
// this.text must be focused (at least on IE)
if ( !this.text.value ) {
// No text.
} else if ( this.text.selectionStart !== undefined ) {
from = this.text.selectionStart;
to = this.text.selectionEnd;
} else if ( document.selection && document.selection.createRange ) { // IE
var rng = document.selection.createRange().duplicate();
if ( rng.parentElement() === this.text ) {
try {
var textRng = this.text.createTextRange();
textRng.move( 'character', 0 );
textRng.setEndPoint( 'EndToEnd', rng );
// We're in a single-line input box: no need to care about IE's strange
// handling of line ends
to = textRng.text.length;
textRng.setEndPoint( 'EndToStart', rng );
from = textRng.text.length;
} catch ( notFocused ) {
from = this.text.value.length;
to = from; // At end of text
}
}
}
return {
start: from,
end: to
};
},
saveView: function () {
this.lastSelection = this.getSelection();
},
processKey: function ( evt ) {
var dir = 0;
switch ( this.lastKey ) {
case UP:
dir = -1;
break;
case DOWN:
dir = 1;
break;
case PGUP:
dir = -HC.listSize;
break;
case PGDOWN:
dir = HC.listSize;
break;
case ESC: // Inhibit default behavior (revert to last real input in FF: we do that ourselves)
return evtKill( evt );
}
if ( dir ) {
if ( this.list.style.display !== 'none' ) {
// List is visible, so there are suggestions
this.highlightSuggestion( dir );
// Kill the event, otherwise some browsers (e.g., Firefox) may additionally treat an up-arrow
// as "place the text cursor at the front", which we don't want here.
return evtKill( evt );
} else if (
this.keyCount <= 1 &&
( !this.callbackObj || this.callbackObj.callsMade === this.callbackObj.nofCalls )
) {
// If no suggestions displayed, get them, unless we're already getting them.
this.textchange();
}
}
return true;
},
highlightSuggestion: function ( dir ) {
if ( noSuggestions || !this.list || this.list.style.display === 'none' ) return false;
var curr = this.list.selectedIndex;
var tgt = -1;
if ( dir === 0 ) {
if ( curr < 0 || curr >= this.list.options.length ) return false;
tgt = curr;
} else {
tgt = curr < 0 ? 0 : curr + dir;
tgt = tgt < 0 ? 0 : tgt;
if ( tgt >= this.list.options.length ) tgt = this.list.options.length - 1;
}
if ( tgt !== curr || dir === 0 ) {
if ( curr >= 0 && curr < this.list.options.length && dir !== 0 ) this.list.options[ curr ].selected = false;
this.list.options[ tgt ].selected = true;
// Get current input text
var v = this.text.value.split( '|' );
var key = v.length > 1 ? '|' + v[ 1 ] : '';
var completed = this.autoComplete( this.list.options[ tgt ].text, this.lastRealInput, null, key, false );
if ( !completed || this.list.options[ tgt ].text === this.lastRealInput ) {
this.text.value = this.list.options[ tgt ].text + key;
if ( this.canSelect() ) this.setSelection( this.list.options[ tgt ].text.length, this.list.options[ tgt ].text.length );
}
this.lastInput = this.list.options[ tgt ].text;
this.inputExists = true; // Might be wrong if from a dab list...
if ( this.icon ) this.icon.src = HC.existsYes;
this.state = CategoryEditor.CHANGE_PENDING;
}
return true;
},
resetKeySelection: function () {
if ( noSuggestions || !this.list || this.list.style.display === 'none' ) return false;
var curr = this.list.selectedIndex;
if ( curr >= 0 && curr < this.list.options.length ) {
this.list.options[ curr ].selected = false;
// Get current input text
var v = this.text.value.split( '|' );
var key = v.length > 1 ? '|' + v[ 1 ] : '';
// ESC is handled strangely by some browsers (e.g., FF); somehow it resets the input value before
// our event handlers ever get a chance to run.
var result = v[ 0 ] !== this.lastInput;
if ( v[ 0 ] !== this.lastRealInput ) {
this.text.value = this.lastRealInput + key;
result = true;
}
this.lastInput = this.lastRealInput;
return result;
}
return false;
}
}; // end CategoryEditor.prototype
function initialize() {
// User configurations: Do this here, called from the onload handler, so that users can
// override it easily in their own user script files by just declaring variables. JSconfig
// is some feature used at Wikimedia Commons.
var config = ( window.JSconfig !== undefined && JSconfig.keys ) ? JSconfig.keys : {};
HC.dont_add_to_watchlist = ( window.hotcat_dont_add_to_watchlist !== undefined ?
!!window.hotcat_dont_add_to_watchlist :
( config.HotCatDontAddToWatchlist !== undefined ? config.HotCatDontAddToWatchlist :
HC.dont_add_to_watchlist ) );
HC.no_autocommit = ( window.hotcat_no_autocommit !== undefined ?
!!window.hotcat_no_autocommit : ( config.HotCatNoAutoCommit !== undefined ?
config.HotCatNoAutoCommit :
// On talk namespace default autocommit off
( conf.wgNamespaceNumber % 2 ?
true : HC.no_autocommit ) ) );
HC.del_needs_diff = ( window.hotcat_del_needs_diff !== undefined ?
!!window.hotcat_del_needs_diff :
( config.HotCatDelNeedsDiff !== undefined ?
config.HotCatDelNeedsDiff :
HC.del_needs_diff ) );
HC.suggest_delay = window.hotcat_suggestion_delay || config.HotCatSuggestionDelay || HC.suggest_delay;
HC.editbox_width = window.hotcat_editbox_width || config.HotCatEditBoxWidth || HC.editbox_width;
HC.suggestions = window.hotcat_suggestions || config.HotCatSuggestions || HC.suggestions;
if ( typeof HC.suggestions !== 'string' || !suggestionConfigs[ HC.suggestions ] ) HC.suggestions = 'combined';
HC.fixed_search = ( window.hotcat_suggestions_fixed !== undefined ?
!!window.hotcat_suggestions_fixed : ( config.HotCatFixedSuggestions !== undefined ?
config.HotCatFixedSuggestions : HC.fixed_search ) );
HC.single_minor = ( window.hotcat_single_changes_are_minor !== undefined ?
!!window.hotcat_single_changes_are_minor :
( config.HotCatMinorSingleChanges !== undefined ?
config.HotCatMinorSingleChanges :
HC.single_minor ) );
HC.bg_changed = window.hotcat_changed_background || config.HotCatChangedBackground || HC.bg_changed;
HC.use_up_down = ( window.hotcat_use_category_links !== undefined ?
!!window.hotcat_use_category_links :
( config.HotCatUseCategoryLinks !== undefined ?
config.HotCatUseCategoryLinks :
HC.use_up_down ) );
HC.listSize = window.hotcat_list_size || config.HotCatListSize || HC.listSize;
if ( conf.wgDBname !== 'commonswiki' ) HC.changeTag = config.HotCatChangeTag || '';
// The next whole shebang is needed, because manual tags get not submitted except of save
if ( HC.changeTag ) {
var eForm = document.editform,
catRegExp = new RegExp( '^\\[\\[(' + HC.category_regexp + '):' ),
oldTxt;
// Returns true if minor change
var isMinorChange = function () {
var newTxt = eForm.wpTextbox1;
if ( !newTxt ) return;
newTxt = newTxt.value;
var oldLines = oldTxt.match( /^.*$/gm ),
newLines = newTxt.match( /^.*$/gm ),
cArr; // changes
var except = function ( aArr, bArr ) {
var result = [],
lArr, // larger
sArr; // smaller
if ( aArr.length < bArr.length ) {
lArr = bArr;
sArr = aArr;
} else {
lArr = aArr;
sArr = bArr;
}
for ( var i = 0; i < lArr.length; i++ ) {
var item = lArr[ i ];
var ind = $.inArray( item, sArr );
if ( ind === -1 ) result.push( item );
else sArr.splice( ind, 1 ); // don't check this item again
}
return result.concat( sArr );
};
cArr = except( oldLines, newLines );
if ( cArr.length ) {
cArr = $.grep( cArr, function ( c ) {
c = $.trim( c );
return ( c && !catRegExp.test( c ) );
} );
}
if ( !cArr.length ) {
oldTxt = newTxt;
return true;
}
};
if ( conf.wgAction === 'submit' && conf.wgArticleId && eForm && eForm.wpSummary && document.getElementById( 'wikiDiff' ) ) {
var sum = eForm.wpSummary,
sumA = eForm.wpAutoSummary;
if ( sum.value && sumA.value === HC.changeTag ) { // HotCat diff
// MD5 hash of the empty string, as HotCat edit is based on empty sum
sumA.value = sumA.value.replace( HC.changeTag, 'd41d8cd98f00b204e9800998ecf8427e' );
// Attr creation and event handling is not same in all (old) browsers so use $
var $ct = $( '<input type="hidden" name="wpChangeTags">' ).val( HC.changeTag );
$( eForm ).append( $ct );
oldTxt = eForm.wpTextbox1.value;
$( '#wpSave' ).one( 'click', function () {
if ( $ct.val() )
sum.value = sum.value.replace( ( HC.messages.using || HC.messages.prefix ), '' );
} );
var removeChangeTag = function () {
$( eForm.wpTextbox1 ).add( sum ).one( 'input', function () {
window.setTimeout( function () {
if ( !isMinorChange() ) $ct.val( '' );
else removeChangeTag();
}, 500 );
} );
};
removeChangeTag();
}
}
}
// Numeric input, make sure we have a numeric value
HC.listSize = parseInt( HC.listSize, 10 );
if ( isNaN( HC.listSize ) || HC.listSize < 5 ) HC.listSize = 5;
HC.listSize = Math.min( HC.listSize, 30 ); // Max size
// Localize search engine names
if ( HC.engine_names ) {
for ( var key in HC.engine_names )
if ( suggestionConfigs[ key ] && HC.engine_names[ key ] ) suggestionConfigs[ key ].name = HC.engine_names[ key ];
}
// Catch both native RTL and "faked" RTL through [[MediaWiki:Rtl.js]]
is_rtl = hasClass( document.body, 'rtl' );
if ( !is_rtl ) {
if ( document.defaultView && document.defaultView.getComputedStyle ) { // Gecko etc.
is_rtl = document.defaultView.getComputedStyle( document.body, null ).getPropertyValue( 'direction' );
} else if ( document.body.currentStyle ) { // IE, has subtle differences to getComputedStyle
is_rtl = document.body.currentStyle.direction;
} else { // Not exactly right, but best effort
is_rtl = document.body.style.direction;
}
is_rtl = ( is_rtl === 'rtl' );
}
}
function can_edit() {
var container = null;
switch ( mw.config.get( 'skin' ) ) {
case 'cologneblue':
container = document.getElementById( 'quickbar' );
/* fall through */
case 'standard':
case 'nostalgia':
if ( !container ) container = document.getElementById( 'topbar' );
var lks = container.getElementsByTagName( 'a' );
for ( var i = 0; i < lks.length; i++ ) {
if (
param( 'title', lks[ i ].href ) === conf.wgPageName &&
param( 'action', lks[ i ].href ) === 'edit'
) {
return true;
}
}
return false;
default:
// all modern skins:
return document.getElementById( 'ca-edit' ) !== null;
}
}
// Legacy stuff
function closeForm() {
// Close all open editors without redirect resolution and other asynchronous stuff.
for ( var i = 0; i < editors.length; i++ ) {
var edit = editors[ i ];
if ( edit.state === CategoryEditor.OPEN ) {
edit.cancel();
} else if ( edit.state === CategoryEditor.CHANGE_PENDING ) {
edit.sanitizeInput();
var value = edit.text.value.split( '|' );
var key = null;
if ( value.length > 1 ) key = value[ 1 ];
var v = value[ 0 ].replace( /_/g, ' ' ).replace( /^\s+|\s+$/g, '' );
if ( !v.length ) {
edit.cancel();
} else {
edit.currentCategory = v;
edit.currentKey = key;
edit.currentExists = this.inputExists;
edit.close();
}
}
}
}
function setup_upload() {
onUpload = true;
// Add an empty category bar at the end of the table containing the description, and change the onsubmit handler.
var ip = document.getElementById( 'mw-htmlform-description' ) || document.getElementById( 'wpDestFile' );
if ( !ip ) {
ip = document.getElementById( 'wpDestFile' );
while ( ip && ip.nodeName.toLowerCase() !== 'table' ) ip = ip.parentNode;
}
if ( !ip ) return;
var reupload = document.getElementById( 'wpForReUpload' );
var destFile = document.getElementById( 'wpDestFile' );
if (
( reupload && !!reupload.value ) ||
( destFile && ( destFile.disabled || destFile.readOnly ) )
) {
return; // re-upload form...
}
// Insert a table row with two fields (label and empty category bar)
var labelCell = make( 'td' );
var lineCell = make( 'td' );
// Create the category line
catLine = make( 'div' );
catLine.className = 'catlinks';
catLine.id = 'catlinks';
catLine.style.textAlign = is_rtl ? 'right' : 'left';
// We'll be inside a table row. Make sure that we don't have margins or strange borders.
catLine.style.margin = '0';
catLine.style.border = 'none';
lineCell.appendChild( catLine );
// Create the label
var label = null;
if ( window.UFUI && window.UIElements && UFUI.getLabel instanceof Function ) {
try {
label = UFUI.getLabel( 'wpCategoriesUploadLbl' );
} catch ( ex ) {
label = null;
}
}
if ( !label ) {
labelCell.id = 'hotcatLabel';
labelCell.appendChild( make( HC.categories, true ) );
} else {
labelCell.id = 'hotcatLabelTranslated';
labelCell.appendChild( label );
}
labelCell.className = 'mw-label';
labelCell.style.textAlign = 'right';
labelCell.style.verticalAlign = 'middle';
// Change the onsubmit handler
var form = document.getElementById( 'upload' ) || document.getElementById( 'mw-upload-form' );
if ( form && ip && ip.insertRow ) {
var newRow = ip.insertRow( -1 );
newRow.appendChild( labelCell );
newRow.appendChild( lineCell );
form.onsubmit = ( function ( oldSubmit ) {
return function () {
var do_submit = true;
if ( oldSubmit ) {
if ( typeof oldSubmit === 'string' ) {
// eslint-disable-next-line no-eval
do_submit = eval( oldSubmit );
} else if ( oldSubmit instanceof Function ) {
do_submit = oldSubmit.apply( form, arguments );
}
}
if ( !do_submit ) return false;
closeForm();
// Copy the categories
var eb = document.getElementById( 'wpUploadDescription' ) || document.getElementById( 'wpDesc' );
var addedOne = false;
for ( var i = 0; i < editors.length; i++ ) {
var t = editors[ i ].currentCategory;
if ( !t ) continue;
var key = editors[ i ].currentKey;
var new_cat = '[[' + HC.category_canonical + ':' + t + ( key ? '|' + key : '' ) + ']]';
// Only add if not already present
var cleanedText = eb.value
.replace( /<!--(\s|\S)*?-->/g, '' )
.replace( /<nowiki>(\s|\S)*?<\/nowiki>/g, '' );
if ( !find_category( cleanedText, t, true ) ) {
eb.value += '\n' + new_cat;
addedOne = true;
}
}
if ( addedOne ) {
// Remove "subst:unc" added by Flinfo if it didn't find categories
eb.value = eb.value.replace( /\{\{subst:unc\}\}/g, '' );
}
return true;
};
}( form.onsubmit ) );
}
}
var cleanedText = null;
function isOnPage( span ) {
if ( span.firstChild.nodeType !== Node.ELEMENT_NODE ) return null;
var catTitle = title( span.firstChild.getAttribute( 'href' ) );
if ( !catTitle ) return null;
catTitle = catTitle.substr( catTitle.indexOf( ':' ) + 1 ).replace( /_/g, ' ' );
if ( HC.blacklist && HC.blacklist.test( catTitle ) ) return null;
var result = {
title: catTitle,
match: [ '', '', '' ]
};
if ( pageText === null ) return result;
if ( cleanedText === null ) {
cleanedText = pageText
.replace( /<!--(\s|\S)*?-->/g, '' )
.replace( /<nowiki>(\s|\S)*?<\/nowiki>/g, '' );
}
result.match = find_category( cleanedText, catTitle, true );
return result;
}
var initialized = false;
var setupTimeout = null;
function findByClass( scope, tag, className ) {
var result = $( scope ).find( tag + '.' + className );
return ( result && result.length ) ? result[ 0 ] : null;
}
function errorAMC() {
alert( 'An error occurred. Unable to setup HotCat' );
}
function enableAMC() {
var api = new mw.Api();
return api.saveOption( 'mf_amc_optin', '1' ).then( function ( r ) {
if ( !r || r.options !== 'success' ) {
errorAMC();
return;
}
if ( window.confirm( 'Please reload your page to use hotcat.' ) ) {
location.reload();
}
}, function () {
errorAMC();
} );
}
function showWarning( text ) {
var warning = document.createElement( 'div' );
warning.setAttribute( 'style', 'padding: 20px; background: var(--wikt-palette-amber-0,orange); color: var(--wikt-palette-black,#333); font-weight: bold; margin-top: 20px;' );
warning.textContent = text;
var btn = document.createElement( 'button' );
btn.classList.add( 'mw-ui-button', 'cdx-button' );
btn.style.display = 'block';
btn.textContent = 'Enable HotCat and AMC mode on this page';
btn.addEventListener( 'click', function () {
enableAMC();
} );
warning.appendChild( btn );
document.getElementById( 'mw-content-text' ).appendChild( warning );
}
function setup( additionalWork ) {
if ( initialized ) return;
initialized = true;
if ( setupTimeout ) {
window.clearTimeout( setupTimeout );
setupTimeout = null;
}
// Find the category bar, or create an empty one if there isn't one. Then add -/+- links after
// each category, and add the + link.
catLine =
// Special:Upload
catLine ||
document.getElementById( 'mw-normal-catlinks' );
var hiddenCats = document.getElementById( 'mw-hidden-catlinks' );
if ( !catLine ) {
// Workaround for T24660
if ( mw.config.get('skin') === 'minerva' ) {
if ( document.body.classList.contains('mw-mf-amc-disabled') ) {
showWarning( 'HotCat requires AMC mode.' );
}
}
var footer = null;
if ( !hiddenCats ) {
footer = findByClass( document, 'div', 'printfooter' );
if ( !footer ) return; // Don't know where to insert the category line
}
catLine = make( 'div' );
catLine.id = 'mw-normal-catlinks';
catLine.style.textAlign = is_rtl ? 'right' : 'left';
// Add a label
var label = make( 'a' );
label.href = conf.wgArticlePath.replace( '$1', 'Special:Categories' );
label.title = HC.categories;
label.appendChild( make( HC.categories, true ) );
catLine.appendChild( label );
catLine.appendChild( make( ':', true ) );
// Insert the new category line
var container = ( hiddenCats ? hiddenCats.parentNode : document.getElementById( 'catlinks' ) );
if ( !container ) {
container = make( 'div' );
container.id = 'catlinks';
footer.parentNode.insertBefore( container, footer.nextSibling );
}
container.className = 'catlinks noprint';
container.style.display = '';
if ( !hiddenCats ) container.appendChild( catLine ); else container.insertBefore( catLine, hiddenCats );
} // end if catLine exists
if ( is_rtl ) catLine.dir = 'rtl';
// Create editors for all existing categories
function createEditors( line, is_hidden ) {
var i;
var cats = line.getElementsByTagName( 'li' );
if ( cats.length ) {
newDOM = true;
line = cats[ 0 ].parentNode;
} else {
cats = line.getElementsByTagName( 'span' );
}
// Copy cats, otherwise it'll also magically contain our added spans as it is a live collection!
var copyCats = new Array( cats.length );
for ( i = 0; i < cats.length; i++ ) copyCats[ i ] = cats[ i ];
for ( i = 0; i < copyCats.length; i++ ) {
var test = isOnPage( copyCats[ i ] );
if ( test !== null && test.match !== null && line ) {
// eslint-disable-next-line no-new
new CategoryEditor( line, copyCats[ i ], test.title, test.match[ 2 ], is_hidden );
}
}
return copyCats.length ? copyCats[ copyCats.length - 1 ] : null;
}
var lastSpan = createEditors( catLine, false );
// Create one to add a new category
// eslint-disable-next-line no-new
new CategoryEditor( newDOM ? catLine.getElementsByTagName( 'ul' )[ 0 ] : catLine, null, null, lastSpan !== null, false );
if ( !onUpload ) {
if ( pageText !== null && hiddenCats ) {
if ( is_rtl ) hiddenCats.dir = 'rtl';
createEditors( hiddenCats, true );
}
// And finally add the "multi-mode" span. (Do this at the end, otherwise it ends up in the list above.)
var enableMulti = make( 'span' );
enableMulti.className = 'noprint';
if ( is_rtl ) enableMulti.dir = 'rtl';
catLine.insertBefore( enableMulti, catLine.firstChild.nextSibling );
enableMulti.appendChild( make( '\xa0', true ) ); // nbsp
multiSpan = make( 'span' );
enableMulti.appendChild( multiSpan );
multiSpan.innerHTML = '(<a>' + HC.addmulti + '</a>)';
var lk = multiSpan.getElementsByTagName( 'a' )[ 0 ];
lk.onclick = function ( evt ) {
setMultiInput();
checkMultiInput();
return evtKill( evt );
};
lk.title = HC.multi_tooltip;
lk.style.cursor = 'pointer';
}
cleanedText = null;
if ( additionalWork instanceof Function ) additionalWork();
mw.hook( 'hotcat.ready' ).fire(); // Execute registered callback functions
$( 'body' ).trigger( 'hotcatSetupCompleted' );
}
function createCommitForm() {
if ( commitForm ) return;
var formContainer = make( 'div' );
formContainer.style.display = 'none';
document.body.appendChild( formContainer );
formContainer.innerHTML =
'<form id="hotcatCommitForm" method="post" enctype="multipart/form-data" action="' +
conf.wgScript + '?title=' + encodeURIComponent( conf.wgPageName ) + '&action=submit">' +
'<input type="hidden" name="wpTextbox1">' +
'<input type="hidden" name="model" value="' + conf.wgPageContentModel + '">' +
'<input type="hidden" name="format" value="text/x-wiki">' +
'<input type="hidden" name="wpSummary" value="">' +
'<input type="checkbox" name="wpMinoredit" value="1">' +
'<input type="checkbox" name="wpWatchthis" value="1">' +
'<input type="hidden" name="wpAutoSummary" value="d41d8cd98f00b204e9800998ecf8427e">' +
'<input type="hidden" name="wpEdittime">' +
'<input type="hidden" name="wpStarttime">' +
'<input type="hidden" name="wpDiff" value="wpDiff">' +
'<input type="hidden" name="oldid" value="0">' +
'<input type="hidden" name="wpIgnoreBlankSummary" value="1">' +
'<input type="submit" name="hcCommit" value="hcCommit">' +
'<input type="hidden" name="wpEditToken">' +
'<input type="hidden" name="wpUltimateParam" value="1">' +
'<input type="hidden" name="wpChangeTags">' +
'<input type="hidden" value="ℳ𝒲♥𝓊𝓃𝒾𝒸ℴ𝒹ℯ" name="wpUnicodeCheck">' +
'</form>';
commitForm = document.getElementById( 'hotcatCommitForm' );
}
function getPage() {
// We know we have an article here.
if ( !conf.wgArticleId ) {
// Doesn't exist yet. Disable on non-existing User pages -- might be a global user page.
if ( conf.wgNamespaceNumber === 2 ) return;
pageText = '';
pageTime = null;
setup( createCommitForm );
} else {
var url = wgServer + conf.wgScriptPath + '/api.php?format=json&callback=HotCat.start&action=query&rawcontinue=&titles=' +
encodeURIComponent( conf.wgPageName ) +
'&prop=info%7Crevisions&rvprop=content%7Ctimestamp%7Cids&meta=siteinfo&rvlimit=1&rvstartid=' +
conf.wgCurRevisionId;
var s = make( 'script' );
s.src = url;
HC.start = function ( json ) {
setPage( json );
setup( createCommitForm );
};
document.getElementsByTagName( 'head' )[ 0 ].appendChild( s );
setupTimeout = window.setTimeout( function () {
setup( createCommitForm );
}, 4000 ); // 4 sec, just in case getting the wikitext takes longer.
}
}
function setState( state ) {
var cats = state.split( '\n' );
if ( !cats.length ) return null;
if ( initialized && editors.length === 1 && editors[ 0 ].isAddCategory ) {
// Insert new spans and create new editors for them.
var newSpans = [];
var before = editors.length === 1 ? editors[ 0 ].span : null;
var i;
for ( i = 0; i < cats.length; i++ ) {
if ( !cats[ i ].length ) continue;
var cat = cats[ i ].split( '|' );
var key = cat.length > 1 ? cat[ 1 ] : null;
cat = cat[ 0 ];
var lk = make( 'a' );
lk.href = wikiPagePath( HC.category_canonical + ':' + cat );
lk.appendChild( make( cat, true ) );
lk.title = cat;
var span = make( 'span' );
span.appendChild( lk );
if ( !i ) catLine.insertBefore( make( ' ', true ), before );
catLine.insertBefore( span, before );
if ( before && i + 1 < cats.length ) parent.insertBefore( make( ' | ', true ), before );
newSpans.push( {
element: span,
title: cat,
key: key
} );
}
// And change the last one...
if ( before ) before.parentNode.insertBefore( make( ' | ', true ), before );
for ( i = 0; i < newSpans.length; i++ ) {
// eslint-disable-next-line no-new
new CategoryEditor( catLine, newSpans[ i ].element, newSpans[ i ].title, newSpans[ i ].key );
}
}
return null;
}
function getState() {
var result = null;
for ( var i = 0; i < editors.length; i++ ) {
var text = editors[ i ].currentCategory;
var key = editors[ i ].currentKey;
if ( text && text.length ) {
if ( key !== null ) text += '|' + key;
if ( result === null ) result = text; else result += '\n' + text;
}
}
return result;
}
function really_run() {
en_wiktionary_get_langdata(function() {
initialize();
if ( !HC.upload_disabled && conf.wgNamespaceNumber === -1 && conf.wgCanonicalSpecialPageName === 'Upload' && conf.wgUserName ) {
setup_upload();
setup( function () {
// Check for state restoration once the setup is done otherwise, but before signalling setup completion
if ( window.UploadForm && UploadForm.previous_hotcat_state ) UploadForm.previous_hotcat_state = setState( UploadForm.previous_hotcat_state );
} );
} else {
if ( !conf.wgIsArticle || conf.wgAction !== 'view' || param( 'diff' ) !== null || param( 'oldid' ) !== null || !can_edit() || HC.disable() ) return;
getPage();
}
});
}
function run() {
if ( HC.started ) return;
HC.started = true;
loadTrigger.register( really_run );
}
// Export legacy functions
window.hotcat_get_state = function () {
return getState();
};
window.hotcat_set_state = function ( state ) {
return setState( state );
};
window.hotcat_close_form = function () {
closeForm();
};
HC.runWhenReady = function ( callback ) {
// run user-registered code once HotCat is fully set up and ready.
mw.hook( 'hotcat.ready' ).add( callback );
};
// Make sure we don't get conflicts with AjaxCategories (core development that should one day
// replace HotCat).
mw.config.set( 'disableAJAXCategories', true );
// Run as soon as possible. This varies depending on MediaWiki version;
// window's 'load' event is always safe, but usually we can do better than that.
if ( conf.wgCanonicalSpecialPageName !== 'Upload' ) {
// Reload HotCat after (VE) edits (bug T103285)
mw.hook( 'postEdit' ).add( function () {
// Reset HotCat in case this is a soft reload (e.g. VisualEditor edit), unless the categories
// were not re-rendered and our interface is still there (e.g. DiscussionTools edit)
if ( document.querySelector( '#catlinks .hotcatlink' ) ) {
return;
}
catLine = null;
editors = [];
initialized = false;
HC.started = false;
run();
} );
}
// We can safely trigger just after user configuration is loaded.
// Use always() instead of then() to also start HotCat if the user module has problems.
$.when( mw.loader.using( 'user' ), $.ready ).always( run );
}( jQuery, mediaWiki ) );
// </nowiki>
qp2yp9147ad7pkve4h3sqrco2yhn5th
89010
89009
2026-08-06T23:33:24Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-HotCat.js]]
89009
javascript
text/javascript
// {{documentation}}
// See also: MediaWiki:Gadget-HotCat.js/local defaults
//window.hotcat_translations_from_commons = true; // Make HotCat load its interface from the Commons (the default setting so no need to set it here)
/**
HotCat V2.43
en.wiktionary fork (with better support for en.wiktionary templates)
upstream: https://commons.wikimedia.org/w/index.php?title=MediaWiki:Gadget-HotCat.js&oldid=818790002
The original script is hosted at [https://commons.wikimedia.org/wiki/MediaWiki:Gadget-HotCat.js].
See it for more information.
License: Quadruple licensed, any of the following:
GFDL
GPL
LGPL
Creative Commons Attribution 3.0 (CC-BY-3.0)
Requires MediaWiki >= MW 1.27.
*/
// <nowiki>
/* eslint-disable vars-on-top, one-var, camelcase, no-alert, curly */
/* global jQuery, mediaWiki, UFUI, JSconfig, UploadForm */
/* jslint strict:false, nonew:false, bitwise:true */
( function ( $, mw ) {
// BEGINNING OF en.wiktionary EXTRA CODE
var EN_WIKTIONARY_CONTENT_NAMESPACE = [0, 100, 118].indexOf(mw.config.get("wgNamespaceNumber")) >= 0;
var EN_WIKTIONARY_TOPICS = ["C", "c", "topics", "top", "topic", "catlangcode"];
var EN_WIKTIONARY_CATLANGNAME = ["cln", "catlangname", "Cln"];
var EN_WIKTIONARY_CATEGORIZE = ["categorize", "cat"];
var EN_WIKTIONARY_PREFER_TOPICS_FOR_NEW_ADDITIONS = true;
var EN_WIKTIONARY_PREFER_CATLANGNAME_FOR_NEW_ADDITIONS = true;
function regexpEscape(string) {
return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}
function cleanUpText(wikitext) {
return wikitext
.replace( /<!--(\s|\S)*?-->/g, replaceByBlanks )
.replace( /<nowiki>(\s|\S)*?<\/nowiki>/g, replaceByBlanks );
}
function en_wiktionary_find_category_allMatches(regexp, wikitext, data_key, data) {
var copiedtext = cleanUpText(wikitext);
var result = [];
var curr_match = null;
while ((curr_match = regexp.exec(copiedtext)) !== null) {
var obj = { match: curr_match };
obj[data_key] = data;
result.push(obj);
}
result.re = regexp;
return result.length ? result : null;
}
function en_wiktionary_codeToCanonicalName(callback) {
$.get({
"url": mw.util.wikiScript('index'),
"data": {
"action": "raw",
"title": "Module:languages/code to canonical name.json"
},
"cache": true
}).then(function(r) {
var parsedData;
try {
parsedData = JSON.parse(r);
callback({ ok: true, data: parsedData });
} catch (e) {
console.error(e);
callback({ ok: false });
}
});
}
function en_wiktionary_populate_langdata(data) {
data.langnames = [];
data.name_to_code = {};
for (var key in data.code_to_name) {
if (data.code_to_name.hasOwnProperty(key)) {
var value = data.code_to_name[key];
data.langnames.push(value);
data.name_to_code[value] = key;
}
}
var escaped = data.langnames.map(regexpEscape);
// sort from longest to shortest to ensure greedy alternation
escaped.sort(function (a, b) { return b.length - a.length; });
data.catlangnameRegexp = "^(" + escaped.join("|") + ") (.+)";
}
var EN_WIKTIONARY_LANGUAGE_DATA_STORAGE_KEY = "HotCat-en-wiktionary-language-data";
function en_wiktionary_cache_langdata(callback) {
if (!window.sessionStorage) {
callback();
return;
}
if (window.sessionStorage.hasOwnProperty(EN_WIKTIONARY_LANGUAGE_DATA_STORAGE_KEY)) {
try {
var timeNow = new Date().getTime();
var data = JSON.parse(window.sessionStorage.getItem(EN_WIKTIONARY_LANGUAGE_DATA_STORAGE_KEY));
if (timeNow < data.expiry) {
EN_WIKTIONARY_LANGDATA = data.data;
EN_WIKTIONARY_LANGDATA_READY = true;
EN_WIKTIONARY_LANGDATA.catlangnameRegexpCompiled = null;
callback();
return;
}
} catch (e) { }
}
en_wiktionary_codeToCanonicalName(function (r) {
if (r.ok) {
EN_WIKTIONARY_LANGDATA = {};
EN_WIKTIONARY_LANGDATA.code_to_name = r.data;
en_wiktionary_populate_langdata(EN_WIKTIONARY_LANGDATA);
}
EN_WIKTIONARY_LANGDATA_READY = true;
if (window.sessionStorage)
window.sessionStorage.setItem(EN_WIKTIONARY_LANGUAGE_DATA_STORAGE_KEY, JSON.stringify({
expiry: new Date().getTime() + 30 * 60 * 1000, // expire in 30 minutes
data: EN_WIKTIONARY_LANGDATA
}));
callback();
});
}
var EN_WIKTIONARY_LANGDATA = null;
var EN_WIKTIONARY_LANGDATA_READY = false;
var EN_WIKTIONARY_LANGDATA_LOCK = false;
var EN_WIKTIONARY_LANGDATA_QUEUE = [];
function en_wiktionary_get_langdata(callback) {
if (EN_WIKTIONARY_LANGDATA_READY) {
callback();
} else {
if (!EN_WIKTIONARY_LANGDATA_LOCK) {
EN_WIKTIONARY_LANGDATA_LOCK = true;
en_wiktionary_cache_langdata(function() {
callback();
EN_WIKTIONARY_LANGDATA_QUEUE.forEach(function(fn) { fn(); });
});
return;
}
EN_WIKTIONARY_LANGDATA_QUEUE.push(callback);
}
}
function EN_WIKTIONARY_TOPICS_START(lang, bar_required) {
return "\\{\\{\\s*(?:" + EN_WIKTIONARY_TOPICS.join("|") + ")\\s*\\|\\s*" + lang + "\\s*\\|" + (bar_required ? "" : "?");
}
function EN_WIKTIONARY_CATLANGNAME_START(lang, bar_required) {
return "\\{\\{\\s*(?:" + EN_WIKTIONARY_CATLANGNAME.join("|") + ")\\s*\\|\\s*" + lang + "\\s*\\|" + (bar_required ? "" : "?");
}
function EN_WIKTIONARY_CATEGORIZE_START(lang, bar_required) {
return "\\{\\{\\s*(?:" + EN_WIKTIONARY_CATEGORIZE.join("|") + ")\\s*\\|\\s*" + (lang == true ? "(" : "") + "[a-z0-9-]+" + (lang == true ? ")" : "") + "\\s*\\|" + (bar_required ? "" : "?");
}
var EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS = "[^}]*";
var EN_WIKTIONARY_TEMPLATE_START_OF_PARAMETER = "?(?<=\\|)";
var EN_WIKTIONARY_TEMPLATE_END_OF_PARAMETER = "(?=\\s*[\|\}])";
var EN_WIKTIONARY_TEMPLATE_END = "\\}\\}";
var EN_WIKTIONARY_TEMPLATE_END_WITH_WHITESPACE = "\\s*\\}\\}\n?";
function en_wiktionary_category_name_detect(category) {
var topicsMatch = category.match(/^([a-z-]+):(.+)$/);
if (topicsMatch) {
return { type: "topics", lang: topicsMatch[1], topic: topicsMatch[2] };
}
if (EN_WIKTIONARY_LANGDATA) {
var data = EN_WIKTIONARY_LANGDATA;
if (!data.catlangnameRegexpCompiled) {
data.catlangnameRegexpCompiled = new RegExp(data.catlangnameRegexp);
}
var catlangnameMatch = category.match(data.catlangnameRegexpCompiled);
if (catlangnameMatch && data.name_to_code[catlangnameMatch[1]]) {
return { type: "catlangname",
lang: data.name_to_code[catlangnameMatch[1]],
topic: catlangnameMatch[2] };
}
}
return null;
}
function en_wiktionary_find_category(wikitext, category, once) {
if (!EN_WIKTIONARY_CONTENT_NAMESPACE) return null;
var catname = en_wiktionary_category_name_detect(category);
var data, matched;
if (catname) {
if (catname.type == "topics") {
var topicTemplateRegex = new RegExp(
EN_WIKTIONARY_TOPICS_START(catname.lang, true) +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
EN_WIKTIONARY_TEMPLATE_START_OF_PARAMETER +
regexpEscape(catname.topic) +
EN_WIKTIONARY_TEMPLATE_END_OF_PARAMETER +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
EN_WIKTIONARY_TEMPLATE_END, "g");
data = { lang: catname.lang, name: catname.topic };
if (once) {
matched = topicTemplateRegex.exec(wikitext);
if (matched) {
matched.enWiktTopics = data;
}
return matched;
}
return en_wiktionary_find_category_allMatches(topicTemplateRegex, wikitext, "enWiktTopics", data);
} else if (catname.type == "catlangname") {
var clnTemplateRegex = new RegExp(
EN_WIKTIONARY_CATLANGNAME_START(catname.lang, true) +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
EN_WIKTIONARY_TEMPLATE_START_OF_PARAMETER +
regexpEscape(catname.topic) +
EN_WIKTIONARY_TEMPLATE_END_OF_PARAMETER +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
EN_WIKTIONARY_TEMPLATE_END, "g");
data = { lang: catname.lang, name: catname.topic };
if (once) {
matched = clnTemplateRegex.exec(wikitext);
if (matched) {
matched.enWiktCatlangname = data;
}
return matched;
}
return en_wiktionary_find_category_allMatches(clnTemplateRegex, wikitext, "enWiktCatlangname", data);
}
}
var catTemplateRegex = new RegExp(EN_WIKTIONARY_CATEGORIZE_START(true, true) +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
EN_WIKTIONARY_TEMPLATE_START_OF_PARAMETER +
regexpEscape(category) +
EN_WIKTIONARY_TEMPLATE_END_OF_PARAMETER +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
EN_WIKTIONARY_TEMPLATE_END, "g");
var catTemplateMatches = wikitext.match(catTemplateRegex);
if (catTemplateMatches) {
data = { lang: catTemplateMatches[1], name: category };
if (once) {
if (catTemplateMatches) {
catTemplateMatches.enWiktCategorize = data;
}
return catTemplateMatches;
}
catTemplateRegex.lastIndex = 0;
return en_wiktionary_find_category_allMatches(catTemplateRegex, wikitext, "enWiktCategorize", data);
}
return null;
}
function en_wiktionary_is_special_match(match) {
return !!(match.enWiktTopics || match.enWiktCatlangname || match.enWiktCategorize);
}
function en_wiktionary_three_to_two_split(wikitext, match) {
var before, after, data, removeTopic;
if (match.enWiktTopics) { // {{topics}}?
data = match.enWiktTopics;
before = wikitext.substring(0, match.match.index);
after = wikitext.substring(match.match.index);
var topicTemplateRegex = new RegExp(
EN_WIKTIONARY_TOPICS_START(data.lang, true) +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
EN_WIKTIONARY_TEMPLATE_START_OF_PARAMETER +
regexpEscape(data.name) +
EN_WIKTIONARY_TEMPLATE_END_OF_PARAMETER +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
EN_WIKTIONARY_TEMPLATE_END + "\\s*?\n?");
removeTopic = function(inner) {
// remove parameter from {{topics}}
var remaining = inner.replace(new RegExp("\\|" +
regexpEscape(data.name) +
EN_WIKTIONARY_TEMPLATE_END_OF_PARAMETER), "");
// if it is now empty, remove it
remaining = remaining.replace(new RegExp(
EN_WIKTIONARY_TOPICS_START(data.lang, false) +
EN_WIKTIONARY_TEMPLATE_END_WITH_WHITESPACE), "");
return remaining;
};
return [before, after.replace(topicTemplateRegex, removeTopic)];
} else if (match.enWiktCatlangname) { // {{cln}}?
data = match.enWiktCatlangname;
before = wikitext.substring(0, match.match.index);
after = wikitext.substring(match.match.index);
var clnTemplateRegex = new RegExp(
EN_WIKTIONARY_CATLANGNAME_START(data.lang, true) +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
EN_WIKTIONARY_TEMPLATE_START_OF_PARAMETER +
regexpEscape(data.name) +
EN_WIKTIONARY_TEMPLATE_END_OF_PARAMETER +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
EN_WIKTIONARY_TEMPLATE_END + "\\s*?\n?");
removeTopic = function(inner) {
// remove parameter from {{cln}}
var remaining = inner.replace(new RegExp("\\|" +
regexpEscape(data.name) +
EN_WIKTIONARY_TEMPLATE_END_OF_PARAMETER), "");
// if it is now empty, remove it
remaining = remaining.replace(new RegExp(
EN_WIKTIONARY_CATLANGNAME_START(data.lang, false) +
EN_WIKTIONARY_TEMPLATE_END_WITH_WHITESPACE), "");
return remaining;
};
return [before, after.replace(clnTemplateRegex, removeTopic)];
} else if (match.enWiktCategorize) { // {{cat}}?
data = match.enWiktCategorize;
before = wikitext.substring(0, match.match.index);
after = wikitext.substring(match.match.index);
var catTemplateRegex = new RegExp(
EN_WIKTIONARY_CATEGORIZE_START(data.lang, true) +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
EN_WIKTIONARY_TEMPLATE_START_OF_PARAMETER +
regexpEscape(data.name) +
EN_WIKTIONARY_TEMPLATE_END_OF_PARAMETER +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
EN_WIKTIONARY_TEMPLATE_END + "\\s*?\n?");
removeTopic = function(inner) {
// remove parameter from {{cln}}
var remaining = inner.replace(new RegExp("\\|" +
regexpEscape(data.name) +
EN_WIKTIONARY_TEMPLATE_END_OF_PARAMETER), "");
// if it is now empty, remove it
remaining = remaining.replace(new RegExp(
EN_WIKTIONARY_CATEGORIZE_START(data.lang, false) +
EN_WIKTIONARY_TEMPLATE_END_WITH_WHITESPACE), "");
return remaining;
};
return [before, after.replace(catTemplateRegex, removeTopic)];
}
before = wikitext.substring(0, match.match.index);
after = wikitext.substring(match.match.index + match.match[0].length);
return [before, after];
}
function en_wiktionary_remove_category(wikitext, match) {
var before_after = en_wiktionary_three_to_two_split(wikitext, match);
return before_after[0] + before_after[1];
}
function en_wiktionary_find_insertionpoint(wikitext, category) {
var catname = en_wiktionary_category_name_detect(category);
if (catname) {
if (catname.lang) {
var langname = catname.langname || (EN_WIKTIONARY_LANGDATA ? EN_WIKTIONARY_LANGDATA.code_to_name[catname.lang] : null);
if (langname) {
// try to put category under the language L2
var l2_heading_this = "^==\\s*" + regexpEscape(langname) + "\\s*==$";
var end_of_l2 = "\\n*(?:^----$)?\\n*^==(?!=)\\s*.*\\s*==$";
var clean_wikitext = cleanUpText(wikitext);
var finalRegexp = "(" + l2_heading_this + "[\\s\\S]+?)" + end_of_l2;
var match = new RegExp(finalRegexp, "m").exec(clean_wikitext);
if (match) {
var rawPoint = match.index + match[1].length;
var noPreLine = false;
if (wikitext.substring(rawPoint - 2, rawPoint) == "]]") {
return {
idx: rawPoint,
onCat: true
};
}
var cookedPoint = rawPoint;
if (wikitext.substring(cookedPoint - 3, cookedPoint) == "}}\n") {
// if the preceding line has a topics/cln template, add noPreLine
var i = cookedPoint - 2;
while (i >= 0 && wikitext[i] !== '\n') {
i--;
}
i += 2;
var previousLine = wikitext.substring(i, cookedPoint - 1);
if (previousLine.match(new RegExp("^\\{\\{(?:" + EN_WIKTIONARY_TOPICS.join("|") + "|" + EN_WIKTIONARY_CATLANGNAME.join("|") + ")\\|"))) {
noPreLine = true;
}
}
// hotfix: fix newline placement
if (!noPreLine && wikitext.charAt(cookedPoint - 1) != "\n" && wikitext.substring(cookedPoint, cookedPoint + 2) == "\n\n") {
++cookedPoint;
}
return {
idx: cookedPoint,
onCat: false,
noPreLine: noPreLine
};
} else {
var l2match = new RegExp(l2_heading_this, "m").exec(clean_wikitext);
if (l2match) {
// assuming there are no more L2s...
var l2_regex = new RegExp("^==(?!=)\\s*.*\\s*==$", "gm");
l2_regex.lastIndex = l2match.index + l2match[0].length;
var l2nextmatch = l2_regex.exec(clean_wikitext);
if (!l2nextmatch) {
// no more L2s
return {
idx: -1,
onCat: false
};
}
}
}
}
}
}
return null; // fallback
}
function en_wiktionary_add_category_hook(wikitext, category, cat_point) {
var catname = en_wiktionary_category_name_detect(category);
if (catname) {
var match, splitPoint;
if (catname.type == "topics") {
// try to find the first language template that matches and
// add the category there
var topicTemplateRegex = new RegExp("(?:^|(?<=\\n))(" +
EN_WIKTIONARY_TOPICS_START(catname.lang, true) +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
")" + EN_WIKTIONARY_TEMPLATE_END);
match = topicTemplateRegex.exec(cleanUpText(wikitext));
if (match) {
splitPoint = match.index + match[1].length;
return wikitext.substring(0, splitPoint) + "|" + catname.topic + wikitext.substring(splitPoint);
}
}
if (catname.type == "catlangname") {
// try to find the first language template that matches and
// add the category there
var clnTemplateRegex = new RegExp("(?:^|(?<=\\n))(" +
EN_WIKTIONARY_CATLANGNAME_START(catname.lang, true) +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
")" + EN_WIKTIONARY_TEMPLATE_END);
match = clnTemplateRegex.exec(cleanUpText(wikitext));
if (match) {
splitPoint = match.index + match[1].length;
return wikitext.substring(0, splitPoint) + "|" + catname.topic + wikitext.substring(splitPoint);
}
}
}
return null; // fallback
}
function en_wiktionary_add_category_string(category, onCat) {
var catname = en_wiktionary_category_name_detect(category);
if (catname) {
if (catname.type == "topics" && EN_WIKTIONARY_PREFER_TOPICS_FOR_NEW_ADDITIONS && !onCat) {
return "{{" + EN_WIKTIONARY_TOPICS[0] + "|" + catname.lang + "|" + catname.topic + "}}";
}
if (catname.type == "catlangname" && EN_WIKTIONARY_PREFER_CATLANGNAME_FOR_NEW_ADDITIONS && !onCat) {
return "{{" + EN_WIKTIONARY_CATLANGNAME[0] + "|" + catname.lang + "|" + catname.topic + "}}";
}
}
return null; // fallback
}
// END OF en.wiktionary EXTRA CODE
// Don't use mw.config.get() as that takes a copy of the config, and so doesn't
// account for values changing, e.g. wgCurRevisionId after a VE edit
var
conf = mw.config.values,
// when running on mobile domain - do not use wgServer.
wgServer = window.location.host.indexOf('.m.') > -1 ?
'//' + window.location.host : mw.config.get( 'wgServer' );
// Guard against double inclusions (in old IE/Opera element ids become window properties)
if ( ( window.HotCat && !window.HotCat.nodeName ) ||
conf.wgAction === 'edit' ) // Not on edit mode
return;
// Configuration stuff.
var HC = window.HotCat = {
// Localize these messages to the main language of your wiki.
messages: {
cat_removed: 'removed [[Category:$1]]',
template_removed: 'removed {{[[Category:$1]]}}',
cat_added: 'added [[Category:$1]]',
cat_keychange: 'new key for [[Category:$1]]: "$2"', // $2 is the new key
cat_notFound: 'Category "$1" not found',
cat_exists: 'Category "$1" already exists; not added.',
cat_resolved: ' (redirect [[Category:$1]] resolved)',
uncat_removed: 'removed {{uncategorized}}',
separator: '; ',
// Some text to prefix to the edit summary.
prefix: '',
// Some text to append to the edit summary. Named 'using' for historical reasons. If you prefer
// to have a marker at the front, use prefix and set this to the empty string.
using: ' using [[Help:Gadget-HotCat|HotCat]]',
// $1 is replaced by a number. If your language has several plural forms (c.f. [[:en:Dual (grammatical form)]]),
// you can set this to an array of strings suitable for passing to mw.language.configPlural().
// If that function doesn't exist, HotCat will simply fall back to using the last
// entry in the array.
multi_change: '$1 categories',
// Button text. Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
// see localization hook below.
commit: 'Save',
// Button text. Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
// see localization hook below.
ok: 'OK',
// Button text. Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
// see localization hook below.
cancel: 'Cancel',
// Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
// see localization hook below.
multi_error: 'Could not retrieve the page text from the server. Therefore, your category changes ' +
'cannot be saved. We apologize for the inconvenience.',
// Defaults to '[[' + category_canonical + ':$1]]'. Can be overridden if in the short edit summaries
// not the standard category name should be used but, say, a shorter namespace alias. $1 is replaced
// by a category name.
short_catchange: null
},
// Plural of category_canonical.
categories: 'Categories',
// Any category in this category is deemed a disambiguation category; i.e., a category that should not contain
// any items, but that contains links to other categories where stuff should be categorized. If you don't have
// that concept on your wiki, set it to null. Use blanks, not underscores.
disambig_category: 'Disambiguation',
// Any category in this category is deemed a (soft) redirect to some other category defined by a link
// to another non-blacklisted category. If your wiki doesn't have soft category redirects, set this to null.
// If a soft-redirected category contains more than one link to another non-blacklisted category, it's considered
// a disambiguation category instead.
redir_category: 'Category redirects',
// The little modification links displayed after category names. U+2212 is a minus sign; U+2193 and U+2191 are
// downward and upward pointing arrows. Do not use ↓ and ↑ in the code!
links: {
change: '(±)',
remove: '(\u2212)',
add: '(+)',
restore: '(×)',
undo: '(×)',
down: '(\u2193)',
up: '(\u2191)'
},
changeTag: conf.wgUserName ? 'HotCat' : '', // if tag is missing, edit is rejected
// The tooltips for the above links
tooltips: {
change: 'Modify',
remove: 'Remove',
add: 'Add a new category',
restore: 'Undo changes',
undo: 'Undo changes',
down: 'Open for modifying and display subcategories',
up: 'Open for modifying and display parent categories'
},
// The HTML content of the "enter multi-mode" link at the front.
addmulti: '<span>+<sup>+</sup></span>',
// Tooltip for the "enter multi-mode" link
multi_tooltip: 'Modify several categories',
// Return true to disable HotCat.
disable: function () {
var ns = conf.wgNamespaceNumber;
var nsIds = conf.wgNamespaceIds;
return (
ns < 0 || // Special pages; Special:Upload is handled differently
ns === 10 || // Templates
ns === 828 || // Module (Lua)
ns === 8 || // MediaWiki
ns === 6 && !conf.wgArticleId || // Non-existing file pages
ns === 2 && /\.(js|css)$/.test( conf.wgTitle ) || // User scripts
nsIds &&
( ns === nsIds.creator ||
ns === nsIds.timedtext ||
ns === nsIds.institution ) );
},
// A regexp matching a templates used to mark uncategorized pages, if your wiki does have that.
// If not, set it to null.
uncat_regexp: /\{\{\s*[Uu]ncategorized\s*[^}]*\}\}\s*(<!--.*?-->\s*)?/g,
// The images used for the little indication icon. Should not need changing.
existsYes: '//upload.wikimedia.org/wikipedia/commons/thumb/b/be/P_yes.svg/20px-P_yes.svg.png',
existsNo: '//upload.wikimedia.org/wikipedia/commons/thumb/4/42/P_no.svg/20px-P_no.svg.png',
// a list of categories which can be removed by removing a template
// key: the category without namespace
// value: A regexp matching the template name, again without namespace
// If you don't have this at your wiki, or don't want this, set it to an empty object {}.
template_categories: {},
// Names for the search engines
engine_names: {
searchindex: 'Search index',
pagelist: 'Page list',
combined: 'Combined search',
subcat: 'Subcategories',
parentcat: 'Parent categories'
},
// Override the decision of whether HotCat should help users by automatically
// capitalising the title in the user input text if the wiki has case-sensitive page names.
// Basically, this will make an API query to check the MediaWiki configuration and HotCat then sets
// this to true for most wikis, and to false on Wiktionary.
//
// You can set this directly if there is a problem with it. For example, Georgian Wikipedia (kawiki),
// is known to have different capitalisation logic between MediaWiki PHP and JavaScript. As such, automatic
// case changes in JavaScript by HotCat would be wrong.
capitalizePageNames: null,
// If upload_disabled is true, HotCat will not be used on the Upload form.
upload_disabled: false,
// Single regular expression matching blacklisted categories that cannot be changed or
// added using HotCat. For instance /\bstubs?$/ (any category ending with the word "stub"
// or "stubs"), or /(\bstubs?$)|\bmaintenance\b/ (stub categories and any category with the
// word "maintenance" in its title.
blacklist: null,
// Stuff changeable by users:
// Background for changed categories in multi-edit mode. Default is a very light salmon pink.
bg_changed: '#FCA',
// If true, HotCat will never automatically submit changes. HotCat will only open an edit page with
// the changes; users must always save explicitly.
no_autocommit: false,
// If true, the "category deletion" link "(-)" will never save automatically but always show an
// edit page where the user has to save the edit manually. Is false by default because that's the
// traditional behavior. This setting overrides no_autocommit for "(-)" links.
del_needs_diff: false,
// Time, in milliseconds, that HotCat waits after a keystroke before making a request to the
// server to get suggestions.
suggest_delay: 100,
// Default width, in characters, of the text input field.
editbox_width: 40,
// One of the engine_names above, to be used as the default suggestion engine.
suggestions: 'combined',
// If true, always use the default engine, and never display a selector.
fixed_search: false,
// If false, do not display the "up" and "down" links
use_up_down: true,
// Default list size
listSize: 10,
// If true, single category changes are marked as minor edits. If false, they're not.
single_minor: true,
// If true, never add a page to the user's watchlist. If false, pages get added to the watchlist if
// the user has the "Add pages I edit to my watchlist" or the "Add pages I create to my watchlist"
// options in his or her preferences set.
dont_add_to_watchlist: false,
shortcuts: null,
addShortcuts: function ( map ) {
if ( !map ) return;
window.HotCat.shortcuts = window.HotCat.shortcuts || {};
for ( var k in map ) {
if ( !map.hasOwnProperty( k ) || typeof k !== 'string' ) continue;
var v = map[ k ];
if ( typeof v !== 'string' ) continue;
k = k.replace( /^\s+|\s+$/g, '' );
v = v.replace( /^\s+|\s+$/g, '' );
if ( !k.length || !v.length ) continue;
window.HotCat.shortcuts[ k ] = v;
}
}
};
// More backwards compatibility. We have a few places where we test for the browser: once for
// Safari < 3.0, and twice for WebKit (Chrome or Safari, any versions)
var ua = navigator.userAgent.toLowerCase();
var is_webkit = /applewebkit\/\d+/.test( ua ) && ua.indexOf( 'spoofer' ) < 0;
var cat_prefix = null;
var noSuggestions = false;
function LoadTrigger( needed ) {
// Define methods in a closure so that self reference is available,
// also allows method calls to be detached.
var self = this;
self.queue = [];
self.needed = needed;
self.register = function ( callback ) {
if ( self.needed <= 0 ) callback(); // Execute directly
else self.queue.push( callback );
};
self.loaded = function () {
self.needed--;
if ( self.needed === 0 ) {
// Run queued callbacks once
for ( var i = 0; i < self.queue.length; i++ ) self.queue[ i ]();
self.queue = [];
}
};
}
// Used to delay running the HotCat setup until /local_defaults and localizations have been loaded.
var loadTrigger = new LoadTrigger( 2 );
function load( uri, callback ) {
var s = document.createElement( 'script' );
s.src = uri;
var called = false;
s.onload = s.onerror = function () {
if ( !called && callback ) {
called = true;
callback();
}
if ( s.parentNode ) {
s.parentNode.removeChild( s );
}
};
document.head.appendChild( s );
}
function loadJS( page, callback ) {
load( wgServer + conf.wgScript + '?title=' + encodeURIComponent( page ) + '&action=raw&ctype=text/javascript', callback );
}
function loadURI( href, callback ) {
var url = href;
if ( url.substring( 0, 2 ) === '//' ) url = window.location.protocol + url; else if ( url.substring( 0, 1 ) === '/' ) url = wgServer + url;
load( url, callback );
}
// Load local configurations, overriding the pre-set default values in the HotCat object above. This is always loaded
// from the wiki where this script is executing, even if this script itself is hotlinked from Commons. This can
// be used to change the default settings, or to provide localized interface texts for edit summaries and so on.
loadJS( 'MediaWiki:Gadget-HotCat.js/local_defaults', loadTrigger.loaded );
// Load localized UI texts. These are the texts that HotCat displays on the page itself. Texts shown in edit summaries
// should be localized in /local_defaults above.
if ( conf.wgUserLanguage !== 'en' ) {
// Lupo: somebody thought it would be a good idea to add this. So the default is true, and you have to set it to false
// explicitly if you're not on Commons and don't want that.
if ( window.hotcat_translations_from_commons === undefined ) window.hotcat_translations_from_commons = true;
// Localization hook to localize HotCat messages, tooltips, and engine names for wgUserLanguage.
if ( window.hotcat_translations_from_commons && wgServer.indexOf( '//commons' ) < 0 ) {
loadURI( '//commons.wikimedia.org/w/index.php?title=' +
'MediaWiki:Gadget-HotCat.js/' + conf.wgUserLanguage +
'&action=raw&ctype=text/javascript', loadTrigger.loaded );
} else {
// Load translations locally
loadJS( 'MediaWiki:Gadget-HotCat.js/' + conf.wgUserLanguage, loadTrigger.loaded );
}
} else {
loadTrigger.loaded();
}
// No further changes should be necessary here.
// The following regular expression strings are used when searching for categories in wikitext.
var wikiTextBlank = '[\\t _\\xA0\\u1680\\u180E\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000]+';
var wikiTextBlankRE = new RegExp( wikiTextBlank, 'g' );
// Regexp for handling blanks inside a category title or namespace name.
// See http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/includes/Title.php?revision=104051&view=markup#l2722
// See also http://www.fileformat.info/info/unicode/category/Zs/list.htm
// MediaWiki collapses several contiguous blanks inside a page title to one single blank. It also replace a
// number of special whitespace characters by simple blanks. And finally, blanks are treated as underscores.
// Therefore, when looking for page titles in wikitext, we must handle all these cases.
// Note: we _do_ include the horizontal tab in the above list, even though the MediaWiki software for some reason
// appears to not handle it. The zero-width space \u200B is _not_ handled as a space inside titles by MW.
var wikiTextBlankOrBidi = '[\\t _\\xA0\\u1680\\u180E\\u2000-\\u200B\\u200E\\u200F\\u2028-\\u202F\\u205F\\u3000]*';
// Whitespace regexp for handling whitespace between link components. Including the horizontal tab, but not \n\r\f\v:
// a link must be on one single line.
// MediaWiki also removes Unicode bidi override characters in page titles (and namespace names) completely.
// This is *not* handled, as it would require us to allow any of [\u200E\u200F\u202A-\u202E] between any two
// characters inside a category link. It _could_ be done though... We _do_ handle strange spaces, including the
// zero-width space \u200B, and bidi overrides between the components of a category link (adjacent to the colon,
// or adjacent to and inside of "[[" and "]]").
// First auto-localize the regexps for the category and the template namespaces.
var formattedNamespaces = conf.wgFormattedNamespaces;
var namespaceIds = conf.wgNamespaceIds;
function autoLocalize( namespaceNumber, fallback ) {
function createRegexpStr( name ) {
if ( !name || !name.length ) return '';
var regex_name = '';
for ( var i = 0; i < name.length; i++ ) {
var initial = name.charAt( i ),
ll = initial.toLowerCase(),
ul = initial.toUpperCase();
if ( ll === ul ) regex_name += initial; else regex_name += '[' + ll + ul + ']';
}
return regex_name
.replace( /([\\^$.?*+()])/g, '\\$1' )
.replace( wikiTextBlankRE, wikiTextBlank );
}
fallback = fallback.toLowerCase();
var canonical = formattedNamespaces[ String( namespaceNumber ) ].toLowerCase();
var regexp = createRegexpStr( canonical );
if ( fallback && canonical !== fallback ) regexp += '|' + createRegexpStr( fallback );
if ( namespaceIds ) {
for ( var cat_name in namespaceIds ) {
if (
typeof cat_name === 'string' &&
cat_name.toLowerCase() !== canonical &&
cat_name.toLowerCase() !== fallback &&
namespaceIds[ cat_name ] === namespaceNumber
) {
regexp += '|' + createRegexpStr( cat_name );
}
}
}
return regexp;
}
HC.category_canonical = formattedNamespaces[ '14' ];
HC.category_regexp = autoLocalize( 14, 'category' );
if ( formattedNamespaces[ '10' ] ) HC.template_regexp = autoLocalize( 10, 'template' );
// Utility functions. Yes, this duplicates some functionality that also exists in other places, but
// to keep this whole stuff in a single file not depending on any other on-wiki JavaScripts, we re-do
// these few operations here.
function make( arg, literal ) {
if ( !arg ) return null;
return literal ? document.createTextNode( arg ) : document.createElement( arg );
}
function param( name, uri ) {
uri = uri || document.location.href;
var re = new RegExp( '[&?]' + name + '=([^&#]*)' );
var m = re.exec( uri );
if ( m && m.length > 1 ) return decodeURIComponent( m[ 1 ] );
return null;
}
function title( href ) {
if ( !href ) return null;
var script = conf.wgScript + '?';
if ( href.indexOf( script ) === 0 || href.indexOf( wgServer + script ) === 0 || wgServer.substring( 0, 2 ) === '//' && href.indexOf( document.location.protocol + wgServer + script ) === 0 ) {
// href="/w/index.php?title=..."
return param( 'title', href );
} else {
// href="/wiki/..."
var prefix = conf.wgArticlePath.replace( '$1', '' );
if ( href.indexOf( prefix ) ) prefix = wgServer + prefix; // Fully expanded URL?
if ( href.indexOf( prefix ) && prefix.substring( 0, 2 ) === '//' ) prefix = document.location.protocol + prefix; // Protocol-relative wgServer?
if ( href.indexOf( prefix ) === 0 ) return decodeURIComponent( href.substring( prefix.length ) );
}
return null;
}
function hasClass( elem, name ) {
return ( ' ' + elem.className + ' ' ).indexOf( ' ' + name + ' ' ) >= 0;
}
function capitalize( str ) {
if ( !str || !str.length ) return str;
return str.substr( 0, 1 ).toUpperCase() + str.substr( 1 );
}
function wikiPagePath( pageName ) {
// Note: do not simply use encodeURI, it doesn't encode '&', which might break if wgArticlePath actually has the $1 in
// a query parameter.
return conf.wgArticlePath.replace( '$1', encodeURIComponent( pageName ).replace( /%3A/g, ':' ).replace( /%2F/g, '/' ) );
}
function escapeRE( str ) {
return str.replace( /([\\^$.?*+()[\]])/g, '\\$1' );
}
function substituteFactory( options ) {
options = options || {};
var lead = options.indicator || '$';
var indicator = escapeRE( lead );
var lbrace = escapeRE( options.lbrace || '{' );
var rbrace = escapeRE( options.rbrace || '}' );
var re;
re = new RegExp(
// $$
'(?:' + indicator + '(' + indicator + '))|' +
// $0, $1
'(?:' + indicator + '(\\d+))|' +
// ${key}
'(?:' + indicator + '(?:' + lbrace + '([^' + lbrace + rbrace + ']+)' + rbrace + '))|' +
// $key (only if first char after $ is not $, digit, or { )
'(?:' + indicator + '(?!(?:[' + indicator + lbrace + ']|\\d))(\\S+?)\\b)',
'g'
);
// Replace $1, $2, or ${key1}, ${key2}, or $key1, $key2 by values from map. $$ is replaced by a single $.
return function ( str, map ) {
if ( !map ) return str;
return str.replace( re, function ( match, prefix, idx, key, alpha ) {
if ( prefix === lead ) return lead;
var k = alpha || key || idx;
var replacement = typeof map[ k ] === 'function' ? map[ k ]( match, k ) : map[ k ];
return typeof replacement === 'string' ? replacement : ( replacement || match );
} );
};
}
var substitute = substituteFactory();
var replaceShortcuts = ( function () {
var replaceHash = substituteFactory( {
indicator: '#',
lbrace: '[',
rbrace: ']'
} );
return function ( str, map ) {
var s = replaceHash( str, map );
return HC.capitalizePageNames ? capitalize( s ) : s;
};
}() );
// Text modification
var findCatsRE =
new RegExp( '\\[\\[' + wikiTextBlankOrBidi + '(?:' + HC.category_regexp + ')' + wikiTextBlankOrBidi + ':[^\\]]+\\]\\]', 'g' );
function replaceByBlanks( match ) {
return match.replace( /(\s|\S)/g, ' ' ); // /./ doesn't match linebreaks. /(\s|\S)/ does.
}
function find_category( wikitext, category, once ) {
var cat_regex = null;
var result_hook = en_wiktionary_find_category(wikitext, category, once);
if (result_hook) {
return result_hook;
}
if ( HC.template_categories[ category ] ) {
cat_regex = new RegExp(
'\\{\\{' + wikiTextBlankOrBidi + '(' + HC.template_regexp + '(?=' + wikiTextBlankOrBidi + ':))?' + wikiTextBlankOrBidi +
'(?:' + HC.template_categories[ category ] + ')' +
wikiTextBlankOrBidi + '(\\|.*?)?\\}\\}',
'g'
);
} else {
var cat_name = escapeRE( category );
var initial = cat_name.substr( 0, 1 );
cat_regex = new RegExp(
'\\[\\[' + wikiTextBlankOrBidi + '(' + HC.category_regexp + ')' + wikiTextBlankOrBidi + ':' + wikiTextBlankOrBidi +
( initial === '\\' || !HC.capitalizePageNames ?
initial :
'[' + initial.toUpperCase() + initial.toLowerCase() + ']' ) +
cat_name.substring( 1 ).replace( wikiTextBlankRE, wikiTextBlank ) +
wikiTextBlankOrBidi + '(\\|.*?)?\\]\\]',
'g'
);
}
if ( once ) return cat_regex.exec( wikitext );
var copiedtext = wikitext
.replace( /<!--(\s|\S)*?-->/g, replaceByBlanks )
.replace( /<nowiki>(\s|\S)*?<\/nowiki>/g, replaceByBlanks );
var result = [];
var curr_match = null;
while ( ( curr_match = cat_regex.exec( copiedtext ) ) !== null ) {
result.push( {
match: curr_match
} );
}
result.re = cat_regex;
return result; // An array containing all matches, with positions, in result[ i ].match
}
var interlanguageRE = null;
function change_category( wikitext, toRemove, toAdd, key, is_hidden ) {
function find_insertionpoint( wikitext, category ) {
var copiedtext = wikitext
.replace( /<!--(\s|\S)*?-->/g, replaceByBlanks )
.replace( /<nowiki>(\s|\S)*?<\/nowiki>/g, replaceByBlanks );
// Search in copiedtext to avoid that we insert inside an HTML comment or a nowiki "element".
var result = en_wiktionary_find_insertionpoint( wikitext, category );
if (result) {
return result;
}
var index = -1;
findCatsRE.lastIndex = 0;
while ( findCatsRE.exec( copiedtext ) !== null ) index = findCatsRE.lastIndex;
if ( index < 0 ) {
// Find the index of the first interlanguage link...
var match = null;
if ( !interlanguageRE ) {
// Approximation without API: interlanguage links start with 2 to 3 lower case letters, optionally followed by
// a sequence of groups consisting of a dash followed by one or more lower case letters. Exceptions are "simple"
// and "tokipona".
match = /((^|\n\r?)(\[\[\s*(([a-z]{2,3}(-[a-z]+)*)|simple|tokipona)\s*:[^\]]+\]\]\s*))+$/.exec( copiedtext );
} else {
match = interlanguageRE.exec( copiedtext );
}
if ( match ) index = match.index;
return {
idx: index,
onCat: false
};
}
return {
idx: index,
onCat: index >= 0
};
}
var summary = [],
nameSpace = HC.category_canonical,
cat_point = -1, // Position of removed category;
keyChange = ( toRemove && toAdd && toRemove === toAdd && toAdd.length ),
matches,
noOnCatOverride = false;
if ( key ) key = '|' + key;
// Remove
if ( toRemove && toRemove.length ) {
matches = find_category( wikitext, toRemove );
if ( !matches || !matches.length ) {
return {
text: wikitext,
summary: summary,
error: HC.messages.cat_notFound.replace( /\$1/g, toRemove )
};
} else {
var after = wikitext;
for (var i = matches.length - 1; i > 0; i--) {
after = en_wiktionary_remove_category(after, matches[i]);
}
var before_after = en_wiktionary_three_to_two_split(after, matches[0]);
noOnCatOverride = en_wiktionary_is_special_match(matches[0]);
var before = before_after[0];
after = before_after[1];
if ( toAdd ) {
// nameSpace = matches[ 0 ].match[ 1 ] || nameSpace; Canonical namespace should be always preferred
if ( key === null ) key = matches[ 0 ].match[ 2 ];
// Remember the category key, if any.
}
// Remove whitespace (properly): strip whitespace, but only up to the next line feed.
// If we then have two linefeeds in a row, remove one. Otherwise, if we have two non-
// whitespace characters, insert a blank.
var i = before.length - 1;
while ( i >= 0 && before.charAt( i ) !== '\n' && before.substr( i, 1 ).search( /\s/ ) >= 0 ) i--;
var j = 0;
while ( j < after.length && after.charAt( j ) !== '\n' && after.substr( j, 1 ).search( /\s/ ) >= 0 ) j++;
if ( i >= 0 && before.charAt( i ) === '\n' && ( !after.length || j < after.length && after.charAt( j ) === '\n' ) ) i--;
if ( i >= 0 ) before = before.substring( 0, i + 1 ); else before = '';
if ( j < after.length ) after = after.substring( j ); else after = '';
if (
before.length && before.substring( before.length - 1 ).search( /\S/ ) >= 0 &&
after.length && after.substr( 0, 1 ).search( /\S/ ) >= 0
) {
before += ' ';
}
cat_point = before.length;
if ( cat_point === 0 && after.length && after.substr( 0, 1 ) === '\n' ) after = after.substr( 1 );
wikitext = before + after;
if ( !keyChange ) {
if ( HC.template_categories[ toRemove ] ) { summary.push( HC.messages.template_removed.replace( /\$1/g, toRemove ) ); } else { summary.push( HC.messages.cat_removed.replace( /\$1/g, toRemove ) ); }
}
}
}
// Add
if ( toAdd && toAdd.length ) {
matches = find_category( wikitext, toAdd );
if ( matches && matches.length ) {
// Already exists
return {
text: wikitext,
summary: summary,
error: HC.messages.cat_exists.replace( /\$1/g, toAdd )
};
} else {
var hooked = en_wiktionary_add_category_hook( wikitext, toAdd, cat_point );
if (hooked) {
wikitext = hooked;
summary.push( HC.messages.cat_added.replace( /\$1/g, toAdd ) );
} else {
var onCat = false;
var noPreLine = false;
if ( cat_point < 0 ) {
var point = find_insertionpoint ( wikitext, toAdd );
cat_point = point.idx;
onCat = point.onCat;
noPreLine = point.noPreLine;
} else {
onCat = true;
}
var newcatstring = en_wiktionary_add_category_string(toAdd, onCat && !noOnCatOverride) || ('[[' + nameSpace + ':' + toAdd + ( key || '' ) + ']]');
if ( cat_point >= 0 ) {
var suffix = wikitext.substring( cat_point );
wikitext = wikitext.substring( 0, cat_point ) + ( cat_point > 0 && !noPreLine ? '\n' : '' ) + newcatstring + ( !onCat ? '\n' : '' );
if ( suffix.length && suffix.substr( 0, 1 ) !== '\n' ) wikitext += '\n' + suffix; else wikitext += suffix;
} else {
if ( wikitext.length && wikitext.substr( wikitext.length - 1, 1 ) !== '\n' ) wikitext += '\n';
wikitext += ( wikitext.length ? '\n' : '' ) + newcatstring;
}
if ( keyChange ) {
var k = key || '';
if ( k.length ) k = k.substr( 1 );
summary.push( substitute( HC.messages.cat_keychange, [ null, toAdd, k ] ) );
} else {
summary.push( HC.messages.cat_added.replace( /\$1/g, toAdd ) );
}
if ( HC.uncat_regexp && !is_hidden ) {
var txt = wikitext.replace( HC.uncat_regexp, '' ); // Remove "uncat" templates
if ( txt.length !== wikitext.length ) {
wikitext = txt;
summary.push( HC.messages.uncat_removed );
}
}
}
}
}
return {
text: wikitext,
summary: summary,
error: null
};
}
// The real HotCat UI
function evtKeys( e ) {
/* eslint-disable no-bitwise */
var code = 0;
if ( e.ctrlKey ) { // All modern browsers
// Ctrl-click seems to be overloaded in FF/Mac (it opens a pop-up menu), so treat cmd-click
// as a ctrl-click, too.
if ( e.ctrlKey || e.metaKey ) code |= 1;
if ( e.shiftKey ) code |= 2;
}
return code;
}
function evtKill( e ) {
if ( e.preventDefault ) {
e.preventDefault();
e.stopPropagation();
} else {
e.cancelBubble = true;
}
return false;
}
var catLine = null,
onUpload = false,
editors = [],
commitButton = null,
commitForm = null,
multiSpan = null,
pageText = null,
pageTime = null,
pageWatched = false,
watchCreate = false,
watchEdit = false,
minorEdits = false,
editToken = null,
is_rtl = false,
serverTime = null,
lastRevId = null,
pageTextRevId = null,
conflictingUser = null,
newDOM = false; // true if MediaWiki serves the new UL-LI DOM for categories
function CategoryEditor() {
this.initialize.apply( this, arguments );
}
function setPage( json ) {
var startTime = null;
if ( json && json.query ) {
if ( json.query.pages ) {
var page = json.query.pages[ !conf.wgArticleId ? '-1' : String( conf.wgArticleId ) ];
if ( page ) {
if ( page.revisions && page.revisions.length ) {
// Revisions are sorted by revision ID, hence [ 0 ] is the one we asked for, and possibly there's a [ 1 ] if we're
// not on the latest revision (edit conflicts and such).
pageText = page.revisions[ 0 ][ '*' ];
if ( page.revisions[ 0 ].timestamp ) pageTime = page.revisions[ 0 ].timestamp.replace( /\D/g, '' );
if ( page.revisions[ 0 ].revid ) pageTextRevId = page.revisions[ 0 ].revid;
if ( page.revisions.length > 1 ) conflictingUser = page.revisions[ 1 ].user;
}
if ( page.lastrevid ) lastRevId = page.lastrevid;
if ( page.starttimestamp ) startTime = page.starttimestamp.replace( /\D/g, '' );
pageWatched = typeof page.watched === 'string';
if ( json.query.tokens ) editToken = json.query.tokens.csrftoken;
if ( page.langlinks && ( !json[ 'query-continue' ] || !json[ 'query-continue' ].langlinks ) ) {
// We have interlanguage links, and we got them all.
var re = '';
for ( var i = 0; i < page.langlinks.length; i++ ) re += ( i > 0 ? '|' : '' ) + page.langlinks[ i ].lang.replace( /([\\^$.?*+()])/g, '\\$1' );
if ( re.length ) interlanguageRE = new RegExp( '((^|\\n\\r?)(\\[\\[\\s*(' + re + ')\\s*:[^\\]]+\\]\\]\\s*))+$' );
}
}
}
// Siteinfo
if ( json.query.general ) {
if ( json.query.general.time && !startTime ) startTime = json.query.general.time.replace( /\D/g, '' );
if ( HC.capitalizePageNames === null ) {
// ResourceLoader's JSParser doesn't like .case, so override eslint.
// eslint-disable-next-line dot-notation
HC.capitalizePageNames = ( json.query.general[ 'case' ] === 'first-letter' );
}
}
serverTime = startTime;
// Userinfo
if ( json.query.userinfo && json.query.userinfo.options ) {
watchCreate = !HC.dont_add_to_watchlist && json.query.userinfo.options.watchcreations === '1';
watchEdit = !HC.dont_add_to_watchlist && json.query.userinfo.options.watchdefault === '1';
minorEdits = json.query.userinfo.options.minordefault === 1;
// If the user has the "All edits are minor" preference enabled, we should honor that
// for single category changes, no matter what the site configuration is.
if ( minorEdits ) HC.single_minor = true;
}
}
}
var saveInProgress = false;
function initiateEdit( doEdit, failure ) {
if ( saveInProgress ) return;
saveInProgress = true;
var oldButtonState;
if ( commitButton ) {
oldButtonState = commitButton.disabled;
commitButton.disabled = true;
}
function fail() {
saveInProgress = false;
if ( commitButton ) commitButton.disabled = oldButtonState;
failure.apply( this, arguments );
}
// Must use Ajax here to get the user options and the edit token.
$.getJSON(
wgServer + conf.wgScriptPath + '/api.php?' +
'format=json&action=query&rawcontinue=&titles=' + encodeURIComponent( conf.wgPageName ) +
'&prop=info%7Crevisions%7Clanglinks&inprop=watched&rvprop=content%7Ctimestamp%7Cids%7Cuser&lllimit=500' +
'&rvlimit=2&rvdir=newer&rvstartid=' + conf.wgCurRevisionId + '&meta=siteinfo%7Cuserinfo%7Ctokens&type=csrf&uiprop=options',
function ( json ) {
setPage( json );
doEdit( fail );
}
).fail( function ( req ) {
fail( req.status + ' ' + req.statusText );
} );
}
function multiChangeMsg( count ) {
var msg = HC.messages.multi_change;
if ( typeof msg !== 'string' && msg.length )
if ( mw.language && mw.language.convertPlural ) { msg = mw.language.convertPlural( count, msg ); } else { msg = msg[ msg.length - 1 ]; }
return substitute( msg, [ null, String( count ) ] );
}
function currentTimestamp() {
var now = new Date();
var ts = String( now.getUTCFullYear() );
function two( s ) {
return s.substr( s.length - 2 );
}
ts +=
two( '0' + ( now.getUTCMonth() + 1 ) ) +
two( '0' + now.getUTCDate() ) +
two( '00' + now.getUTCHours() ) +
two( '00' + now.getUTCMinutes() ) +
two( '00' + now.getUTCSeconds() );
return ts;
}
function performChanges( failure, singleEditor ) {
if ( pageText === null ) {
failure( HC.messages.multi_error );
return;
}
// Backwards compatibility after message change (added $2 to cat_keychange)
if ( HC.messages.cat_keychange.indexOf( '$2' ) < 0 ) HC.messages.cat_keychange += '"$2"';
// More backwards-compatibility with earlier HotCat versions:
if ( !HC.messages.short_catchange ) HC.messages.short_catchange = '[[' + HC.category_canonical + ':$1]]';
// Create a form and submit it. We don't use the edit API (api.php?action=edit) because
// (a) sensibly reporting back errors like edit conflicts is always a hassle, and
// (b) we want to show a diff for multi-edits anyway, and
// (c) we want to trigger onsubmit events, allowing user code to intercept the edit.
// Using the form, we can do (b) and (c), and we get (a) for free. And, of course, using the form
// automatically reloads the page with the updated categories on a successful submit, which
// we would have to do explicitly if we used the edit API.
var action;
// Normally, we don't have to care about edit conflicts. If some other user edited the page in the meantime, the
// server will take care of it and merge the edit automatically or present an edit conflict screen. However, the
// server suppresses edit conflicts with oneself. Hence, if we have a conflict, and the conflicting user is the
// current user, then we set the "oldid" value and switch to diff, which gives the "you are editing an old version;
// if you save, any more recent changes will be lost" screen.
var selfEditConflict = ( lastRevId !== null && lastRevId !== conf.wgCurRevisionId || pageTextRevId !== null &&
pageTextRevId !== conf.wgCurRevisionId ) && conflictingUser && conflictingUser === conf.wgUserName;
if ( singleEditor && !singleEditor.noCommit && !HC.no_autocommit && editToken && !selfEditConflict ) {
// If we do have an edit conflict, but not with ourself, that's no reason not to attempt to save: the server side may actually be able to
// merge the changes. We just need to make sure that we do present a diff view if it's a self edit conflict.
commitForm.wpEditToken.value = editToken;
action = commitForm.wpDiff;
if ( action ) action.name = action.value = 'wpSave';
} else {
action = commitForm.wpSave;
if ( action ) action.name = action.value = 'wpDiff';
}
var result = {
text: pageText
},
changed = [],
added = [],
deleted = [],
changes = 0,
toEdit = singleEditor ? [ singleEditor ] : editors,
error = null,
edit,
i;
for ( i = 0; i < toEdit.length; i++ ) {
edit = toEdit[ i ];
if ( edit.state === CategoryEditor.CHANGED ) {
result = change_category(
result.text,
edit.originalCategory,
edit.currentCategory,
edit.currentKey,
edit.currentHidden );
if ( !result.error ) {
changes++;
if ( !edit.originalCategory || !edit.originalCategory.length ) {
added.push( edit.currentCategory );
} else {
changed.push( {
from: edit.originalCategory,
to: edit.currentCategory
} );
}
} else if ( error === null ) {
error = result.error;
}
} else if (
edit.state === CategoryEditor.DELETED && edit.originalCategory && edit.originalCategory.length ) {
result = change_category(
result.text,
edit.originalCategory,
null, null, false );
if ( !result.error ) {
changes++;
deleted.push( edit.originalCategory );
} else if ( error === null ) {
error = result.error;
}
}
}
if ( error !== null ) { // Do not commit if there were errors
action = commitForm.wpSave;
if ( action ) action.name = action.value = 'wpDiff';
}
// Fill in the form and submit it
commitForm.wpMinoredit.checked = minorEdits;
commitForm.wpWatchthis.checked = !conf.wgArticleId && watchCreate || watchEdit || pageWatched;
if ( conf.wgArticleId || !!singleEditor ) {
// Prepare change-tag save
if ( action && action.value === 'wpSave' ) {
if ( HC.changeTag ) {
commitForm.wpChangeTags.value = HC.changeTag;
HC.messages.using = '';
HC.messages.prefix = '';
}
} else {
commitForm.wpAutoSummary.value = HC.changeTag;
}
if ( changes === 1 ) {
if ( result.summary && result.summary.length ) commitForm.wpSummary.value = HC.messages.prefix + result.summary.join( HC.messages.separator ) + HC.messages.using;
commitForm.wpMinoredit.checked = HC.single_minor || minorEdits;
} else if ( changes ) {
var summary = [];
var shortSummary = [];
// Deleted
for ( i = 0; i < deleted.length; i++ ) summary.push( '−' + substitute( HC.messages.short_catchange, [ null, deleted[ i ] ] ) );
if ( deleted.length === 1 ) shortSummary.push( '−' + substitute( HC.messages.short_catchange, [ null, deleted[ 0 ] ] ) ); else if ( deleted.length ) shortSummary.push( '− ' + multiChangeMsg( deleted.length ) );
// Added
for ( i = 0; i < added.length; i++ ) summary.push( '+' + substitute( HC.messages.short_catchange, [ null, added[ i ] ] ) );
if ( added.length === 1 ) shortSummary.push( '+' + substitute( HC.messages.short_catchange, [ null, added[ 0 ] ] ) ); else if ( added.length ) shortSummary.push( '+ ' + multiChangeMsg( added.length ) );
// Changed
var arrow = is_rtl ? '\u2190' : '\u2192'; // left and right arrows. Don't use ← and → in the code.
for ( i = 0; i < changed.length; i++ ) {
if ( changed[ i ].from !== changed[ i ].to ) {
summary.push(
'±' + substitute( HC.messages.short_catchange, [ null, changed[ i ].from ] ) + arrow +
substitute( HC.messages.short_catchange, [ null, changed[ i ].to ] )
);
} else {
summary.push( '±' + substitute( HC.messages.short_catchange, [ null, changed[ i ].from ] ) );
}
}
if ( changed.length === 1 ) {
if ( changed[ 0 ].from !== changed[ 0 ].to ) {
shortSummary.push(
'±' + substitute( HC.messages.short_catchange, [ null, changed[ 0 ].from ] ) + arrow +
substitute( HC.messages.short_catchange, [ null, changed[ 0 ].to ] )
);
} else {
shortSummary.push( '±' + substitute( HC.messages.short_catchange, [ null, changed[ 0 ].from ] ) );
}
} else if ( changed.length ) {
shortSummary.push( '± ' + multiChangeMsg( changed.length ) );
}
if ( summary.length ) {
summary = summary.join( HC.messages.separator );
if ( summary.length > 200 - HC.messages.prefix.length - HC.messages.using.length ) summary = shortSummary.join( HC.messages.separator );
commitForm.wpSummary.value = HC.messages.prefix + summary + HC.messages.using;
}
}
}
commitForm.wpTextbox1.value = result.text;
commitForm.wpStarttime.value = serverTime || currentTimestamp();
commitForm.wpEdittime.value = pageTime || commitForm.wpStarttime.value;
if ( selfEditConflict ) commitForm.oldid.value = String( pageTextRevId || conf.wgCurRevisionId );
// Submit the form in a way that triggers onsubmit events: commitForm.submit() doesn't.
commitForm.hcCommit.click();
}
function resolveOne( page, toResolve ) {
var cats = page.categories,
lks = page.links,
is_dab = false,
is_redir = typeof page.redirect === 'string', // Hard redirect?
is_hidden = page.categoryinfo && typeof page.categoryinfo.hidden === 'string',
is_missing = typeof page.missing === 'string',
i;
for ( i = 0; i < toResolve.length; i++ ) {
if ( i && toResolve[ i ].dabInputCleaned !== page.title.substring( page.title.indexOf( ':' ) + 1 ) ) continue;
// Note: the server returns in page an NFC normalized Unicode title. If our input was not NFC normalized, we may not find
// any entry here. If we have only one editor to resolve (the most common case, I presume), we may simply skip the check.
toResolve[ i ].currentHidden = is_hidden;
toResolve[ i ].inputExists = !is_missing;
toResolve[ i ].icon.src = ( is_missing ? HC.existsNo : HC.existsYes );
}
if ( is_missing ) return;
if ( !is_redir && cats && ( HC.disambig_category || HC.redir_category ) ) {
for ( var c = 0; c < cats.length; c++ ) {
var cat = cats[ c ].title;
// Strip namespace prefix
if ( cat ) {
cat = cat.substring( cat.indexOf( ':' ) + 1 ).replace( /_/g, ' ' );
if ( cat === HC.disambig_category ) {
is_dab = true;
break;
} else if ( cat === HC.redir_category ) {
is_redir = true;
break;
}
}
}
}
if ( !is_redir && !is_dab ) return;
if ( !lks || !lks.length ) return;
var titles = [];
for ( i = 0; i < lks.length; i++ ) {
if (
// Category namespace -- always true since we ask only for the category links
lks[ i ].ns === 14 &&
// Name not empty
lks[ i ].title && lks[ i ].title.length
) {
// Internal link to existing thingy. Extract the page name and remove the namespace.
var match = lks[ i ].title;
match = match.substring( match.indexOf( ':' ) + 1 );
// Exclude blacklisted categories.
if ( !HC.blacklist || !HC.blacklist.test( match ) ) titles.push( match );
}
}
if ( !titles.length ) return;
for ( i = 0; i < toResolve.length; i++ ) {
if ( i && toResolve[ i ].dabInputCleaned !== page.title.substring( page.title.indexOf( ':' ) + 1 ) ) continue;
toResolve[ i ].inputExists = true; // Might actually be wrong if it's a redirect pointing to a non-existing category
toResolve[ i ].icon.src = HC.existsYes;
if ( titles.length > 1 ) {
toResolve[ i ].dab = titles;
} else {
toResolve[ i ].text.value =
titles[ 0 ] + ( toResolve[ i ].currentKey !== null ? '|' + toResolve[ i ].currentKey : '' );
}
}
}
function resolveRedirects( toResolve, params ) {
if ( !params || !params.query || !params.query.pages ) return;
for ( var p in params.query.pages ) resolveOne( params.query.pages[ p ], toResolve );
}
function resolveMulti( toResolve, callback ) {
var i;
for ( i = 0; i < toResolve.length; i++ ) {
toResolve[ i ].dab = null;
toResolve[ i ].dabInput = toResolve[ i ].lastInput;
}
if ( noSuggestions ) {
callback( toResolve );
return;
}
// Use %7C instead of |, otherwise Konqueror insists on re-encoding the arguments, resulting in doubly encoded
// category names. (That is a bug in Konqueror. Other browsers don't have this problem.)
var args = 'action=query&prop=info%7Clinks%7Ccategories%7Ccategoryinfo&plnamespace=14' +
'&pllimit=' + ( toResolve.length * 10 ) +
'&cllimit=' + ( toResolve.length * 10 ) +
'&format=json&titles=';
for ( i = 0; i < toResolve.length; i++ ) {
var v = toResolve[ i ].dabInput;
v = replaceShortcuts( v, HC.shortcuts );
toResolve[ i ].dabInputCleaned = v;
args += encodeURIComponent( 'Category:' + v );
if ( i + 1 < toResolve.length ) args += '%7C';
}
$.getJSON( wgServer + conf.wgScriptPath + '/api.php?' + args,
function ( json ) {
resolveRedirects( toResolve, json );
callback( toResolve );
} ).fail( function ( req ) {
if ( !req ) noSuggestions = true;
callback( toResolve );
} );
}
function makeActive( which ) {
if ( which.is_active ) return;
for ( var i = 0; i < editors.length; i++ )
if ( editors[ i ] !== which ) editors[ i ].inactivate();
which.is_active = true;
if ( which.dab ) {
// eslint-disable-next-line no-use-before-define
showDab( which );
} else {
// Check for programmatic value changes.
var expectedInput = which.lastRealInput || which.lastInput || '';
var actualValue = which.text.value || '';
if ( !expectedInput.length && actualValue.length || expectedInput.length && actualValue.indexOf( expectedInput ) ) {
// Somehow the field's value appears to have changed, and which.lastSelection therefore is no longer valid. Try to set the
// cursor at the end of the category, and do not display the old suggestion list.
which.showsList = false;
var v = actualValue.split( '|' );
which.lastRealInput = which.lastInput = v[ 0 ];
if ( v.length > 1 ) which.currentKey = v[ 1 ];
if ( which.lastSelection ) {
which.lastSelection = {
start: v[ 0 ].length,
end: v[ 0 ].length
};
}
}
if ( which.showsList ) which.displayList();
if ( which.lastSelection ) {
if ( is_webkit ) {
// WebKit (Safari, Chrome) has problems selecting inside focus()
// See http://code.google.com/p/chromium/issues/detail?id=32865#c6
window.setTimeout(
function () {
which.setSelection( which.lastSelection.start, which.lastSelection.end );
},
1 );
} else {
which.setSelection( which.lastSelection.start, which.lastSelection.end );
}
}
}
}
function showDab( which ) {
if ( !which.is_active ) {
makeActive( which );
} else {
which.showSuggestions( which.dab, false, null, null ); // do autocompletion, no key, no engine selector
which.dab = null;
}
}
function multiSubmit() {
var toResolve = [];
for ( var i = 0; i < editors.length; i++ )
if ( editors[ i ].state === CategoryEditor.CHANGE_PENDING || editors[ i ].state === CategoryEditor.OPEN ) toResolve.push( editors[ i ] );
if ( !toResolve.length ) {
initiateEdit( function ( failure ) {
performChanges( failure );
}, function ( msg ) {
alert( msg );
} );
return;
}
resolveMulti( toResolve, function ( resolved ) {
var firstDab = null;
var dontChange = false;
for ( var i = 0; i < resolved.length; i++ ) {
if ( resolved[ i ].lastInput !== resolved[ i ].dabInput ) {
// We didn't disable all the open editors, but we did asynchronous calls. It is
// theoretically possible that the user changed something...
dontChange = true;
} else {
if ( resolved[ i ].dab ) {
if ( !firstDab ) firstDab = resolved[ i ];
} else {
if ( resolved[ i ].acceptCheck( true ) ) resolved[ i ].commit();
}
}
}
if ( firstDab ) {
showDab( firstDab );
} else if ( !dontChange ) {
initiateEdit( function ( failure ) {
performChanges( failure );
}, function ( msg ) {
alert( msg );
} );
}
} );
}
function setMultiInput() {
if ( commitButton || onUpload ) return;
commitButton = make( 'input' );
commitButton.type = 'button';
commitButton.value = HC.messages.commit;
commitButton.onclick = multiSubmit;
if ( multiSpan ) multiSpan.parentNode.replaceChild( commitButton, multiSpan ); else catLine.appendChild( commitButton );
}
function checkMultiInput() {
if ( !commitButton ) return;
var hasChanges = false;
for ( var i = 0; i < editors.length; i++ ) {
if ( editors[ i ].state !== CategoryEditor.UNCHANGED ) {
hasChanges = true;
break;
}
}
commitButton.disabled = !hasChanges;
}
var suggestionEngines = {
opensearch: {
uri: '/api.php?format=json&action=opensearch&namespace=14&limit=30&search=Category:$1', // $1 = search term
// Function to convert result of uri into an array of category names
handler: function ( queryResult, queryKey ) {
if ( queryResult && queryResult.length >= 2 ) {
var key = queryResult[ 0 ].substring( queryResult[ 0 ].indexOf( ':' ) + 1 );
var titles = queryResult[ 1 ];
var exists = false;
if ( !cat_prefix ) cat_prefix = new RegExp( '^(' + HC.category_regexp + '):' );
for ( var i = 0; i < titles.length; i++ ) {
cat_prefix.lastIndex = 0;
var m = cat_prefix.exec( titles[ i ] );
if ( m && m.length > 1 ) {
titles[ i ] = titles[ i ].substring( titles[ i ].indexOf( ':' ) + 1 ); // rm namespace
if ( key === titles[ i ] ) exists = true;
} else {
titles.splice( i, 1 ); // Nope, it's not a category after all.
i--;
}
}
titles.exists = exists;
if ( queryKey !== key ) titles.normalized = key;
// Remember the NFC normalized key we got back from the server
return titles;
}
return null;
}
},
internalsearch: {
uri: '/api.php?format=json&action=query&list=allpages&apnamespace=14&aplimit=30&apfrom=$1&apprefix=$1',
handler: function ( queryResult ) {
if ( queryResult && queryResult.query && queryResult.query.allpages ) {
var titles = queryResult.query.allpages;
for ( var i = 0; i < titles.length; i++ ) titles[ i ] = titles[ i ].title.substring( titles[ i ].title.indexOf( ':' ) + 1 ); // rm namespace
return titles;
}
return null;
}
},
exists: {
uri: '/api.php?format=json&action=query&prop=info&titles=Category:$1',
handler: function ( queryResult, queryKey ) {
if ( queryResult && queryResult.query && queryResult.query.pages && !queryResult.query.pages[ -1 ] ) {
// Should have exactly 1
for ( var p in queryResult.query.pages ) {
var title = queryResult.query.pages[ p ].title;
title = title.substring( title.indexOf( ':' ) + 1 );
var titles = [ title ];
titles.exists = true;
if ( queryKey !== title ) titles.normalized = title;
// NFC
return titles;
}
}
return null;
}
},
subcategories: {
uri: '/api.php?format=json&action=query&list=categorymembers&cmtype=subcat&cmlimit=max&cmtitle=Category:$1',
handler: function ( queryResult ) {
if ( queryResult && queryResult.query && queryResult.query.categorymembers ) {
var titles = queryResult.query.categorymembers;
for ( var i = 0; i < titles.length; i++ ) titles[ i ] = titles[ i ].title.substring( titles[ i ].title.indexOf( ':' ) + 1 ); // rm namespace
return titles;
}
return null;
}
},
parentcategories: {
uri: '/api.php?format=json&action=query&prop=categories&titles=Category:$1&cllimit=max',
handler: function ( queryResult ) {
if ( queryResult && queryResult.query && queryResult.query.pages ) {
for ( var p in queryResult.query.pages ) {
if ( queryResult.query.pages[ p ].categories ) {
var titles = queryResult.query.pages[ p ].categories;
for ( var i = 0; i < titles.length; i++ ) titles[ i ] = titles[ i ].title.substring( titles[ i ].title.indexOf( ':' ) + 1 ); // rm namespace
return titles;
}
}
}
return null;
}
}
};
var suggestionConfigs = {
searchindex: {
name: 'Search index',
engines: [ 'opensearch' ],
cache: {},
show: true,
temp: false,
noCompletion: false
},
pagelist: {
name: 'Page list',
engines: [ 'internalsearch', 'exists' ],
cache: {},
show: true,
temp: false,
noCompletion: false
},
combined: {
name: 'Combined search',
engines: [ 'opensearch', 'internalsearch' ],
cache: {},
show: true,
temp: false,
noCompletion: false
},
subcat: {
name: 'Subcategories',
engines: [ 'subcategories' ],
cache: {},
show: true,
temp: true,
noCompletion: true
},
parentcat: {
name: 'Parent categories',
engines: [ 'parentcategories' ],
cache: {},
show: true,
temp: true,
noCompletion: true
}
};
CategoryEditor.UNCHANGED = 0;
CategoryEditor.OPEN = 1; // Open, but no input yet
CategoryEditor.CHANGE_PENDING = 2; // Open, some input made
CategoryEditor.CHANGED = 3;
CategoryEditor.DELETED = 4;
// Support: IE6
// IE6 sometimes forgets to redraw the list when editors are opened or closed.
// Adding/removing a dummy element helps, at least when opening editors.
var dummyElement = make( '\xa0', true );
function forceRedraw() {
if ( dummyElement.parentNode ) document.body.removeChild( dummyElement ); else document.body.appendChild( dummyElement );
}
// Event keyCodes that we handle in the text input field/suggestion list.
var BS = 8,
TAB = 9,
RET = 13,
ESC = 27,
SPACE = 32,
PGUP = 33,
PGDOWN = 34,
UP = 38,
DOWN = 40,
DEL = 46,
IME = 229;
CategoryEditor.prototype = {
initialize: function ( line, span, after, key, is_hidden ) {
// If a span is given, 'after' is the category title, otherwise it may be an element after which to
// insert the new span. 'key' is likewise overloaded; if a span is given, it is the category key (if
// known), otherwise it is a boolean indicating whether a bar shall be prepended.
if ( !span ) {
this.isAddCategory = true;
// Create add span and append to catLinks
this.originalCategory = '';
this.originalKey = null;
this.originalExists = false;
if ( !newDOM ) {
span = make( 'span' );
span.className = 'noprint';
if ( key ) {
span.appendChild( make( ' | ', true ) );
if ( after ) {
after.parentNode.insertBefore( span, after.nextSibling );
after = after.nextSibling;
} else if (line) {
line.appendChild( span );
}
} else if ( line && line.firstChild ) {
span.appendChild( make( ' ', true ) );
line.appendChild( span );
}
}
this.linkSpan = make( 'span' );
this.linkSpan.className = 'noprint nopopups hotcatlink';
var lk = make( 'a' );
lk.href = '#catlinks';
lk.onclick = this.open.bind( this );
lk.appendChild( make( HC.links.add, true ) );
lk.title = HC.tooltips.add;
this.linkSpan.appendChild( lk );
span = make( newDOM ? 'li' : 'span' );
span.className = 'noprint';
if ( is_rtl ) span.dir = 'rtl';
span.appendChild( this.linkSpan );
if ( after ) {
after.parentNode.insertBefore( span, after.nextSibling );
} else if ( line ) {
line.appendChild( span );
}
this.normalLinks = null;
this.undelLink = null;
this.catLink = null;
} else {
if ( is_rtl ) span.dir = 'rtl';
this.isAddCategory = false;
this.catLink = span.firstChild;
this.originalCategory = after;
this.originalKey = ( key && key.length > 1 ) ? key.substr( 1 ) : null; // > 1 because it includes the leading bar
this.originalExists = !hasClass( this.catLink, 'new' );
// Create change and del links
this.makeLinkSpan();
if ( !this.originalExists && this.upDownLinks ) this.upDownLinks.style.display = 'none';
span.appendChild( this.linkSpan );
}
this.originalHidden = is_hidden;
this.line = line;
this.engine = HC.suggestions;
this.span = span;
this.currentCategory = this.originalCategory;
this.currentExists = this.originalExists;
this.currentHidden = this.originalHidden;
this.currentKey = this.originalKey;
this.state = CategoryEditor.UNCHANGED;
this.lastSavedState = CategoryEditor.UNCHANGED;
this.lastSavedCategory = this.originalCategory;
this.lastSavedKey = this.originalKey;
this.lastSavedExists = this.originalExists;
this.lastSavedHidden = this.originalHidden;
if ( this.catLink && this.currentKey ) this.catLink.title = this.currentKey;
editors[ editors.length ] = this;
},
makeLinkSpan: function () {
this.normalLinks = make( 'span' );
var lk = null;
if ( this.originalCategory && this.originalCategory.length ) {
lk = make( 'a' );
lk.href = '#catlinks';
lk.onclick = this.remove.bind( this );
lk.appendChild( make( HC.links.remove, true ) );
lk.title = HC.tooltips.remove;
this.normalLinks.appendChild( make( ' ', true ) );
this.normalLinks.appendChild( lk );
}
if ( !HC.template_categories[ this.originalCategory ] ) {
lk = make( 'a' );
lk.href = '#catlinks';
lk.onclick = this.open.bind( this );
lk.appendChild( make( HC.links.change, true ) );
lk.title = HC.tooltips.change;
this.normalLinks.appendChild( make( ' ', true ) );
this.normalLinks.appendChild( lk );
if ( !noSuggestions && HC.use_up_down ) {
this.upDownLinks = make( 'span' );
lk = make( 'a' );
lk.href = '#catlinks';
lk.onclick = this.down.bind( this );
lk.appendChild( make( HC.links.down, true ) );
lk.title = HC.tooltips.down;
this.upDownLinks.appendChild( make( ' ', true ) );
this.upDownLinks.appendChild( lk );
lk = make( 'a' );
lk.href = '#catlinks';
lk.onclick = this.up.bind( this );
lk.appendChild( make( HC.links.up, true ) );
lk.title = HC.tooltips.up;
this.upDownLinks.appendChild( make( ' ', true ) );
this.upDownLinks.appendChild( lk );
this.normalLinks.appendChild( this.upDownLinks );
}
}
this.linkSpan = make( 'span' );
this.linkSpan.className = 'noprint nopopups hotcatlink';
this.linkSpan.appendChild( this.normalLinks );
this.undelLink = make( 'span' );
this.undelLink.className = 'nopopups hotcatlink';
this.undelLink.style.display = 'none';
lk = make( 'a' );
lk.href = '#catlinks';
lk.onclick = this.restore.bind( this );
lk.appendChild( make( HC.links.restore, true ) );
lk.title = HC.tooltips.restore;
this.undelLink.appendChild( make( ' ', true ) );
this.undelLink.appendChild( lk );
this.linkSpan.appendChild( this.undelLink );
},
invokeSuggestions: function ( dont_autocomplete ) {
if ( this.engine && suggestionConfigs[ this.engine ] && suggestionConfigs[ this.engine ].temp && !dont_autocomplete ) this.engine = HC.suggestions; // Reset to a search upon input
this.state = CategoryEditor.CHANGE_PENDING;
var self = this;
window.setTimeout( function () {
self.textchange( dont_autocomplete );
}, HC.suggest_delay );
},
makeForm: function () {
var form = make( 'form' );
form.method = 'POST';
form.onsubmit = this.accept.bind( this );
this.form = form;
var self = this;
var text = make( 'input' );
text.type = 'text';
text.size = HC.editbox_width;
if ( !noSuggestions ) {
// Be careful here to handle IME input. This is browser/OS/IME dependent, but basically there are two mechanisms:
// - Modern (DOM Level 3) browsers use compositionstart/compositionend events to signal composition; if the
// composition is not canceled, there'll be a textInput event following. During a composition key events are
// either all suppressed (FF/Gecko), or otherwise have keyDown === IME for all keys (Webkit).
// - Webkit sends a textInput followed by keyDown === IME and a keyUp with the key that ended composition.
// - Gecko doesn't send textInput but just a keyUp with the key that ended composition, without sending keyDown
// first. Gecko doesn't send any keydown while IME is active.
// - Older browsers signal composition by keyDown === IME for the first and subsequent keys for a composition. The
// first keyDown !== IME is certainly after the end of the composition. Typically, composition end can also be
// detected by a keyDown IME with a keyUp of space, tab, escape, or return.
text.onkeyup = function ( evt ) {
var key = evt.keyCode || 0;
if ( self.ime && self.lastKey === IME && !self.usesComposition && ( key === TAB || key === RET || key === ESC || key === SPACE ) ) self.ime = false;
if ( self.ime ) return true;
if ( key === UP || key === DOWN || key === PGUP || key === PGDOWN ) {
// In case a browser doesn't generate keypress events for arrow keys...
if ( self.keyCount === 0 ) return self.processKey( evt );
} else {
if ( key === ESC && self.lastKey !== IME ) {
if ( !self.resetKeySelection() ) {
// No undo of key selection: treat ESC as "cancel".
self.cancel();
return;
}
}
// Also do this for ESC as a workaround for Firefox bug 524360
// https://bugzilla.mozilla.org/show_bug.cgi?id=524360
self.invokeSuggestions( key === BS || key === DEL || key === ESC );
}
return true;
};
text.onkeydown = function ( evt ) {
var key = evt.keyCode || 0;
self.lastKey = key;
self.keyCount = 0;
// DOM Level < 3 IME input
if ( !self.ime && key === IME && !self.usesComposition ) {
// self.usesComposition catches browsers that may emit spurious keydown IME after a composition has ended
self.ime = true;
} else if ( self.ime && key !== IME && !( key >= 16 && key <= 20 || key >= 91 && key <= 93 || key === 144 ) ) {
// Ignore control keys: ctrl, shift, alt, alt gr, caps lock, windows/apple cmd keys, num lock. Only the windows keys
// terminate IME (apple cmd doesn't), but they also cause a blur, so it's OK to ignore them here.
// Note: Safari 4 (530.17) propagates ESC out of an IME composition (observed at least on Win XP).
self.ime = false;
}
if ( self.ime ) return true;
// Handle return explicitly, to override the default form submission to be able to check for ctrl
if ( key === RET ) return self.accept( evt );
// Inhibit default behavior of ESC (revert to last real input in FF: we do that ourselves)
return ( key === ESC ) ? evtKill( evt ) : true;
};
// And handle continued pressing of arrow keys
text.onkeypress = function ( evt ) {
self.keyCount++;
return self.processKey( evt );
};
$( text ).on( 'focus', function () {
makeActive( self );
} );
// On IE, blur events are asynchronous, and may thus arrive after the element has lost the focus. Since IE
// can get the selection only while the element is active (has the focus), we may not always get the selection.
// Therefore, use an IE-specific synchronous event on IE...
// Don't test for text.selectionStart being defined;
$( text ).on(
( text.onbeforedeactivate !== undefined && text.createTextRange ) ? 'beforedeactivate' : 'blur',
this.saveView.bind( this ) );
// DOM Level 3 IME handling
try {
// Setting lastKey = IME provides a fake keyDown for Gecko's single keyUp after a cmposition. If we didn't do this,
// cancelling a composition via ESC would also cancel and close the whole category input editor.
$( text ).on( 'compositionstart', function () {
self.lastKey = IME;
self.usesComposition = true;
self.ime = true;
} );
$( text ).on( 'compositionend', function () {
self.lastKey = IME;
self.usesComposition = true;
self.ime = false;
} );
$( text ).on( 'textInput', function () {
self.ime = false;
self.invokeSuggestions( false );
} );
} catch ( any ) {
// Just in case some browsers might produce exceptions with these DOM Level 3 events
}
$( text ).on( 'blur', function () {
self.usesComposition = false;
self.ime = false;
} );
}
this.text = text;
this.icon = make( 'img' );
var list = null;
if ( !noSuggestions ) {
list = make( 'select' );
list.onclick = function () {
if ( self.highlightSuggestion( 0 ) ) self.textchange( false, true );
};
list.ondblclick = function ( e ) {
if ( self.highlightSuggestion( 0 ) ) self.accept( e );
};
list.onchange = function () {
self.highlightSuggestion( 0 );
self.text.focus();
};
list.onkeyup = function ( evt ) {
if ( evt.keyCode === ESC ) {
self.resetKeySelection();
self.text.focus();
window.setTimeout( function () {
self.textchange( true );
}, HC.suggest_delay );
} else if ( evt.keyCode === RET ) {
self.accept( evt );
}
};
if ( !HC.fixed_search ) {
var engineSelector = make( 'select' );
for ( var key in suggestionConfigs ) {
if ( suggestionConfigs[ key ].show ) {
var opt = make( 'option' );
opt.value = key;
if ( key === this.engine ) opt.selected = true;
opt.appendChild( make( suggestionConfigs[ key ].name, true ) );
engineSelector.appendChild( opt );
}
}
engineSelector.onchange = function () {
self.engine = self.engineSelector.options[ self.engineSelector.selectedIndex ].value;
self.text.focus();
self.textchange( true, true ); // Don't autocomplete, force re-display of list
};
this.engineSelector = engineSelector;
}
}
this.list = list;
function button_label( id, defaultText ) {
var label = null;
if (
onUpload &&
window.UFUI !== undefined &&
window.UIElements !== undefined &&
UFUI.getLabel instanceof Function
) {
try {
label = UFUI.getLabel( id, true );
// Extract the plain text. IE doesn't know that Node.TEXT_NODE === 3
while ( label && label.nodeType !== 3 ) label = label.firstChild;
} catch ( ex ) {
label = null;
}
}
if ( !label || !label.data ) return defaultText;
return label.data;
}
// Do not use type 'submit'; we cannot detect modifier keys if we do
var OK = make( 'input' );
OK.type = 'button';
OK.value = button_label( 'wpOkUploadLbl', HC.messages.ok );
OK.onclick = this.accept.bind( this );
this.ok = OK;
var cancel = make( 'input' );
cancel.type = 'button';
cancel.value = button_label( 'wpCancelUploadLbl', HC.messages.cancel );
cancel.onclick = this.cancel.bind( this );
this.cancelButton = cancel;
var span = make( 'span' );
span.className = 'hotcatinput';
span.style.position = 'relative';
span.appendChild( text );
// Support: IE8, IE9
// Put some text into this span (a0 is nbsp) and make sure it always stays on the same
// line as the input field, otherwise, IE8/9 miscalculates the height of the span and
// then the engine selector may overlap the input field.
span.appendChild( make( '\xa0', true ) );
span.style.whiteSpace = 'nowrap';
if ( list ) span.appendChild( list );
if ( this.engineSelector ) span.appendChild( this.engineSelector );
if ( !noSuggestions ) span.appendChild( this.icon );
span.appendChild( OK );
span.appendChild( cancel );
form.appendChild( span );
form.style.display = 'none';
this.span.appendChild( form );
},
display: function ( evt ) {
if ( this.isAddCategory && !onUpload && this.line ) {
// eslint-disable-next-line no-new
new CategoryEditor( this.line, null, this.span, true ); // Create a new one
}
if ( !commitButton && !onUpload ) {
for ( var i = 0; i < editors.length; i++ ) {
if ( editors[ i ].state !== CategoryEditor.UNCHANGED ) {
setMultiInput();
break;
}
}
}
if ( !this.form ) this.makeForm();
if ( this.list ) this.list.style.display = 'none';
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
this.currentCategory = this.lastSavedCategory;
this.currentExists = this.lastSavedExists;
this.currentHidden = this.lastSavedHidden;
this.currentKey = this.lastSavedKey;
this.icon.src = ( this.currentExists ? HC.existsYes : HC.existsNo );
this.text.value = this.currentCategory + ( this.currentKey !== null ? '|' + this.currentKey : '' );
this.originalState = this.state;
this.lastInput = this.currentCategory;
this.inputExists = this.currentExists;
this.state = this.state === CategoryEditor.UNCHANGED ? CategoryEditor.OPEN : CategoryEditor.CHANGE_PENDING;
this.lastSelection = {
start: this.currentCategory.length,
end: this.currentCategory.length
};
this.showsList = false;
// Display the form
if ( this.catLink ) this.catLink.style.display = 'none';
this.linkSpan.style.display = 'none';
this.form.style.display = 'inline';
this.ok.disabled = false;
// Kill the event before focussing, otherwise IE will kill the onfocus event!
var result = evtKill( evt );
this.text.focus();
this.text.readOnly = false;
checkMultiInput();
return result;
},
show: function ( evt, engine, readOnly ) {
var result = this.display( evt );
var v = this.lastSavedCategory;
if ( !v.length ) return result;
this.text.readOnly = !!readOnly;
this.engine = engine;
this.textchange( false, true ); // do autocompletion, force display of suggestions
forceRedraw();
return result;
},
open: function ( evt ) {
return this.show( evt, ( this.engine && suggestionConfigs[ this.engine ].temp ) ? HC.suggestions : this.engine );
},
down: function ( evt ) {
return this.show( evt, 'subcat', true );
},
up: function ( evt ) {
return this.show( evt, 'parentcat' );
},
cancel: function () {
if ( this.isAddCategory && !onUpload ) {
this.removeEditor(); // We added a new adder when opening
return;
}
// Close, re-display link
this.inactivate();
this.form.style.display = 'none';
if ( this.catLink ) this.catLink.style.display = '';
this.linkSpan.style.display = '';
this.state = this.originalState;
this.currentCategory = this.lastSavedCategory;
this.currentKey = this.lastSavedKey;
this.currentExists = this.lastSavedExists;
this.currentHidden = this.lastSavedHidden;
if ( this.catLink )
if ( this.currentKey && this.currentKey.length ) { this.catLink.title = this.currentKey; } else { this.catLink.title = ''; }
if ( this.state === CategoryEditor.UNCHANGED ) {
if ( this.catLink ) this.catLink.style.backgroundColor = 'transparent';
} else {
if ( !onUpload ) {
try {
this.catLink.style.backgroundColor = HC.bg_changed;
} catch ( ex ) {}
}
}
checkMultiInput();
forceRedraw();
},
removeEditor: function () {
if ( !newDOM ) {
var next = this.span.nextSibling;
if ( next ) next.parentNode.removeChild( next );
}
if (this.span && this.span.parentNode) {
this.span.parentNode.removeChild( this.span );
}
for ( var i = 0; i < editors.length; i++ ) {
if ( editors[ i ] === this ) {
editors.splice( i, 1 );
break;
}
}
checkMultiInput();
},
rollback: function ( evt ) {
this.undoLink.parentNode.removeChild( this.undoLink );
this.undoLink = null;
this.currentCategory = this.originalCategory;
this.currentKey = this.originalKey;
this.currentExists = this.originalExists;
this.currentHidden = this.originalHidden;
this.lastSavedCategory = this.originalCategory;
this.lastSavedKey = this.originalKey;
this.lastSavedExists = this.originalExists;
this.lastSavedHidden = this.originalHidden;
this.state = CategoryEditor.UNCHANGED;
if ( !this.currentCategory || !this.currentCategory.length ) {
// It was a newly added category. Remove the whole editor.
this.removeEditor();
} else {
// Redisplay the link...
this.catLink.removeChild( this.catLink.firstChild );
this.catLink.appendChild( make( this.currentCategory, true ) );
this.catLink.href = wikiPagePath( HC.category_canonical + ':' + this.currentCategory );
this.catLink.title = this.currentKey || '';
this.catLink.className = this.currentExists ? '' : 'new';
this.catLink.style.backgroundColor = 'transparent';
if ( this.upDownLinks ) this.upDownLinks.style.display = this.currentExists ? '' : 'none';
checkMultiInput();
}
return evtKill( evt );
},
inactivate: function () {
if ( this.list ) this.list.style.display = 'none';
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
this.is_active = false;
},
acceptCheck: function ( dontCheck ) {
this.sanitizeInput();
var value = this.text.value.split( '|' );
var key = null;
if ( value.length > 1 ) key = value[ 1 ];
var v = value[ 0 ].replace( /_/g, ' ' ).replace( /^\s+|\s+$/g, '' );
if ( HC.capitalizePageNames ) v = capitalize( v );
this.lastInput = v;
v = replaceShortcuts( v, HC.shortcuts );
if ( !v.length ) {
this.cancel();
return false;
}
if ( !dontCheck && (
conf.wgNamespaceNumber === 14 && v === conf.wgTitle || HC.blacklist && HC.blacklist.test( v ) ) ) {
this.cancel();
return false;
}
this.currentCategory = v;
this.currentKey = key;
this.currentExists = this.inputExists;
return true;
},
accept: function ( evt ) {
// eslint-disable-next-line no-bitwise
this.noCommit = ( evtKeys( evt ) & 1 ) !== 0;
var result = evtKill( evt );
if ( this.acceptCheck() ) {
var toResolve = [ this ];
var original = this.currentCategory;
resolveMulti( toResolve, function ( resolved ) {
if ( resolved[ 0 ].dab ) {
showDab( resolved[ 0 ] );
} else {
if ( resolved[ 0 ].acceptCheck( true ) ) {
resolved[ 0 ].commit(
( resolved[ 0 ].currentCategory !== original ) ?
HC.messages.cat_resolved.replace( /\$1/g, original ) :
null );
}
}
} );
}
return result;
},
close: function () {
if ( !this.catLink ) {
// Create a catLink
this.catLink = make( 'a' );
this.catLink.appendChild( make( 'foo', true ) );
this.catLink.style.display = 'none';
this.span.insertBefore( this.catLink, this.span.firstChild.nextSibling );
}
this.catLink.removeChild( this.catLink.firstChild );
this.catLink.appendChild( make( this.currentCategory, true ) );
this.catLink.href = wikiPagePath( HC.category_canonical + ':' + this.currentCategory );
this.catLink.className = this.currentExists ? '' : 'new';
this.lastSavedCategory = this.currentCategory;
this.lastSavedKey = this.currentKey;
this.lastSavedExists = this.currentExists;
this.lastSavedHidden = this.currentHidden;
// Close form and redisplay category
this.inactivate();
this.form.style.display = 'none';
this.catLink.title = this.currentKey || '';
this.catLink.style.display = '';
if ( this.isAddCategory ) {
if ( onUpload && this.line ) {
// eslint-disable-next-line no-new
new CategoryEditor( this.line, null, this.span, true ); // Create a new one
}
this.isAddCategory = false;
this.linkSpan.parentNode.removeChild( this.linkSpan );
this.makeLinkSpan();
this.span.appendChild( this.linkSpan );
}
if ( !this.undoLink ) {
// Append an undo link.
var span = make( 'span' );
var lk = make( 'a' );
lk.href = '#catlinks';
lk.onclick = this.rollback.bind( this );
lk.appendChild( make( HC.links.undo, true ) );
lk.title = HC.tooltips.undo;
span.appendChild( make( ' ', true ) );
span.appendChild( lk );
this.normalLinks.appendChild( span );
this.undoLink = span;
if ( !onUpload ) {
try {
this.catLink.style.backgroundColor = HC.bg_changed;
} catch ( ex ) {}
}
}
if ( this.upDownLinks ) this.upDownLinks.style.display = this.lastSavedExists ? '' : 'none';
this.linkSpan.style.display = '';
this.state = CategoryEditor.CHANGED;
checkMultiInput();
forceRedraw();
},
commit: function () {
// Check again to catch problem cases after redirect resolution
if (
(
this.currentCategory === this.originalCategory &&
(
this.currentKey === this.originalKey ||
this.currentKey === null && !this.originalKey.length
)
) ||
conf.wgNamespaceNumber === 14 && this.currentCategory === conf.wgTitle ||
HC.blacklist && HC.blacklist.test( this.currentCategory )
) {
this.cancel();
return;
}
this.close();
if ( !commitButton && !onUpload ) {
var self = this;
initiateEdit( function ( failure ) {
performChanges( failure, self );
}, function ( msg ) {
alert( msg );
} );
}
},
remove: function ( evt ) {
// eslint-disable-next-line no-bitwise
this.doRemove( evtKeys( evt ) & 1 );
return evtKill( evt );
},
doRemove: function ( noCommit ) {
if ( this.isAddCategory ) { // Empty input on adding a new category
this.cancel();
return;
}
if ( !commitButton && !onUpload ) {
for ( var i = 0; i < editors.length; i++ ) {
if ( editors[ i ].state !== CategoryEditor.UNCHANGED ) {
setMultiInput();
break;
}
}
}
if ( commitButton ) {
this.catLink.title = '';
this.catLink.style.cssText += '; text-decoration : line-through !important;';
try {
this.catLink.style.backgroundColor = HC.bg_changed;
} catch ( ex ) {}
this.originalState = this.state;
this.state = CategoryEditor.DELETED;
this.normalLinks.style.display = 'none';
this.undelLink.style.display = '';
checkMultiInput();
} else {
if ( onUpload ) {
// Remove this editor completely
this.removeEditor();
} else {
this.originalState = this.state;
this.state = CategoryEditor.DELETED;
this.noCommit = noCommit || HC.del_needs_diff;
var self = this;
initiateEdit(
function ( failure ) {
performChanges( failure, self );
},
function ( msg ) {
self.state = self.originalState;
alert( msg );
} );
}
}
},
restore: function ( evt ) {
// Can occur only if we do have a commit button and are not on the upload form
this.catLink.title = this.currentKey || '';
this.catLink.style.textDecoration = '';
this.state = this.originalState;
if ( this.state === CategoryEditor.UNCHANGED ) {
this.catLink.style.backgroundColor = 'transparent';
} else {
try {
this.catLink.style.backgroundColor = HC.bg_changed;
} catch ( ex ) {}
}
this.normalLinks.style.display = '';
this.undelLink.style.display = 'none';
checkMultiInput();
return evtKill( evt );
},
// Internal operations
selectEngine: function ( engineName ) {
if ( !this.engineSelector ) return;
for ( var i = 0; i < this.engineSelector.options.length; i++ ) this.engineSelector.options[ i ].selected = this.engineSelector.options[ i ].value === engineName;
},
sanitizeInput: function () {
var v = this.text.value || '';
v = v.replace( /^(\s|_)+/, '' ); // Trim leading blanks and underscores
var re = new RegExp( '^(' + HC.category_regexp + '):' );
if ( re.test( v ) ) v = v.substring( v.indexOf( ':' ) + 1 ).replace( /^(\s|_)+/, '' );
v = v.replace(/\u200E$/, ''); // Trim ending left-to-right mark
if ( HC.capitalizePageNames ) v = capitalize( v );
// Update the input field if needed, manually restoring the cursor afterwards
// (but not the whole selection, so that further typing does not overwrite anything)
if ( this.text.value !== null && this.text.value !== v ) {
var cursorPos = this.text.selectionStart;
this.text.value = v;
this.text.setSelectionRange( cursorPos, cursorPos );
}
},
makeCall: function ( url, callbackObj, engine, queryKey, cleanKey ) {
var cb = callbackObj,
e = engine,
v = queryKey,
z = cleanKey,
thisObj = this;
function done() {
cb.callsMade++;
if ( cb.callsMade === cb.nofCalls ) {
if ( cb.exists ) cb.allTitles.exists = true;
if ( cb.normalized ) cb.allTitles.normalized = cb.normalized;
if ( !cb.dontCache && !suggestionConfigs[ cb.engineName ].cache[ z ] ) suggestionConfigs[ cb.engineName ].cache[ z ] = cb.allTitles;
thisObj.text.readOnly = false;
if ( !cb.cancelled ) thisObj.showSuggestions( cb.allTitles, cb.noCompletion, v, cb.engineName );
if ( cb === thisObj.callbackObj ) thisObj.callbackObj = null;
cb = undefined;
}
}
$.getJSON( url, function ( json ) {
var titles = e.handler( json, z );
if ( titles && titles.length ) {
if ( cb.allTitles === null ) cb.allTitles = titles; else cb.allTitles = cb.allTitles.concat( titles );
if ( titles.exists ) cb.exists = true;
if ( titles.normalized ) cb.normalized = titles.normalized;
}
done();
} ).fail( function ( req ) {
if ( !req ) noSuggestions = true;
cb.dontCache = true;
done();
} );
},
callbackObj: null,
textchange: function ( dont_autocomplete, force ) {
// Hide all other lists
makeActive( this );
// Get input value, omit sort key, if any
this.sanitizeInput();
var v = this.text.value;
// Disregard anything after a pipe.
var pipe = v.indexOf( '|' );
if ( pipe >= 0 ) {
this.currentKey = v.substring( pipe + 1 );
v = v.substring( 0, pipe );
} else {
this.currentKey = null;
}
if ( this.lastInput === v && !force ) return; // No change
if ( this.lastInput !== v ) checkMultiInput();
this.lastInput = v;
this.lastRealInput = v;
// Mark blacklisted inputs.
this.ok.disabled = v.length && HC.blacklist && HC.blacklist.test( v );
if ( noSuggestions ) {
// No Ajax: just make sure the list is hidden
if ( this.list ) this.list.style.display = 'none';
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
if ( this.icon ) this.icon.style.display = 'none';
return;
}
if ( !v.length ) {
this.showSuggestions( [] );
return;
}
var cleanKey = v.replace( /[\u200E\u200F\u202A-\u202E]/g, '' ).replace( wikiTextBlankRE, ' ' );
cleanKey = replaceShortcuts( cleanKey, HC.shortcuts );
cleanKey = cleanKey.replace( /^\s+|\s+$/g, '' );
if ( !cleanKey.length ) {
this.showSuggestions( [] );
return;
}
if ( this.callbackObj ) this.callbackObj.cancelled = true;
var engineName = suggestionConfigs[ this.engine ] ? this.engine : 'combined';
dont_autocomplete = dont_autocomplete || suggestionConfigs[ engineName ].noCompletion;
if ( suggestionConfigs[ engineName ].cache[ cleanKey ] ) {
this.showSuggestions( suggestionConfigs[ engineName ].cache[ cleanKey ], dont_autocomplete, v, engineName );
return;
}
var engines = suggestionConfigs[ engineName ].engines;
this.callbackObj = {
allTitles: null,
callsMade: 0,
nofCalls: engines.length,
noCompletion: dont_autocomplete,
engineName: engineName
};
this.makeCalls( engines, this.callbackObj, v, cleanKey );
},
makeCalls: function ( engines, cb, v, cleanKey ) {
for ( var j = 0; j < engines.length; j++ ) {
var engine = suggestionEngines[ engines[ j ] ];
var url = wgServer + conf.wgScriptPath + engine.uri.replace( /\$1/g, encodeURIComponent( cleanKey ) );
this.makeCall( url, cb, engine, v, cleanKey );
}
},
showSuggestions: function ( titles, dontAutocomplete, queryKey, engineName ) {
this.text.readOnly = false;
this.dab = null;
this.showsList = false;
if ( !this.list ) return;
if ( noSuggestions ) {
if ( this.list ) this.list.style.display = 'none';
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
if ( this.icon ) this.icon.style.display = 'none';
this.inputExists = true; // Default...
return;
}
this.engineName = engineName;
if ( engineName ) {
if ( !this.engineSelector ) this.engineName = null;
} else {
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
}
if ( queryKey ) {
if ( this.lastInput.indexOf( queryKey ) ) return;
if ( this.lastQuery && this.lastInput.indexOf( this.lastQuery ) === 0 && this.lastQuery.length > queryKey.length ) return;
}
this.lastQuery = queryKey;
// Get current input text
var v = this.text.value.split( '|' );
var key = v.length > 1 ? '|' + v[ 1 ] : '';
v = ( HC.capitalizePageNames ? capitalize( v[ 0 ] ) : v[ 0 ] );
var vNormalized = v;
var knownToExist = titles && titles.exists;
var i;
if ( titles ) {
if ( titles.normalized && v.indexOf( queryKey ) === 0 ) {
// We got back a different normalization than what is in the input field
vNormalized = titles.normalized + v.substring( queryKey.length );
}
var vLow = vNormalized.toLowerCase();
// Strip blacklisted categories
if ( HC.blacklist ) {
for ( i = 0; i < titles.length; i++ ) {
if ( HC.blacklist.test( titles[ i ] ) ) {
titles.splice( i, 1 );
i--;
}
}
}
titles.sort(
function ( a, b ) {
if ( a === b ) return 0;
if ( a.indexOf( b ) === 0 ) return 1;
// a begins with b: a > b
if ( b.indexOf( a ) === 0 ) return -1;
// b begins with a: a < b
// Opensearch may return stuff not beginning with the search prefix!
var prefixMatchA = ( a.indexOf( vNormalized ) === 0 ? 1 : 0 );
var prefixMatchB = ( b.indexOf( vNormalized ) === 0 ? 1 : 0 );
if ( prefixMatchA !== prefixMatchB ) return prefixMatchB - prefixMatchA;
// Case-insensitive prefix match!
var aLow = a.toLowerCase(),
bLow = b.toLowerCase();
prefixMatchA = ( aLow.indexOf( vLow ) === 0 ? 1 : 0 );
prefixMatchB = ( bLow.indexOf( vLow ) === 0 ? 1 : 0 );
if ( prefixMatchA !== prefixMatchB ) return prefixMatchB - prefixMatchA;
if ( a < b ) return -1;
if ( b < a ) return 1;
return 0;
} );
// Remove duplicates and self-references
for ( i = 0; i < titles.length; i++ ) {
if (
i + 1 < titles.length && titles[ i ] === titles[ i + 1 ] ||
conf.wgNamespaceNumber === 14 && titles[ i ] === conf.wgTitle
) {
titles.splice( i, 1 );
i--;
}
}
}
if ( !titles || !titles.length ) {
if ( this.list ) this.list.style.display = 'none';
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
if ( engineName && suggestionConfigs[ engineName ] && !suggestionConfigs[ engineName ].temp ) {
if ( this.icon ) this.icon.src = HC.existsNo;
this.inputExists = false;
}
return;
}
var firstTitle = titles[ 0 ];
var completed = this.autoComplete( firstTitle, v, vNormalized, key, dontAutocomplete );
var existing = completed || knownToExist || firstTitle === replaceShortcuts( v, HC.shortcuts );
if ( engineName && suggestionConfigs[ engineName ] && !suggestionConfigs[ engineName ].temp ) {
this.icon.src = ( existing ? HC.existsYes : HC.existsNo );
this.inputExists = existing;
}
if ( completed ) {
this.lastInput = firstTitle;
if ( titles.length === 1 ) {
this.list.style.display = 'none';
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
return;
}
}
// (Re-)fill the list
while ( this.list.firstChild ) this.list.removeChild( this.list.firstChild );
for ( i = 0; i < titles.length; i++ ) {
var opt = make( 'option' );
opt.appendChild( make( titles[ i ], true ) );
opt.selected = completed && ( i === 0 );
this.list.appendChild( opt );
}
this.displayList();
},
displayList: function () {
this.showsList = true;
if ( !this.is_active ) {
this.list.style.display = 'none';
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
return;
}
var nofItems = ( this.list.options.length > HC.listSize ? HC.listSize : this.list.options.length );
if ( nofItems <= 1 ) nofItems = 2;
this.list.size = nofItems;
this.list.style.align = is_rtl ? 'right' : 'left';
this.list.style.zIndex = 5;
this.list.style.position = 'absolute';
// Compute initial list position. First the height.
var anchor = is_rtl ? 'right' : 'left';
var listh = 0;
if ( this.list.style.display === 'none' ) {
// Off-screen display to get the height
this.list.style.top = this.text.offsetTop + 'px';
this.list.style[ anchor ] = '-10000px';
this.list.style.display = '';
listh = this.list.offsetHeight;
this.list.style.display = 'none';
} else {
listh = this.list.offsetHeight;
}
// Approximate calculation of maximum list size
var maxListHeight = listh;
if ( nofItems < HC.listSize ) maxListHeight = ( listh / nofItems ) * HC.listSize;
function viewport( what ) {
if ( is_webkit && !document.evaluate ) {
// Safari < 3.0
return window[ 'inner' + what ];
}
var s = 'client' + what;
if ( window.opera ) return document.body[ s ];
return ( document.documentElement ? document.documentElement[ s ] : 0 ) || document.body[ s ] || 0;
}
function scroll_offset( what ) {
var s = 'scroll' + what;
var result = ( document.documentElement ? document.documentElement[ s ] : 0 ) || document.body[ s ] || 0;
if ( is_rtl && what === 'Left' ) {
// RTL inconsistencies.
// FF: 0 at the far right, then increasingly negative values.
// IE >= 8: 0 at the far right, then increasingly positive values.
// Webkit: scrollWidth - clientWidth at the far right, then down to zero.
// Opera: don't know...
if ( result < 0 ) result = -result;
if ( !is_webkit ) result = scroll_offset( 'Width' ) - viewport( 'Width' ) - result;
// Now all have webkit behavior, i.e. zero if at the leftmost edge.
}
return result;
}
function position( node ) {
// Stripped-down simplified position function. It's good enough for our purposes.
if ( node.getBoundingClientRect ) {
var box = node.getBoundingClientRect();
return {
x: Math.round( box.left + scroll_offset( 'Left' ) ),
y: Math.round( box.top + scroll_offset( 'Top' ) )
};
}
var t = 0,
l = 0;
do {
t += ( node.offsetTop || 0 );
l += ( node.offsetLeft || 0 );
node = node.offsetParent;
} while ( node );
return {
x: l,
y: t
};
}
var textPos = position( this.text ),
nl = 0,
nt = 0,
offset = 0,
// Opera 9.5 somehow has offsetWidth = 0 here?? Use the next best value...
textBoxWidth = this.text.offsetWidth || this.text.clientWidth;
if ( this.engineName ) {
this.engineSelector.style.zIndex = 5;
this.engineSelector.style.position = 'absolute';
this.engineSelector.style.width = textBoxWidth + 'px';
// Figure out the height of this selector: display it off-screen, then hide it again.
if ( this.engineSelector.style.display === 'none' ) {
this.engineSelector.style[ anchor ] = '-10000px';
this.engineSelector.style.top = '0';
this.engineSelector.style.display = '';
offset = this.engineSelector.offsetHeight;
this.engineSelector.style.display = 'none';
} else {
offset = this.engineSelector.offsetHeight;
}
this.engineSelector.style[ anchor ] = nl + 'px';
}
if ( textPos.y < maxListHeight + offset + 1 ) {
// The list might extend beyond the upper border of the page. Let's avoid that by placing it
// below the input text field.
nt = this.text.offsetHeight + offset + 1;
if ( this.engineName ) this.engineSelector.style.top = this.text.offsetHeight + 'px';
} else {
nt = -listh - offset - 1;
if ( this.engineName ) this.engineSelector.style.top = -( offset + 1 ) + 'px';
}
this.list.style.top = nt + 'px';
this.list.style.width = ''; // No fixed width (yet)
this.list.style[ anchor ] = nl + 'px';
if ( this.engineName ) {
this.selectEngine( this.engineName );
this.engineSelector.style.display = '';
}
this.list.style.display = 'block';
// Set the width of the list
if ( this.list.offsetWidth < textBoxWidth ) {
this.list.style.width = textBoxWidth + 'px';
return;
}
// If the list is wider than the textbox: make sure it fits horizontally into the browser window
var scroll = scroll_offset( 'Left' );
var view_w = viewport( 'Width' );
var w = this.list.offsetWidth;
var l_pos = position( this.list );
var left = l_pos.x;
var right = left + w;
if ( left < scroll || right > scroll + view_w ) {
if ( w > view_w ) {
w = view_w;
this.list.style.width = w + 'px';
if ( is_rtl ) left = right - w; else right = left + w;
}
var relative_offset = 0;
if ( left < scroll ) relative_offset = scroll - left; else if ( right > scroll + view_w ) relative_offset = -( right - scroll - view_w );
if ( is_rtl ) relative_offset = -relative_offset;
if ( relative_offset ) this.list.style[ anchor ] = ( nl + relative_offset ) + 'px';
}
},
autoComplete: function ( newVal, actVal, normalizedActVal, key, dontModify ) {
if ( newVal === actVal ) return true;
if ( dontModify || this.ime || !this.canSelect() ) return false;
// If we can't select properly or an IME composition is ongoing, autocompletion would be a major annoyance to the user.
if ( newVal.indexOf( actVal ) ) {
// Maybe it'll work with the normalized value (NFC)?
if ( normalizedActVal && newVal.indexOf( normalizedActVal ) === 0 ) {
if ( this.lastRealInput === actVal ) this.lastRealInput = normalizedActVal;
actVal = normalizedActVal;
} else {
return false;
}
}
// Actual input is a prefix of the new text. Fill in new text, selecting the newly added suffix
// such that it can be easily removed by typing backspace if the suggestion is unwanted.
this.text.focus();
this.text.value = newVal + key;
this.setSelection( actVal.length, newVal.length );
return true;
},
canSelect: function () {
return this.text.setSelectionRange ||
this.text.createTextRange ||
this.text.selectionStart !== undefined &&
this.text.selectionEnd !== undefined;
},
setSelection: function ( from, to ) {
// this.text must be focused (at least on IE)
if ( !this.text.value ) return;
if ( this.text.setSelectionRange ) { // e.g. khtml
this.text.setSelectionRange( from, to );
} else if ( this.text.selectionStart !== undefined ) {
if ( from > this.text.selectionStart ) {
this.text.selectionEnd = to;
this.text.selectionStart = from;
} else {
this.text.selectionStart = from;
this.text.selectionEnd = to;
}
} else if ( this.text.createTextRange ) { // IE
var new_selection = this.text.createTextRange();
new_selection.move( 'character', from );
new_selection.moveEnd( 'character', to - from );
new_selection.select();
}
},
getSelection: function () {
var from = 0,
to = 0;
// this.text must be focused (at least on IE)
if ( !this.text.value ) {
// No text.
} else if ( this.text.selectionStart !== undefined ) {
from = this.text.selectionStart;
to = this.text.selectionEnd;
} else if ( document.selection && document.selection.createRange ) { // IE
var rng = document.selection.createRange().duplicate();
if ( rng.parentElement() === this.text ) {
try {
var textRng = this.text.createTextRange();
textRng.move( 'character', 0 );
textRng.setEndPoint( 'EndToEnd', rng );
// We're in a single-line input box: no need to care about IE's strange
// handling of line ends
to = textRng.text.length;
textRng.setEndPoint( 'EndToStart', rng );
from = textRng.text.length;
} catch ( notFocused ) {
from = this.text.value.length;
to = from; // At end of text
}
}
}
return {
start: from,
end: to
};
},
saveView: function () {
this.lastSelection = this.getSelection();
},
processKey: function ( evt ) {
var dir = 0;
switch ( this.lastKey ) {
case UP:
dir = -1;
break;
case DOWN:
dir = 1;
break;
case PGUP:
dir = -HC.listSize;
break;
case PGDOWN:
dir = HC.listSize;
break;
case ESC: // Inhibit default behavior (revert to last real input in FF: we do that ourselves)
return evtKill( evt );
}
if ( dir ) {
if ( this.list.style.display !== 'none' ) {
// List is visible, so there are suggestions
this.highlightSuggestion( dir );
// Kill the event, otherwise some browsers (e.g., Firefox) may additionally treat an up-arrow
// as "place the text cursor at the front", which we don't want here.
return evtKill( evt );
} else if (
this.keyCount <= 1 &&
( !this.callbackObj || this.callbackObj.callsMade === this.callbackObj.nofCalls )
) {
// If no suggestions displayed, get them, unless we're already getting them.
this.textchange();
}
}
return true;
},
highlightSuggestion: function ( dir ) {
if ( noSuggestions || !this.list || this.list.style.display === 'none' ) return false;
var curr = this.list.selectedIndex;
var tgt = -1;
if ( dir === 0 ) {
if ( curr < 0 || curr >= this.list.options.length ) return false;
tgt = curr;
} else {
tgt = curr < 0 ? 0 : curr + dir;
tgt = tgt < 0 ? 0 : tgt;
if ( tgt >= this.list.options.length ) tgt = this.list.options.length - 1;
}
if ( tgt !== curr || dir === 0 ) {
if ( curr >= 0 && curr < this.list.options.length && dir !== 0 ) this.list.options[ curr ].selected = false;
this.list.options[ tgt ].selected = true;
// Get current input text
var v = this.text.value.split( '|' );
var key = v.length > 1 ? '|' + v[ 1 ] : '';
var completed = this.autoComplete( this.list.options[ tgt ].text, this.lastRealInput, null, key, false );
if ( !completed || this.list.options[ tgt ].text === this.lastRealInput ) {
this.text.value = this.list.options[ tgt ].text + key;
if ( this.canSelect() ) this.setSelection( this.list.options[ tgt ].text.length, this.list.options[ tgt ].text.length );
}
this.lastInput = this.list.options[ tgt ].text;
this.inputExists = true; // Might be wrong if from a dab list...
if ( this.icon ) this.icon.src = HC.existsYes;
this.state = CategoryEditor.CHANGE_PENDING;
}
return true;
},
resetKeySelection: function () {
if ( noSuggestions || !this.list || this.list.style.display === 'none' ) return false;
var curr = this.list.selectedIndex;
if ( curr >= 0 && curr < this.list.options.length ) {
this.list.options[ curr ].selected = false;
// Get current input text
var v = this.text.value.split( '|' );
var key = v.length > 1 ? '|' + v[ 1 ] : '';
// ESC is handled strangely by some browsers (e.g., FF); somehow it resets the input value before
// our event handlers ever get a chance to run.
var result = v[ 0 ] !== this.lastInput;
if ( v[ 0 ] !== this.lastRealInput ) {
this.text.value = this.lastRealInput + key;
result = true;
}
this.lastInput = this.lastRealInput;
return result;
}
return false;
}
}; // end CategoryEditor.prototype
function initialize() {
// User configurations: Do this here, called from the onload handler, so that users can
// override it easily in their own user script files by just declaring variables. JSconfig
// is some feature used at Wikimedia Commons.
var config = ( window.JSconfig !== undefined && JSconfig.keys ) ? JSconfig.keys : {};
HC.dont_add_to_watchlist = ( window.hotcat_dont_add_to_watchlist !== undefined ?
!!window.hotcat_dont_add_to_watchlist :
( config.HotCatDontAddToWatchlist !== undefined ? config.HotCatDontAddToWatchlist :
HC.dont_add_to_watchlist ) );
HC.no_autocommit = ( window.hotcat_no_autocommit !== undefined ?
!!window.hotcat_no_autocommit : ( config.HotCatNoAutoCommit !== undefined ?
config.HotCatNoAutoCommit :
// On talk namespace default autocommit off
( conf.wgNamespaceNumber % 2 ?
true : HC.no_autocommit ) ) );
HC.del_needs_diff = ( window.hotcat_del_needs_diff !== undefined ?
!!window.hotcat_del_needs_diff :
( config.HotCatDelNeedsDiff !== undefined ?
config.HotCatDelNeedsDiff :
HC.del_needs_diff ) );
HC.suggest_delay = window.hotcat_suggestion_delay || config.HotCatSuggestionDelay || HC.suggest_delay;
HC.editbox_width = window.hotcat_editbox_width || config.HotCatEditBoxWidth || HC.editbox_width;
HC.suggestions = window.hotcat_suggestions || config.HotCatSuggestions || HC.suggestions;
if ( typeof HC.suggestions !== 'string' || !suggestionConfigs[ HC.suggestions ] ) HC.suggestions = 'combined';
HC.fixed_search = ( window.hotcat_suggestions_fixed !== undefined ?
!!window.hotcat_suggestions_fixed : ( config.HotCatFixedSuggestions !== undefined ?
config.HotCatFixedSuggestions : HC.fixed_search ) );
HC.single_minor = ( window.hotcat_single_changes_are_minor !== undefined ?
!!window.hotcat_single_changes_are_minor :
( config.HotCatMinorSingleChanges !== undefined ?
config.HotCatMinorSingleChanges :
HC.single_minor ) );
HC.bg_changed = window.hotcat_changed_background || config.HotCatChangedBackground || HC.bg_changed;
HC.use_up_down = ( window.hotcat_use_category_links !== undefined ?
!!window.hotcat_use_category_links :
( config.HotCatUseCategoryLinks !== undefined ?
config.HotCatUseCategoryLinks :
HC.use_up_down ) );
HC.listSize = window.hotcat_list_size || config.HotCatListSize || HC.listSize;
if ( conf.wgDBname !== 'commonswiki' ) HC.changeTag = config.HotCatChangeTag || '';
// The next whole shebang is needed, because manual tags get not submitted except of save
if ( HC.changeTag ) {
var eForm = document.editform,
catRegExp = new RegExp( '^\\[\\[(' + HC.category_regexp + '):' ),
oldTxt;
// Returns true if minor change
var isMinorChange = function () {
var newTxt = eForm.wpTextbox1;
if ( !newTxt ) return;
newTxt = newTxt.value;
var oldLines = oldTxt.match( /^.*$/gm ),
newLines = newTxt.match( /^.*$/gm ),
cArr; // changes
var except = function ( aArr, bArr ) {
var result = [],
lArr, // larger
sArr; // smaller
if ( aArr.length < bArr.length ) {
lArr = bArr;
sArr = aArr;
} else {
lArr = aArr;
sArr = bArr;
}
for ( var i = 0; i < lArr.length; i++ ) {
var item = lArr[ i ];
var ind = $.inArray( item, sArr );
if ( ind === -1 ) result.push( item );
else sArr.splice( ind, 1 ); // don't check this item again
}
return result.concat( sArr );
};
cArr = except( oldLines, newLines );
if ( cArr.length ) {
cArr = $.grep( cArr, function ( c ) {
c = $.trim( c );
return ( c && !catRegExp.test( c ) );
} );
}
if ( !cArr.length ) {
oldTxt = newTxt;
return true;
}
};
if ( conf.wgAction === 'submit' && conf.wgArticleId && eForm && eForm.wpSummary && document.getElementById( 'wikiDiff' ) ) {
var sum = eForm.wpSummary,
sumA = eForm.wpAutoSummary;
if ( sum.value && sumA.value === HC.changeTag ) { // HotCat diff
// MD5 hash of the empty string, as HotCat edit is based on empty sum
sumA.value = sumA.value.replace( HC.changeTag, 'd41d8cd98f00b204e9800998ecf8427e' );
// Attr creation and event handling is not same in all (old) browsers so use $
var $ct = $( '<input type="hidden" name="wpChangeTags">' ).val( HC.changeTag );
$( eForm ).append( $ct );
oldTxt = eForm.wpTextbox1.value;
$( '#wpSave' ).one( 'click', function () {
if ( $ct.val() )
sum.value = sum.value.replace( ( HC.messages.using || HC.messages.prefix ), '' );
} );
var removeChangeTag = function () {
$( eForm.wpTextbox1 ).add( sum ).one( 'input', function () {
window.setTimeout( function () {
if ( !isMinorChange() ) $ct.val( '' );
else removeChangeTag();
}, 500 );
} );
};
removeChangeTag();
}
}
}
// Numeric input, make sure we have a numeric value
HC.listSize = parseInt( HC.listSize, 10 );
if ( isNaN( HC.listSize ) || HC.listSize < 5 ) HC.listSize = 5;
HC.listSize = Math.min( HC.listSize, 30 ); // Max size
// Localize search engine names
if ( HC.engine_names ) {
for ( var key in HC.engine_names )
if ( suggestionConfigs[ key ] && HC.engine_names[ key ] ) suggestionConfigs[ key ].name = HC.engine_names[ key ];
}
// Catch both native RTL and "faked" RTL through [[MediaWiki:Rtl.js]]
is_rtl = hasClass( document.body, 'rtl' );
if ( !is_rtl ) {
if ( document.defaultView && document.defaultView.getComputedStyle ) { // Gecko etc.
is_rtl = document.defaultView.getComputedStyle( document.body, null ).getPropertyValue( 'direction' );
} else if ( document.body.currentStyle ) { // IE, has subtle differences to getComputedStyle
is_rtl = document.body.currentStyle.direction;
} else { // Not exactly right, but best effort
is_rtl = document.body.style.direction;
}
is_rtl = ( is_rtl === 'rtl' );
}
}
function can_edit() {
var container = null;
switch ( mw.config.get( 'skin' ) ) {
case 'cologneblue':
container = document.getElementById( 'quickbar' );
/* fall through */
case 'standard':
case 'nostalgia':
if ( !container ) container = document.getElementById( 'topbar' );
var lks = container.getElementsByTagName( 'a' );
for ( var i = 0; i < lks.length; i++ ) {
if (
param( 'title', lks[ i ].href ) === conf.wgPageName &&
param( 'action', lks[ i ].href ) === 'edit'
) {
return true;
}
}
return false;
default:
// all modern skins:
return document.getElementById( 'ca-edit' ) !== null;
}
}
// Legacy stuff
function closeForm() {
// Close all open editors without redirect resolution and other asynchronous stuff.
for ( var i = 0; i < editors.length; i++ ) {
var edit = editors[ i ];
if ( edit.state === CategoryEditor.OPEN ) {
edit.cancel();
} else if ( edit.state === CategoryEditor.CHANGE_PENDING ) {
edit.sanitizeInput();
var value = edit.text.value.split( '|' );
var key = null;
if ( value.length > 1 ) key = value[ 1 ];
var v = value[ 0 ].replace( /_/g, ' ' ).replace( /^\s+|\s+$/g, '' );
if ( !v.length ) {
edit.cancel();
} else {
edit.currentCategory = v;
edit.currentKey = key;
edit.currentExists = this.inputExists;
edit.close();
}
}
}
}
function setup_upload() {
onUpload = true;
// Add an empty category bar at the end of the table containing the description, and change the onsubmit handler.
var ip = document.getElementById( 'mw-htmlform-description' ) || document.getElementById( 'wpDestFile' );
if ( !ip ) {
ip = document.getElementById( 'wpDestFile' );
while ( ip && ip.nodeName.toLowerCase() !== 'table' ) ip = ip.parentNode;
}
if ( !ip ) return;
var reupload = document.getElementById( 'wpForReUpload' );
var destFile = document.getElementById( 'wpDestFile' );
if (
( reupload && !!reupload.value ) ||
( destFile && ( destFile.disabled || destFile.readOnly ) )
) {
return; // re-upload form...
}
// Insert a table row with two fields (label and empty category bar)
var labelCell = make( 'td' );
var lineCell = make( 'td' );
// Create the category line
catLine = make( 'div' );
catLine.className = 'catlinks';
catLine.id = 'catlinks';
catLine.style.textAlign = is_rtl ? 'right' : 'left';
// We'll be inside a table row. Make sure that we don't have margins or strange borders.
catLine.style.margin = '0';
catLine.style.border = 'none';
lineCell.appendChild( catLine );
// Create the label
var label = null;
if ( window.UFUI && window.UIElements && UFUI.getLabel instanceof Function ) {
try {
label = UFUI.getLabel( 'wpCategoriesUploadLbl' );
} catch ( ex ) {
label = null;
}
}
if ( !label ) {
labelCell.id = 'hotcatLabel';
labelCell.appendChild( make( HC.categories, true ) );
} else {
labelCell.id = 'hotcatLabelTranslated';
labelCell.appendChild( label );
}
labelCell.className = 'mw-label';
labelCell.style.textAlign = 'right';
labelCell.style.verticalAlign = 'middle';
// Change the onsubmit handler
var form = document.getElementById( 'upload' ) || document.getElementById( 'mw-upload-form' );
if ( form && ip && ip.insertRow ) {
var newRow = ip.insertRow( -1 );
newRow.appendChild( labelCell );
newRow.appendChild( lineCell );
form.onsubmit = ( function ( oldSubmit ) {
return function () {
var do_submit = true;
if ( oldSubmit ) {
if ( typeof oldSubmit === 'string' ) {
// eslint-disable-next-line no-eval
do_submit = eval( oldSubmit );
} else if ( oldSubmit instanceof Function ) {
do_submit = oldSubmit.apply( form, arguments );
}
}
if ( !do_submit ) return false;
closeForm();
// Copy the categories
var eb = document.getElementById( 'wpUploadDescription' ) || document.getElementById( 'wpDesc' );
var addedOne = false;
for ( var i = 0; i < editors.length; i++ ) {
var t = editors[ i ].currentCategory;
if ( !t ) continue;
var key = editors[ i ].currentKey;
var new_cat = '[[' + HC.category_canonical + ':' + t + ( key ? '|' + key : '' ) + ']]';
// Only add if not already present
var cleanedText = eb.value
.replace( /<!--(\s|\S)*?-->/g, '' )
.replace( /<nowiki>(\s|\S)*?<\/nowiki>/g, '' );
if ( !find_category( cleanedText, t, true ) ) {
eb.value += '\n' + new_cat;
addedOne = true;
}
}
if ( addedOne ) {
// Remove "subst:unc" added by Flinfo if it didn't find categories
eb.value = eb.value.replace( /\{\{subst:unc\}\}/g, '' );
}
return true;
};
}( form.onsubmit ) );
}
}
var cleanedText = null;
function isOnPage( span ) {
if ( span.firstChild.nodeType !== Node.ELEMENT_NODE ) return null;
var catTitle = title( span.firstChild.getAttribute( 'href' ) );
if ( !catTitle ) return null;
catTitle = catTitle.substr( catTitle.indexOf( ':' ) + 1 ).replace( /_/g, ' ' );
if ( HC.blacklist && HC.blacklist.test( catTitle ) ) return null;
var result = {
title: catTitle,
match: [ '', '', '' ]
};
if ( pageText === null ) return result;
if ( cleanedText === null ) {
cleanedText = pageText
.replace( /<!--(\s|\S)*?-->/g, '' )
.replace( /<nowiki>(\s|\S)*?<\/nowiki>/g, '' );
}
result.match = find_category( cleanedText, catTitle, true );
return result;
}
var initialized = false;
var setupTimeout = null;
function findByClass( scope, tag, className ) {
var result = $( scope ).find( tag + '.' + className );
return ( result && result.length ) ? result[ 0 ] : null;
}
function errorAMC() {
alert( 'An error occurred. Unable to setup HotCat' );
}
function enableAMC() {
var api = new mw.Api();
return api.saveOption( 'mf_amc_optin', '1' ).then( function ( r ) {
if ( !r || r.options !== 'success' ) {
errorAMC();
return;
}
if ( window.confirm( 'Please reload your page to use hotcat.' ) ) {
location.reload();
}
}, function () {
errorAMC();
} );
}
function showWarning( text ) {
var warning = document.createElement( 'div' );
warning.setAttribute( 'style', 'padding: 20px; background: var(--wikt-palette-amber-0,orange); color: var(--wikt-palette-black,#333); font-weight: bold; margin-top: 20px;' );
warning.textContent = text;
var btn = document.createElement( 'button' );
btn.classList.add( 'mw-ui-button', 'cdx-button' );
btn.style.display = 'block';
btn.textContent = 'Enable HotCat and AMC mode on this page';
btn.addEventListener( 'click', function () {
enableAMC();
} );
warning.appendChild( btn );
document.getElementById( 'mw-content-text' ).appendChild( warning );
}
function setup( additionalWork ) {
if ( initialized ) return;
initialized = true;
if ( setupTimeout ) {
window.clearTimeout( setupTimeout );
setupTimeout = null;
}
// Find the category bar, or create an empty one if there isn't one. Then add -/+- links after
// each category, and add the + link.
catLine =
// Special:Upload
catLine ||
document.getElementById( 'mw-normal-catlinks' );
var hiddenCats = document.getElementById( 'mw-hidden-catlinks' );
if ( !catLine ) {
// Workaround for T24660
if ( mw.config.get('skin') === 'minerva' ) {
if ( document.body.classList.contains('mw-mf-amc-disabled') ) {
showWarning( 'HotCat requires AMC mode.' );
}
}
var footer = null;
if ( !hiddenCats ) {
footer = findByClass( document, 'div', 'printfooter' );
if ( !footer ) return; // Don't know where to insert the category line
}
catLine = make( 'div' );
catLine.id = 'mw-normal-catlinks';
catLine.style.textAlign = is_rtl ? 'right' : 'left';
// Add a label
var label = make( 'a' );
label.href = conf.wgArticlePath.replace( '$1', 'Special:Categories' );
label.title = HC.categories;
label.appendChild( make( HC.categories, true ) );
catLine.appendChild( label );
catLine.appendChild( make( ':', true ) );
// Insert the new category line
var container = ( hiddenCats ? hiddenCats.parentNode : document.getElementById( 'catlinks' ) );
if ( !container ) {
container = make( 'div' );
container.id = 'catlinks';
footer.parentNode.insertBefore( container, footer.nextSibling );
}
container.className = 'catlinks noprint';
container.style.display = '';
if ( !hiddenCats ) container.appendChild( catLine ); else container.insertBefore( catLine, hiddenCats );
} // end if catLine exists
if ( is_rtl ) catLine.dir = 'rtl';
// Create editors for all existing categories
function createEditors( line, is_hidden ) {
var i;
var cats = line.getElementsByTagName( 'li' );
if ( cats.length ) {
newDOM = true;
line = cats[ 0 ].parentNode;
} else {
cats = line.getElementsByTagName( 'span' );
}
// Copy cats, otherwise it'll also magically contain our added spans as it is a live collection!
var copyCats = new Array( cats.length );
for ( i = 0; i < cats.length; i++ ) copyCats[ i ] = cats[ i ];
for ( i = 0; i < copyCats.length; i++ ) {
var test = isOnPage( copyCats[ i ] );
if ( test !== null && test.match !== null && line ) {
// eslint-disable-next-line no-new
new CategoryEditor( line, copyCats[ i ], test.title, test.match[ 2 ], is_hidden );
}
}
return copyCats.length ? copyCats[ copyCats.length - 1 ] : null;
}
var lastSpan = createEditors( catLine, false );
// Create one to add a new category
// eslint-disable-next-line no-new
new CategoryEditor( newDOM ? catLine.getElementsByTagName( 'ul' )[ 0 ] : catLine, null, null, lastSpan !== null, false );
if ( !onUpload ) {
if ( pageText !== null && hiddenCats ) {
if ( is_rtl ) hiddenCats.dir = 'rtl';
createEditors( hiddenCats, true );
}
// And finally add the "multi-mode" span. (Do this at the end, otherwise it ends up in the list above.)
var enableMulti = make( 'span' );
enableMulti.className = 'noprint';
if ( is_rtl ) enableMulti.dir = 'rtl';
catLine.insertBefore( enableMulti, catLine.firstChild.nextSibling );
enableMulti.appendChild( make( '\xa0', true ) ); // nbsp
multiSpan = make( 'span' );
enableMulti.appendChild( multiSpan );
multiSpan.innerHTML = '(<a>' + HC.addmulti + '</a>)';
var lk = multiSpan.getElementsByTagName( 'a' )[ 0 ];
lk.onclick = function ( evt ) {
setMultiInput();
checkMultiInput();
return evtKill( evt );
};
lk.title = HC.multi_tooltip;
lk.style.cursor = 'pointer';
}
cleanedText = null;
if ( additionalWork instanceof Function ) additionalWork();
mw.hook( 'hotcat.ready' ).fire(); // Execute registered callback functions
$( 'body' ).trigger( 'hotcatSetupCompleted' );
}
function createCommitForm() {
if ( commitForm ) return;
var formContainer = make( 'div' );
formContainer.style.display = 'none';
document.body.appendChild( formContainer );
formContainer.innerHTML =
'<form id="hotcatCommitForm" method="post" enctype="multipart/form-data" action="' +
conf.wgScript + '?title=' + encodeURIComponent( conf.wgPageName ) + '&action=submit">' +
'<input type="hidden" name="wpTextbox1">' +
'<input type="hidden" name="model" value="' + conf.wgPageContentModel + '">' +
'<input type="hidden" name="format" value="text/x-wiki">' +
'<input type="hidden" name="wpSummary" value="">' +
'<input type="checkbox" name="wpMinoredit" value="1">' +
'<input type="checkbox" name="wpWatchthis" value="1">' +
'<input type="hidden" name="wpAutoSummary" value="d41d8cd98f00b204e9800998ecf8427e">' +
'<input type="hidden" name="wpEdittime">' +
'<input type="hidden" name="wpStarttime">' +
'<input type="hidden" name="wpDiff" value="wpDiff">' +
'<input type="hidden" name="oldid" value="0">' +
'<input type="hidden" name="wpIgnoreBlankSummary" value="1">' +
'<input type="submit" name="hcCommit" value="hcCommit">' +
'<input type="hidden" name="wpEditToken">' +
'<input type="hidden" name="wpUltimateParam" value="1">' +
'<input type="hidden" name="wpChangeTags">' +
'<input type="hidden" value="ℳ𝒲♥𝓊𝓃𝒾𝒸ℴ𝒹ℯ" name="wpUnicodeCheck">' +
'</form>';
commitForm = document.getElementById( 'hotcatCommitForm' );
}
function getPage() {
// We know we have an article here.
if ( !conf.wgArticleId ) {
// Doesn't exist yet. Disable on non-existing User pages -- might be a global user page.
if ( conf.wgNamespaceNumber === 2 ) return;
pageText = '';
pageTime = null;
setup( createCommitForm );
} else {
var url = wgServer + conf.wgScriptPath + '/api.php?format=json&callback=HotCat.start&action=query&rawcontinue=&titles=' +
encodeURIComponent( conf.wgPageName ) +
'&prop=info%7Crevisions&rvprop=content%7Ctimestamp%7Cids&meta=siteinfo&rvlimit=1&rvstartid=' +
conf.wgCurRevisionId;
var s = make( 'script' );
s.src = url;
HC.start = function ( json ) {
setPage( json );
setup( createCommitForm );
};
document.getElementsByTagName( 'head' )[ 0 ].appendChild( s );
setupTimeout = window.setTimeout( function () {
setup( createCommitForm );
}, 4000 ); // 4 sec, just in case getting the wikitext takes longer.
}
}
function setState( state ) {
var cats = state.split( '\n' );
if ( !cats.length ) return null;
if ( initialized && editors.length === 1 && editors[ 0 ].isAddCategory ) {
// Insert new spans and create new editors for them.
var newSpans = [];
var before = editors.length === 1 ? editors[ 0 ].span : null;
var i;
for ( i = 0; i < cats.length; i++ ) {
if ( !cats[ i ].length ) continue;
var cat = cats[ i ].split( '|' );
var key = cat.length > 1 ? cat[ 1 ] : null;
cat = cat[ 0 ];
var lk = make( 'a' );
lk.href = wikiPagePath( HC.category_canonical + ':' + cat );
lk.appendChild( make( cat, true ) );
lk.title = cat;
var span = make( 'span' );
span.appendChild( lk );
if ( !i ) catLine.insertBefore( make( ' ', true ), before );
catLine.insertBefore( span, before );
if ( before && i + 1 < cats.length ) parent.insertBefore( make( ' | ', true ), before );
newSpans.push( {
element: span,
title: cat,
key: key
} );
}
// And change the last one...
if ( before ) before.parentNode.insertBefore( make( ' | ', true ), before );
for ( i = 0; i < newSpans.length; i++ ) {
// eslint-disable-next-line no-new
new CategoryEditor( catLine, newSpans[ i ].element, newSpans[ i ].title, newSpans[ i ].key );
}
}
return null;
}
function getState() {
var result = null;
for ( var i = 0; i < editors.length; i++ ) {
var text = editors[ i ].currentCategory;
var key = editors[ i ].currentKey;
if ( text && text.length ) {
if ( key !== null ) text += '|' + key;
if ( result === null ) result = text; else result += '\n' + text;
}
}
return result;
}
function really_run() {
en_wiktionary_get_langdata(function() {
initialize();
if ( !HC.upload_disabled && conf.wgNamespaceNumber === -1 && conf.wgCanonicalSpecialPageName === 'Upload' && conf.wgUserName ) {
setup_upload();
setup( function () {
// Check for state restoration once the setup is done otherwise, but before signalling setup completion
if ( window.UploadForm && UploadForm.previous_hotcat_state ) UploadForm.previous_hotcat_state = setState( UploadForm.previous_hotcat_state );
} );
} else {
if ( !conf.wgIsArticle || conf.wgAction !== 'view' || param( 'diff' ) !== null || param( 'oldid' ) !== null || !can_edit() || HC.disable() ) return;
getPage();
}
});
}
function run() {
if ( HC.started ) return;
HC.started = true;
loadTrigger.register( really_run );
}
// Export legacy functions
window.hotcat_get_state = function () {
return getState();
};
window.hotcat_set_state = function ( state ) {
return setState( state );
};
window.hotcat_close_form = function () {
closeForm();
};
HC.runWhenReady = function ( callback ) {
// run user-registered code once HotCat is fully set up and ready.
mw.hook( 'hotcat.ready' ).add( callback );
};
// Make sure we don't get conflicts with AjaxCategories (core development that should one day
// replace HotCat).
mw.config.set( 'disableAJAXCategories', true );
// Run as soon as possible. This varies depending on MediaWiki version;
// window's 'load' event is always safe, but usually we can do better than that.
if ( conf.wgCanonicalSpecialPageName !== 'Upload' ) {
// Reload HotCat after (VE) edits (bug T103285)
mw.hook( 'postEdit' ).add( function () {
// Reset HotCat in case this is a soft reload (e.g. VisualEditor edit), unless the categories
// were not re-rendered and our interface is still there (e.g. DiscussionTools edit)
if ( document.querySelector( '#catlinks .hotcatlink' ) ) {
return;
}
catLine = null;
editors = [];
initialized = false;
HC.started = false;
run();
} );
}
// We can safely trigger just after user configuration is loaded.
// Use always() instead of then() to also start HotCat if the user module has problems.
$.when( mw.loader.using( 'user' ), $.ready ).always( run );
}( jQuery, mediaWiki ) );
// </nowiki>
qp2yp9147ad7pkve4h3sqrco2yhn5th
MediaWiki:Gadget-FastRevert.js
8
18004
89011
2021-12-14T04:07:21Z
en>Erutuon
0
add revert link next to bottom edit
89011
javascript
text/javascript
/**
************* FastRevert *******************
* Allows one to restore an old version *
* *
* Author: Quentinv57, for Wikimedia *
* -- 06 Feb. 2010 -- *
********************************************
*/
$( document ).ready( function () {
if (mw.util.getParamValue('action') === 'history') {
var chemin = '//en.wiktionary.org/w/index.php?action=edit&retablir';
var pageHistory = document.getElementById('pagehistory');
pageHistory = pageHistory && pageHistory.getElementsByTagName('li');
var prevUser;
for (var i = 0; i < pageHistory.length; i++) {
var li = pageHistory[i];
var oldid = li.dataset.mwRevid; // data-mw-revid="..."
var userLink = li.querySelector(".history-user .mw-userlink");
// Get inside of bdi tag if it's present.
while (userLink.firstElementChild) {
userLink = userLink.firstElementChild;
}
var username = userLink.textContent;
if (i !== 0) {
var revertLink = document.createElement( "a" );
revertLink.href = chemin + '&oldid=' + oldid + '&user=' + username + '&user2=' + prevUser;
revertLink.appendChild( document.createTextNode( "FastRevert" ) );
li.appendChild( document.createTextNode( " \u2014 (" ) );
li.appendChild( revertLink );
li.appendChild( document.createTextNode( ")" ) );
}
prevUser = username;
}
} else if (location.href.match(/&retablir&/)) {
var user2 = mw.util.getParamValue('user2');
var message = prompt(
'What message do you want to leave?',
'Reverted edits by [[Special:Contributions/' + user2 + '|' + user2 + ']]'
);
var user = mw.util.getParamValue('user');
document.getElementById('wpSummary').value = (message ? message + '; ' : '')
+ 'Restore to version ' + mw.util.getParamValue('oldid')
+ ' by [[Special:Contributions/' + user + '|' + user + ']]';
if (message != null) document.getElementById('editform').submit();
}
});
4v10c32kaz8zwq5wmz28zdpaglqxslq
89012
89011
2026-08-06T23:33:33Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-FastRevert.js]]
89011
javascript
text/javascript
/**
************* FastRevert *******************
* Allows one to restore an old version *
* *
* Author: Quentinv57, for Wikimedia *
* -- 06 Feb. 2010 -- *
********************************************
*/
$( document ).ready( function () {
if (mw.util.getParamValue('action') === 'history') {
var chemin = '//en.wiktionary.org/w/index.php?action=edit&retablir';
var pageHistory = document.getElementById('pagehistory');
pageHistory = pageHistory && pageHistory.getElementsByTagName('li');
var prevUser;
for (var i = 0; i < pageHistory.length; i++) {
var li = pageHistory[i];
var oldid = li.dataset.mwRevid; // data-mw-revid="..."
var userLink = li.querySelector(".history-user .mw-userlink");
// Get inside of bdi tag if it's present.
while (userLink.firstElementChild) {
userLink = userLink.firstElementChild;
}
var username = userLink.textContent;
if (i !== 0) {
var revertLink = document.createElement( "a" );
revertLink.href = chemin + '&oldid=' + oldid + '&user=' + username + '&user2=' + prevUser;
revertLink.appendChild( document.createTextNode( "FastRevert" ) );
li.appendChild( document.createTextNode( " \u2014 (" ) );
li.appendChild( revertLink );
li.appendChild( document.createTextNode( ")" ) );
}
prevUser = username;
}
} else if (location.href.match(/&retablir&/)) {
var user2 = mw.util.getParamValue('user2');
var message = prompt(
'What message do you want to leave?',
'Reverted edits by [[Special:Contributions/' + user2 + '|' + user2 + ']]'
);
var user = mw.util.getParamValue('user');
document.getElementById('wpSummary').value = (message ? message + '; ' : '')
+ 'Restore to version ' + mw.util.getParamValue('oldid')
+ ' by [[Special:Contributions/' + user + '|' + user + ']]';
if (message != null) document.getElementById('editform').submit();
}
});
4v10c32kaz8zwq5wmz28zdpaglqxslq
MediaWiki:Gadget-ChangesByLanguage.js
8
18005
89013
2024-01-17T04:58:37Z
en>This, that and the other
0
make the gadget aware of the "new" recent changes
89013
javascript
text/javascript
( [ "Recentchanges", "Watchlist", "Recentchangeslinked" ] ).indexOf( mw.config.get( "wgCanonicalSpecialPageName" ) ) !== -1 && $( function () {
var diffLinksSelector = ".mw-changeslist .special li > .mw-changeslist-line-inner > a:first-child, .mw-enhanced-rc .mw-title + a, .mw-enhanced-rc-nested a[tabindex] + a";
var getRevisions = ( function () {
var revRequests = {}, pRequests = {};
function sendRequests( requests, isPageid ) {
var pendingReqs = [];
$.each( requests, function ( i, req ) {
if ( !req.status ) {
pendingReqs.push( i );
req.status = 1;
}
} );
for ( var i = 0; i < pendingReqs.length; i += 50 ) {
var query = {
action: 'query',
prop: 'revisions',
rvprop: 'content|ids',
maxage: 60 * 60 * 12,
smaxage: 60 * 60 * 12
};
query[ isPageid ? 'pageids' : 'revids' ] = pendingReqs.slice( i, i + 50 ).join( '|' );
( new mw.Api() ).get( query ).done( function ( results ) {
$.each( results.query.pages, function ( id, revision ) {
$.each( revision.revisions, function ( i, rev ) {
var rid = isPageid ? id : rev.revid,
result = requests[ rid ].result =
rev[ '*' ];
requests[ rid ].status = 2;
$.each( requests[ rid ], function ( i, callback ) {
callback( result );
} );
} );
} );
} );
}
}
return function getRevisions( id, callback, isPageid ) {
if ( id ) {
var requests = isPageid ? pRequests : revRequests,
req = requests[ id ] || ( requests[ id ] = [] );
if ( callback ) {
if ( !req.status || req.status === 1 ) {
req.push( callback );
} else {
callback( req.result );
}
}
} else {
sendRequests( pRequests, true );
sendRequests( revRequests, false );
}
return getRevisions;
};
} )();
function getSection( content, language ) {
var start = content.indexOf( '==' + language + '==' ),
end = content.substr( start ).search( /$|\n==[^=]/ );
if ( start === -1 ) {
return false;
} else {
return content
.substr( start, end )
// Don't count a new section being added below as a change.
.replace( /[\n \-]+$/g, '' )
// Filter out translations sections.
.replace( /(\{\{(?:check)?trans-top[^\}]*\}\})[\s\S]*?(?=\{\{(?:check)?trans-bottom\}\})/g, '$1' );
}
}
function getTranslations( content, language ) {
var t = content
.match( new RegExp( '\n[\\*\\:]+\\s*' + language + ': [^\n]+', 'g' ) );
return t ? t.join( '\n' ) : '';
}
function getLanguage() {
return languageData
.split( /\s*[,|]\s*/g)
// Remove empty strings
.filter( function ( x ) { return x; } );
}
function hideFiltered() {
if ( !languages || !languages.length ) {
return;
}
$( diffLinksSelector ).each( function( i, diffLink ) {
var href = diffLink.href,
oldid = mw.util.getParamValue( "oldid", href ),
curid = mw.util.getParamValue( "diff", href ),
title = mw.util.getParamValue( "title", href );
if ( title && !title.includes( ":" ) && oldid && curid ) {
getRevisions( curid );
getRevisions( oldid, function ( oldRevision ) {
getRevisions( curid, function ( curRevision ) {
var show = languages.some( function ( language ) {
var oldSection = getSection( oldRevision, language ),
newSection = getSection( curRevision, language );
if ( oldSection !== newSection ) {
return true;
} else if ( getTranslations( oldRevision, language ) !== getTranslations( curRevision, language ) ) {
return true;
}
} );
if ( !show ) {
diffLink.parentNode.parentNode.style.display = 'none';
}
} );
} );
}
} );
// Newly created pages, without diff links.
$( '.newpage' ).prev().prev().each( function ( i, histLink ) {
var href = histLink.href,
curid = mw.util.getParamValue( "curid", href );
getRevisions( curid, function ( curRevision ) {
var show = languages.some( function ( language ) {
if ( getSection( curRevision, language ) || getTranslations( curRevision, language ) ) {
return true;
}
} );
if ( !show ) {
histLink.parentNode.parentNode.style.display = 'none';
}
}, true );
} );
// Send the API requests.
getRevisions();
}
function showAll() {
$( '.mw-changeslist .special li' ).show();
}
// Settings
var options = document.querySelector( "#mw-watchlist-options, .rcoptions, .mw-rcfilters-head" ),
optionsDiv = document.createElement( "div" ),
input,
languages,
languageData,
isEnhanced = $( '.mw-enhanced-rc, .mw-rcfilters-head' ).length;
if ( options ) {
options.appendChild( document.createElement( "hr" ) ).style.margin = "8px 0";
if ( isEnhanced ) {
// If "Enhanced recent changes" is enabled, this script doesn't work.
optionsDiv.appendChild(
document.createTextNode( '(The "Recent changes by language" gadget requires the "Use non-JavaScript interface" preference to be ON and the "Group changes by page" preference to be OFF.)' )
);
options.appendChild( optionsDiv );
} else {
optionsDiv.appendChild( document.createTextNode( 'Languages: ' ) );
input = optionsDiv.appendChild( document.createElement( 'input' ) );
languageData = ( input.value = ( localStorage[ 'YR-rcbl' ] || '' ) );
languages = getLanguage();
input.onkeydown = input.onkeyup = function ( e ) {
languageData = localStorage[ 'YR-rcbl' ] = this.value;
if ( e.keyCode === 13 && e.type === 'keydown' ) {
languages = getLanguage();
showAll();
hideFiltered();
e.preventDefault && e.preventDefault();
return false;
}
};
input.onblur = function () {
languageData = localStorage[ 'YR-rcbl' ] = this.value;
languages = getLanguage();
showAll();
hideFiltered();
};
options.appendChild( optionsDiv );
// Run
hideFiltered();
}
}
} );
jn2ameweu3sqdms3gimerf9lemwt7k7
89014
89013
2026-08-06T23:33:41Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-ChangesByLanguage.js]]
89013
javascript
text/javascript
( [ "Recentchanges", "Watchlist", "Recentchangeslinked" ] ).indexOf( mw.config.get( "wgCanonicalSpecialPageName" ) ) !== -1 && $( function () {
var diffLinksSelector = ".mw-changeslist .special li > .mw-changeslist-line-inner > a:first-child, .mw-enhanced-rc .mw-title + a, .mw-enhanced-rc-nested a[tabindex] + a";
var getRevisions = ( function () {
var revRequests = {}, pRequests = {};
function sendRequests( requests, isPageid ) {
var pendingReqs = [];
$.each( requests, function ( i, req ) {
if ( !req.status ) {
pendingReqs.push( i );
req.status = 1;
}
} );
for ( var i = 0; i < pendingReqs.length; i += 50 ) {
var query = {
action: 'query',
prop: 'revisions',
rvprop: 'content|ids',
maxage: 60 * 60 * 12,
smaxage: 60 * 60 * 12
};
query[ isPageid ? 'pageids' : 'revids' ] = pendingReqs.slice( i, i + 50 ).join( '|' );
( new mw.Api() ).get( query ).done( function ( results ) {
$.each( results.query.pages, function ( id, revision ) {
$.each( revision.revisions, function ( i, rev ) {
var rid = isPageid ? id : rev.revid,
result = requests[ rid ].result =
rev[ '*' ];
requests[ rid ].status = 2;
$.each( requests[ rid ], function ( i, callback ) {
callback( result );
} );
} );
} );
} );
}
}
return function getRevisions( id, callback, isPageid ) {
if ( id ) {
var requests = isPageid ? pRequests : revRequests,
req = requests[ id ] || ( requests[ id ] = [] );
if ( callback ) {
if ( !req.status || req.status === 1 ) {
req.push( callback );
} else {
callback( req.result );
}
}
} else {
sendRequests( pRequests, true );
sendRequests( revRequests, false );
}
return getRevisions;
};
} )();
function getSection( content, language ) {
var start = content.indexOf( '==' + language + '==' ),
end = content.substr( start ).search( /$|\n==[^=]/ );
if ( start === -1 ) {
return false;
} else {
return content
.substr( start, end )
// Don't count a new section being added below as a change.
.replace( /[\n \-]+$/g, '' )
// Filter out translations sections.
.replace( /(\{\{(?:check)?trans-top[^\}]*\}\})[\s\S]*?(?=\{\{(?:check)?trans-bottom\}\})/g, '$1' );
}
}
function getTranslations( content, language ) {
var t = content
.match( new RegExp( '\n[\\*\\:]+\\s*' + language + ': [^\n]+', 'g' ) );
return t ? t.join( '\n' ) : '';
}
function getLanguage() {
return languageData
.split( /\s*[,|]\s*/g)
// Remove empty strings
.filter( function ( x ) { return x; } );
}
function hideFiltered() {
if ( !languages || !languages.length ) {
return;
}
$( diffLinksSelector ).each( function( i, diffLink ) {
var href = diffLink.href,
oldid = mw.util.getParamValue( "oldid", href ),
curid = mw.util.getParamValue( "diff", href ),
title = mw.util.getParamValue( "title", href );
if ( title && !title.includes( ":" ) && oldid && curid ) {
getRevisions( curid );
getRevisions( oldid, function ( oldRevision ) {
getRevisions( curid, function ( curRevision ) {
var show = languages.some( function ( language ) {
var oldSection = getSection( oldRevision, language ),
newSection = getSection( curRevision, language );
if ( oldSection !== newSection ) {
return true;
} else if ( getTranslations( oldRevision, language ) !== getTranslations( curRevision, language ) ) {
return true;
}
} );
if ( !show ) {
diffLink.parentNode.parentNode.style.display = 'none';
}
} );
} );
}
} );
// Newly created pages, without diff links.
$( '.newpage' ).prev().prev().each( function ( i, histLink ) {
var href = histLink.href,
curid = mw.util.getParamValue( "curid", href );
getRevisions( curid, function ( curRevision ) {
var show = languages.some( function ( language ) {
if ( getSection( curRevision, language ) || getTranslations( curRevision, language ) ) {
return true;
}
} );
if ( !show ) {
histLink.parentNode.parentNode.style.display = 'none';
}
}, true );
} );
// Send the API requests.
getRevisions();
}
function showAll() {
$( '.mw-changeslist .special li' ).show();
}
// Settings
var options = document.querySelector( "#mw-watchlist-options, .rcoptions, .mw-rcfilters-head" ),
optionsDiv = document.createElement( "div" ),
input,
languages,
languageData,
isEnhanced = $( '.mw-enhanced-rc, .mw-rcfilters-head' ).length;
if ( options ) {
options.appendChild( document.createElement( "hr" ) ).style.margin = "8px 0";
if ( isEnhanced ) {
// If "Enhanced recent changes" is enabled, this script doesn't work.
optionsDiv.appendChild(
document.createTextNode( '(The "Recent changes by language" gadget requires the "Use non-JavaScript interface" preference to be ON and the "Group changes by page" preference to be OFF.)' )
);
options.appendChild( optionsDiv );
} else {
optionsDiv.appendChild( document.createTextNode( 'Languages: ' ) );
input = optionsDiv.appendChild( document.createElement( 'input' ) );
languageData = ( input.value = ( localStorage[ 'YR-rcbl' ] || '' ) );
languages = getLanguage();
input.onkeydown = input.onkeyup = function ( e ) {
languageData = localStorage[ 'YR-rcbl' ] = this.value;
if ( e.keyCode === 13 && e.type === 'keydown' ) {
languages = getLanguage();
showAll();
hideFiltered();
e.preventDefault && e.preventDefault();
return false;
}
};
input.onblur = function () {
languageData = localStorage[ 'YR-rcbl' ] = this.value;
languages = getLanguage();
showAll();
hideFiltered();
};
options.appendChild( optionsDiv );
// Run
hideFiltered();
}
}
} );
jn2ameweu3sqdms3gimerf9lemwt7k7
MediaWiki:Gadget-DefSideBoxes.js
8
18006
89015
2025-09-17T01:08:42Z
en>Ioaxxere
0
Polyfill deprecated function newNode
89015
javascript
text/javascript
// Newer version of User:Yair rand/editor.js
// <nowiki>
// ******** imported from [[MediaWiki:Gadget-LegacyScriptsNewNode.js]] ********
var newNode = window.newNode = function newNode(tagname) {
var node = document.createElement(tagname);
for (var i = 1; i < arguments.length; i++) {
var argument = arguments[i];
if (typeof argument == 'string') { //Text
node.appendChild(document.createTextNode(argument));
} else if (typeof argument == 'object') {
if (argument instanceof Node) { // If it is a DOM Node
node.appendChild(argument);
} else { // Attributes (hopefully)
for (var j in argument) {
if (j === 'class') { // Classname different because...
node.className = argument[j];
} else if (j == 'style') { // Style is special
node.style.cssText = argument[j];
} else if (typeof argument[j] == 'function') { // Basic event handlers
node.addEventListener(j, argument[j], false);
} else {
node.setAttribute(j, argument[j]); //Normal attributes
}
}
}
}
}
return node;
};
// ****************************************************************************
var autoTranslitScripts = {
"Geor": true
};
window.makedefsidebox = function(q) {
function clickiconfunction() {
var moreoptions, originalHeight, buttons;
function uncollapse() {
$(moreoptions).slideUp();
$("body").off("click", uncollapse);
}
editdefinition(q);
if ($('>.addusexbutton', q).length === 0) {
buttons = q.insertBefore(newNode('div', {
'class': 'addusexbutton'
}, newNode('a', '+Add example sentence', {
'click': function() {
setupusexadder(q);
}
}), newNode('a', '+Add quotation', {
'click': function() {
setupquoteadder(q);
}
}), newNode('a', 'More ►', {
'click': function() {
$(moreoptions).slideDown();
setTimeout(function() {
$("body").click(uncollapse);
}, 16);
}
}), newNode('span', {
'style': 'display:inline-block;'
}, moreoptions = newNode('ul', newNode('li', q.className ? newNode('a', 'Edit gloss', {
'click': function() {
editgloss(q);
}
}) : newNode('a', 'Add gloss', {
'click': function() {
addgloss(q);
}
})), newNode('li', newNode('a', q.className ? undefined : {
'class': 'DSBinactive',
'title': 'Please add a gloss before adding synonyms.'
}, {
'click': function() {
setupnymadder(q, "Synonyms");
}
}, 'Add synonyms')), newNode('li', newNode('a', q.className ? undefined : {
'class': 'DSBinactive',
'title': 'Please add a gloss before adding antonyms.'
}, {
'click': function() {
setupnymadder(q, "Antonyms");
}
}, 'Add antonyms')), newNode('li', newNode('a', {
'href': mw.config.get('wgScript') + '?title=User_talk:Yair_rand/editor2.js&action=edit§ion=new'
}, 'Give feedback on this tool')), newNode('li', newNode('a', {
click: function() {
$.cookie("DisableYREdit", 1, {
path: '/'
});
location.href = location.href;
}
}, 'Disable this tool'))))), $('>dl,>ul,>ol', q)[0] || null);
}
}
q.insertBefore(
newNode('span', {
'class': 'defEditButton'
}, newNode('span', {
'click': clickiconfunction,
'title': 'Edit'
})), q.firstChild);
return clickiconfunction;
}
function setupusexadder(qq) {
var x, y;
var usexform;
var findsubdef = (qq.parentNode.parentNode.nodeName.toLowerCase() == "li" ? "#" : "") + (qq.parentNode.parentNode.parentNode.parentNode.nodeName.toLowerCase() == "li" ? "#" : "")
editor = new Editor()
findlang(qq.parentNode, function(findlang_) {
var findscript = (new LangMetadata()).getScripts(findlang_),
selectedscript = findscript[0] || "Latn";
if (qq.getElementsByTagName('dl')[0] && qq.getElementsByTagName('dl')[0].parentNode == qq) x = qq.getElementsByTagName('dl')[0];
else if ($(">ul", qq).length > 0) x = qq.insertBefore(newNode('dl'), $(">ul", qq)[0]);
else x = qq.appendChild(newNode('dl'));
x.appendChild(y = newNode('dd'))
new AdderWrapper(new Editor(), {
'createForm': function() {
var x, i = 0,
y = function() {
selectedscript = this.value;
usexform.getElementsByTagName('dd')[0].style.display = this.value == 'Latn' ? 'none' : 'block'
}
if (findscript.length > 1) {
for (x = newNode('select', {
'name': 'script',
'change': y
}); i < findscript.length; i++) {
x.appendChild(newNode('option', {
'value': findscript[i]
}, findscript[i]))
}
} else {
x = newNode('input', {
'name': 'script',
'value': findscript[0] || "Latn",
'style': "width:50px;",
'change': y
})
}
return usexform = newNode('form', {
'style': 'display:inline;'
}, newNode('input', {
'size': 100,
'placeholder': 'Example sentence',
'name': 'examplesentence'
}), newNode('span', {
'style': 'display:' + (findscript.length == 1 ? "none" : "inline") + ";"
}, 'Script: ', x), newNode('div', {
'class': "DSBRedButton",
'click': function() {
usexform.style.display = 'none'
}
}, newNode('div', 'Cancel')), newNode('div', {
'class': "DSBGreenButton",
'click': function() {
usexform.onsubmit()
}
}, newNode('div', "Preview ►")), newNode('input', {
'type': 'submit',
'style': 'visibility:hidden;width:0px;'
}), newNode('dl', (findlang_ == "en" ? {
'style': 'display:none;'
} : ''), newNode('dd', 'Transliteration: ', ((findscript[0] == 'Latn' || !findscript[0]) ? {
'style': 'display:none;'
} : ''), newNode('input', {
'size': '100px',
name: 'transliteration'
})), newNode('dd', 'Translation: ', newNode('input', {
'size': '100px',
name: 'translation'
}))))
},
'fields': {
'examplesentence': function(txt, error) {
return util.validateNoWikisyntax('example sentence', true)(txt, error)
},
'transliteration': function(txt, error) {
return txt || '';
},
'translation': function(txt, error) {
if (findlang_ != "en") {
return util.validateNoWikisyntax('translation', true)(txt, error);
}
return '';
},
'script': util.validateNoWikisyntax('script', true)
},
'onsubmit': function(values, render) {
var renderWikitext = "";
if (findlang_ === "en")
renderWikitext = "''" + values.examplesentence + "''";
else {
renderWikitext = "{" + "{ux|" + findlang_ + "|" + values.examplesentence + "|" + values.translation;
if (values.script) {
renderWikitext += "|sc=" + values.script;
if (!(values.script in autoTranslitScripts) && values.transliteration)
renderWikitext += "|tr=" + values.transliteration;
}
renderWikitext += "}}";
}
render(renderWikitext, function(newhtml) {
var findnumberofheaders = findnumberofheaders_(qq);
var findnumberofdefs = findnumberofdefs_(qq);
var usextobeadded = newNode('span')
usextobeadded.innerHTML = newhtml.replace(/<p>|<\/p>/g, '');
editor.addEdit({
edit: function(wikitext) {
var usexWikitext = renderWikitext;
wikitext = wikitext.replace(RegExp(
"(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}" + findnumberofdefs + "[\\s\\S]*?)\n(?!#" + findsubdef + ":)|$"), "$1\n#" + findsubdef + ":" + usexWikitext + "\n");
ccc = wikitext;
return wikitext;
},
redo: function() {
usexform.parentNode.appendChild(usextobeadded);
usextobeadded.style.display = 'block';
usexform.style.display = "none";
y.style.display = 'block'
},
undo: function() {
usexform.parentNode.removeChild(usextobeadded);
usexform.style.display = "inline";
},
summary: "+usex: " + values.examplesentence
}, usextobeadded)
})
}
}, y)
usexform.firstChild.focus()
})
}
window.editdefinition = function(qq) {
(new Editor).withCurrentText(function(wikitext) {
if (qq.YREditing) {
return
}
qq.YREditing = true;
var findnumberofheaders = findnumberofheaders_(qq),
findnumberofdefs = findnumberofdefs_(qq)
var temporarydefholder = qq.insertBefore(newNode('span', {
'style': 'display:none;'
}), qq.childNodes[1].nodeName.toLowerCase() != "form" ? qq.childNodes[1] : qq.childNodes[2])
while (qq.childNodes[3] && qq.childNodes[3].nodeName != "DL" && qq.childNodes[3].nodeName != "UL" && qq.childNodes[3].nodeName != "OL" && qq.childNodes[3].className != "HQToggle" && qq.childNodes[3].className != "addusexbutton")
temporarydefholder.appendChild(qq.childNodes[3]);
if (qq.childNodes[2] != temporarydefholder) temporarydefholder.insertBefore(qq.childNodes[2], temporarydefholder.firstChild);
var defform
var olddef = wikitext.replace(RegExp("(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}" + findnumberofdefs + "\\s?)"), '').match(/^(\{\{senseid\|[^\}]*\}\})?\s?(.*)/)[2]
new AdderWrapper(new Editor(), {
'createForm': function() {
return defform = newNode('form', {
'style': 'width:100%;display:inline-block;margin-bottom:-6px;'
}, newNode('table', {
'style': 'width:100%;'
}, newNode('tr', newNode('td', newNode('input', {
'style': 'height:17px;width:100%;',
'name': 'definition',
'size': 80,
'value': olddef
})
// newNode('input',{type:'submit',value:'Preview'}),
// newNode('input',{type:'button',value:'Cancel',click:function(){temporarydefholder.style.display='inline';defform.style.display='none';qq.YREditing = false}})
), newNode('td', {
'style': 'white-space:nowrap;'
}, newNode('div', {
'class': "DSBRedButton"
}, newNode('div', {
click: function() {
temporarydefholder.style.display = 'inline';
defform.style.display = 'none';
qq.YREditing = false
}
}, "Discard Changes")), newNode('div', {
'class': "DSBGreenButton"
}, newNode('div', {
click: function() {
defform.onsubmit()
}
}, "Preview Changes ►"))))))
},
'fields': {
'definition': function(txt, error) {
return txt || error("Please specify a definition.")
}
},
'onsubmit': function(values, render) {
render(values.definition, function(newhtml) {
editor = new Editor()
var findnumberofdefs = findnumberofdefs_(qq) // apparently this is sometimes necessary
var addedspan = newNode('span')
addedspan.innerHTML = newhtml
var updatecatscallback;
editor.addEdit({
edit: function(wikitext) {
wikitext = wikitext.replace(RegExp("(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}" + findnumberofdefs + "\\s?(\{\{senseid\\|[^\}]*\}\})*?)(?!{" + "{senseid\\|).*"), "$1" + values.definition);
ccc = wikitext;
return wikitext
},
redo: function() {
defform.parentNode.insertBefore(addedspan, defform);
defform.style.display = temporarydefholder.style.display = 'none';
updatecatscallback = window.tabbedLanguages && updateCategories(findtopli(defform.parentNode).parentNode.parentNode, values.definition, olddef)
qq.YREditing = false;
},
undo: function() {
defform.parentNode.removeChild(addedspan);
defform.style.display = "inline";
defform.firstChild.firstChild.firstChild.firstChild.value = olddef;
updatecatscallback && updatecatscallback();
qq.YREditing = true;
},
summary: "def: " + values.definition
}, addedspan)
})
}
}, qq, qq.childNodes[1].nodeName.toLowerCase() != "form" ? qq.childNodes[1] : qq.childNodes[2])
})
}
function findtopli(li) {
for (; li.parentNode.parentNode.nodeName.toLowerCase() == "li";) {
li = li.parentNode.parentNode;
}
return li;
}
function findnumberofheaders_(tosearchfrom) {
tosearchfrom = findtopli(tosearchfrom).parentNode;
while (!tosearchfrom.nodeName.match(/^h\d$/i))
tosearchfrom = tosearchfrom.previousSibling;
return Number(tosearchfrom.lastChild.getElementsByTagName('a')[0].href.match(/\d*$/))
}
function findnumberofdefs_(tosearchfrom) {
var findnumberofdefs = [],
tosearchfrom_, findnumberofpredefs = 0,
result;
// subdefinitions
while (tosearchfrom.nodeName.toLowerCase() == "li") {
tosearchfrom_ = tosearchfrom;
findnumberofdefs.splice(0, 0, 0);
while (tosearchfrom) {
if (tosearchfrom.nodeName.toLowerCase() == "li") findnumberofdefs[0]++;
tosearchfrom = tosearchfrom.previousSibling;
}
tosearchfrom = tosearchfrom_.parentNode.parentNode;
}
// in case of any def lists before the current def list under the same header
for (tosearchfrom_ = tosearchfrom_.parentNode.previousSibling; !tosearchfrom_.nodeName.match(/^h\d$/i); tosearchfrom_ = tosearchfrom_.previousSibling) {
if (tosearchfrom_.nodeName.toLowerCase() == "ol") findnumberofpredefs += 1;
}
result = "([\\s\\S]*?\n#[\\s\\S]*?\n(?!#)){" + findnumberofpredefs + "}";
for (var i = 0; i < findnumberofdefs.length; i++) {
result += "([\\s\\S]*?\n" + Array(i + 2).join("#") + "(?![:*#])){" + findnumberofdefs[i] + "}";
}
return result;
}
function findlang(tosearchfrom, callback) {
var tosearchfrom_, findlang__
if (tosearchfrom.parentNode.nodeName.toLowerCase() == "li") tosearchfrom = findtopli(tosearchfrom.parentNode).parentNode;
if (tosearchfrom.parentNode.className == "languageContainer") {
if (tosearchfrom.parentNode.id == "Englishcontainer") return callback("en");
} else {
tosearchfrom_ = tosearchfrom;
while (tosearchfrom_.nodeName.toLowerCase() != "h2")
tosearchfrom_ = tosearchfrom_.previousSibling;
if (tosearchfrom_.lastChild.innerHTML == "English") return callback("en");
}
tosearchfrom_ = tosearchfrom;
while (tosearchfrom_ && !/^h\d$/i.test(tosearchfrom_.nodeName) && tosearchfrom_.className != "fakeheader")
tosearchfrom_ = tosearchfrom_.previousSibling;
while (tosearchfrom_ && tosearchfrom_.nodeName.toLowerCase() != "p" && tosearchfrom_.className != "fakeheader")
tosearchfrom_ = tosearchfrom_.nextSibling;
if (tosearchfrom_ && tosearchfrom_.className != "fakeheader" && tosearchfrom_.firstChild && (tosearchfrom_.firstChild.lang || (tosearchfrom_.firstChild.firstChild && tosearchfrom_.firstChild.firstChild.lang) || (tosearchfrom_.firstChild.firstChild && tosearchfrom_.firstChild.firstChild.firstChild && tosearchfrom_.firstChild.firstChild.firstChild.lang))) {
return callback(tosearchfrom_.firstChild.lang || tosearchfrom_.firstChild.firstChild.lang || tosearchfrom_.firstChild.firstChild.firstChild.lang)
}
if (tosearchfrom.parentNode.className == "languageContainer") findlang__ = tosearchfrom.parentNode.id.split("container")[0];
else {
while (tosearchfrom.nodeName.toLowerCase() != "h2")
tosearchfrom = tosearchfrom.previousSibling;
findlang__ = (findlang__ = tosearchfrom.getElementsByTagName("span")[0]).innerText || findlang__.textContent
}
window.languagecodefinder_preload = window.languagecodefinder_preload || mw.loader.getScript('https://en.wiktionary.org/w/index.php?title=User:Yair_rand/languagecodefinder.js&action=raw&ctype=text/javascript');
window.languagecodefinder_preload.always( function() {
window.languagenametocode && languagenametocode[findlang__] && callback(languagenametocode[findlang__])
});
}
function setupquoteadder(q) {
findlang(q.parentNode, function(findlang_) {
var z;
if (!$(".HQToggle a", q)[0]) {
z = q.appendChild(document.createElement("ul"))
setupHiddenQuotes(q);
} else {
z = $(">ul", q)[0];
}
if (z.style.display == 'none') {
$(".HQToggle a", q).click();
}
var zz = z.appendChild(document.createElement("li")),
quoteform;
var findsubdef = q.parentNode.parentNode.nodeName.toLowerCase() == "li" ? "#" : "",
editor = new Editor();
new AdderWrapper(editor, {
'createForm': function() {
return quoteform = newNode('form', {
'style': 'display:inline;'
}, 'Year: ', newNode('input', {
'name': 'year',
'size': 4
}), ' Author: ', newNode('input', {
'name': 'author',
'size': 20
}), ' Source title: ', newNode('input', {
'name': 'source',
'size': 20
}), ' Publisher: ', newNode('input', {
'name': 'publisher',
'size': 20
}), ' Page(s): ', newNode('input', {
'name': 'page',
'size': 5
}), newNode('dl', newNode('dd', 'Quotation: ', newNode('input', {
'name': 'quotation',
'size': 40
}), newNode('div', {
'class': "DSBRedButton",
'click': function() {
z.removeChild(zz)
}
}, newNode('div', 'Cancel')), newNode('div', {
'class': "DSBGreenButton",
'click': function() {
quoteform.onsubmit()
}
}, newNode('div', "Preview ►")), newNode('input', {
'type': 'submit',
'style': 'visibility:hidden;width:0px;'
}), newNode('dl', {
'style': 'display:' + (findlang_ == "en" ? "none" : "block")
}, newNode('dd', 'Translation: ', newNode('input', {
'size': 40,
'name': 'translation'
}))))))
},
'fields': {
'quotation': util.validateNoWikisyntax('quotation', true),
'year': util.validateNoWikisyntax('year', true),
'author': function(t) {
return t
},
'source': function(t) {
return t ? ", ''" + t + "''" : ''
},
'publisher': function(t) {
return t ? ", " + t : ''
},
'page': function(t) {
return t ? (/\-/.test(t) ? "pages " + t : "page " + t) : t
},
'translation': function(t) {
return t
}
},
'onsubmit': function(values, render) {
var topline = "'''" + values.year + "'''" + (values.author ? ", " + values.author : '') + values.source + values.publisher + (values.page ? ", " + values.page : '')
render(topline + "\n:" + values.quotation + (values.translation ? "\n:" + values.translation : ""), function(newhtml) {
var findnumberofheaders = findnumberofheaders_(q),
findnumberofdefs = findnumberofdefs_(q),
quotetobeadded = newNode('span', {
'style': 'display:block;'
});
quotetobeadded.innerHTML = newhtml.replace(/\<p\>|\<\/p\>/g, '');
editor.addEdit({
'edit': function(wikitext) {
return ccc = wikitext.replace(RegExp("(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}" + findnumberofdefs + "[\\s\\S]*?)\n(?!#" + findsubdef + "[:\*])|$"), "$1\n#" + findsubdef + "*" + topline + "\n#" + findsubdef + "*:" + values.quotation + (values.translation ? "\n#" + findsubdef + "*:" + values.translation : '') + '\n')
},
'redo': function() {
quoteform.style.display = 'none';
quotetobeadded.style.display = 'block';
zz.appendChild(quotetobeadded)
},
'undo': function() {
quoteform.style.display = 'inline';
zz.removeChild(quotetobeadded)
},
'summary': '+quote: "' + values.quotation + '"- ' + values.source
}, quotetobeadded)
})
}
}, zz)
})
}
window.addPOSHeader = function(cancelhover) {
var bodyContent = $(".mw-content-ltr")[0];
if (!defeditoptions.addingPOS) {
defeditoptions.addingPOS = true;
editor = new Editor()
var headerLevel = (window.languageContainers && languageContainers[currentLanguageTab] && languageContainers[currentLanguageTab].firstChild) || bodyContent.firstChild;
var ii;
var temp, temp_;
var theform;
var findnumberofheaders;
var addedwikitext;
var prefakeheaders;
var currentFocus;
var langNames = [],
docLink, docExists = {};
if (window.loadedTabs) {
langNames = tabbedLanguages;
} else {
mw.util.$content.find("h2>.mw-headline").each(function() {
langNames.push($(this).text())
})
}
var langNameCodes;
new mw.Api().get({
"action": "expandtemplates",
"format": "json",
"text": "{{#invoke:languages/javascript-interface|GetAllData}}",
"prop": "wikitext"
}).done(function(r){
if(r.expandtemplates && r.expandtemplates.wikitext){
langNameCodes = JSON.parse(r.expandtemplates.wikitext);
}
});
function updateDocLink() {
var pos = theform.getElementsByTagName("input")[0].value;
pos = (({
Adjective: "adj",
Adverb: "adv",
Interjection: "interj",
Preposition: "prep",
Pronoun: "pron",
Conjunction: "con",
Contraction: "cont",
Determiner: "det"
})[pos] || pos.toLowerCase());
if (!langNameCodes || !pos || !theform.getElementsByTagName("input")[0].value) {
return;
}
var currentLang = window.loadedTabs ? theform.parentNode.id.split("container")[0] : (function() {
for (var s = theform; s && s.nodeName != "H2"; s = s.previousSibling);
return s && s.lastChild && (s.lastChild.innerText || s.lastChild.textContent)
})();
var docName = langNameCodes[currentLang] + "-" + pos;
if (docExists[docName] === false) {
return docLink.style.display = 'none';
}
if (docExists[docName] === true) {
docLink.style.display = 'inline';
docLink.href = mw.config.get("wgArticlePath").replace("$1", "Template:" + docName + "/doc");
return;
}
if (docExists[docName] === 'l') {
docLink.style.display = 'none';
return;
}
docExists[docName] = 'l';
new mw.Api().get({
action: 'query',
prop: 'info',
titles: 'Template:' + docName + '/documentation'
}).then(function(r) {
docExists[docName] = r.query.pages.length == 1;
updateDocLink();
});
}
var poslist = '\nNoun\nVerb\nAdjective\nAdverb\nPronoun\nConjunction\nInterjection\nPreposition\nProper noun\nArticle\nPrepositional phrase\nAcronym\nAbbreviation\nInitialism\nContraction\nPrefix\nSuffix\nSymbol\nLetter\nLigature\nIdiom\nPhrase\nNumber\nNumeral\nCardinal number\nOrdinal number\nCardinal numeral\nOrdinal numeral\nPersonal pronoun\nDeterminer\nDemonstrative determiner\nClitic\nInfix\nInterfix\nAffix\nCircumfix\nCircumposition\nCounter\nKanji\nKanji reading\nHanja reading\nHirigana letter\nKatakana letter\nPinyin\nHan character\nHanzi\nHanja\nProverb\nExpression\nParticle\nInfinitive particle\nPossesive adjective\nPostposition\nPhrasal verb\nParticiple\nGerund\nInterrogative auxiliary verb\nPronominal adverb\nAdnominal\nAbstract pronoun\nConjunction particle\nCombining form\nRoot\nPinyin syllable\nSyllable\nHirigana character\nKatakana character\nJyutping syllable\nGismu\nLujvo\nBrivla\nClassifier\nPredicative\nMeasure word\nCorrelative\nPreverb\nPrenoun\nNoun stem\nNoun class\nCombined-kana character\nCmavo\n';
while (headerLevel.nextSibling) {
if (/^h[34]$/i.test(headerLevel.nodeName) && !/Etymology|Pronunciation|Alternative\sforms/.test(headerLevel.innerHTML)) {
headerLevel = headerLevel.nodeName.toLowerCase();
break;
}
headerLevel = headerLevel.nextSibling
}
currentBoxToBeAdded = newNode('div', {
'style': 'position:fixed;top:500px;'
})
new AdderWrapper(editor, {
createForm: function() {
return theform = newNode('form', currentFocus = newNode('input', {
'style': 'font-size: 0.8em;font-size: ' + ((headerLevel == 'h3') ? '132%' : '116%') + ';font-weight: bold;font-family: sans-serif;width:auto;',
'placeholder': 'Part of speech',
'rows': 1,
'name': 'pos',
focus: function() {
currentFocus = this
}
}), newNode('input', {
'type': 'submit',
'value': 'Preview'
}), newNode('input', {
'type': 'button',
'value': 'Cancel',
click: function() {
theform.style.display = 'none';
hoverNewPOS(null);
defeditoptions.addingPOS = false;
document.onmousemove = null;
document.body.style.cursor = '';
}
}), newNode('br'), newNode('input', {
'style': 'width:300px;font-family:monospace;',
'placeholder': '{\{head}}',
'name': 'inflline',
focus: function() {
currentFocus = this
}
}), docLink = newNode('a', {
style: "margin-left:5px;display:none;",
'target': '_blank'
}, "Help?"), newNode('ol', newNode('li', newNode('input', {
'size': 100,
'disabled': 'disabled'
}))))
},
fields: {
'pos': function(txt, error) {
return poslist.indexOf('\n' + txt + '\n') > -1 ? txt : error('Please select a valid part of speech.')
},
'inflline': function(txt, error) {
return txt || "{" + "{head}}"
}
},
onsubmit: function(values, render) {
addedwikitext = '{' + '{subst:=}}==' + (headerLevel == 'h4' ? '=' : '') + values.pos + (headerLevel == 'h4' ? '=' : '') + '===\n' + values.inflline
hoverNewPOS(null);
document.onmousemove = null;
defeditoptions.addingPOS = false;
document.body.style.cursor = '';
render(addedwikitext, function(res) {
theform.style.display = 'none'
temp = document.createDocumentFragment();
temp.appendChild(temp_ = newNode('div', {
'class': 'fakeheader'
}));
temp_.innerHTML = res;
temp.appendChild(newNode('ol', newNode('li', ' ', newNode('span'))));
theform.parentNode.insertBefore(temp, theform);
prefakeheaders = 0
findnumberofheaders = theform.previousSibling.previousSibling;
while (true) {
if (findnumberofheaders.previousSibling) {
findnumberofheaders = findnumberofheaders.previousSibling;
if (/h[2-6]/i.test(findnumberofheaders.nodeName)) {
findnumberofheaders = Number(findnumberofheaders.lastChild.getElementsByTagName('a')[0].href.split("section=")[1]);
break;
}
findnumberofheaders.className == 'fakeheader' && prefakeheaders++;
} else {
findnumberofheaders.parentNode.insertBefore(newNode('h2', languageHeaderEditButtons[currentLanguageTab].cloneNode(true), {
'style': 'display:none;'
}), findnumberofheaders);
}
}
var updatecatscallback;
editor.addEdit({
'edit': function(wikitext) {
return ccc = wikitext.replace(RegExp("(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}([\\s\\S]*?\n\\{\\{subst:\\=\\}\\}==" + (headerLevel == 'h4' ? '=' : '') + "[^=]){" + prefakeheaders + "}[\\s\\S]*?(?=(\n=|\n----|\n\\{\\{subst:\\=\\}\\}==" + (headerLevel == 'h4' ? '=' : '') + "[^=]|$)))"), "$1\n" + addedwikitext + "\n\n# {" + "{rfdef}}\n")
},
'redo': function() {
theform.style.display = 'none';
theform.parentNode.insertBefore(temp, theform);
updatecatscallback = window.tabbedLanguages && updateCategories(theform.parentNode, addedwikitext)
},
'undo': function() {
theform.style.display = 'block';
temp.appendChild(temp_);
temp.appendChild(theform.previousSibling);
updatecatscallback && updatecatscallback()
},
'summary': '+pos ' + values.pos
}, temp_)
makedefsidebox(theform.previousSibling.firstChild)();
editor.withCurrentText(function() {
theform.previousSibling.getElementsByTagName('input')[0].value = '';
theform.previousSibling.getElementsByTagName('input')[0].focus()
})
})
}
}, currentBoxToBeAdded)
cancelhover || setUpBoxToBeAdded();
theform.firstChild.focus()
function hoverNewPOS(thefunction) {
for (var ii = bodyContent.getElementsByTagName(headerLevel), i = 0; i < ii.length; i++) {
if (!/^Etymology|Pronunciation|Alternative\sforms$/.test(ii[i].firstChild.innerHTML))(ii[i].parentNode.className == 'fakeheader' ? ii[i].parentNode : ii[i]).onmouseover = thefunction;
}
if (window.tabbedLanguages) {
for (var i = 0; i < languageContainers.length; i++)
languageContainers[i].lastChild.onmouseover = thefunction;
} else {
ii = bodyContent.getElementsByTagName('hr');
for (var i = 0; i < ii.length; i++)
ii[i].onmouseover = thefunction;
}
}
function hoverNewPOS_() {
currentBoxToBeAdded.style.display = 'none';
this.parentNode.insertBefore(theform, this);
currentFocus.focus();
updateDocLink()
}
cancelhover || hoverNewPOS(hoverNewPOS_);
// autocomplete pos field
theform.firstChild.onkeydown = function(e) {
if ((e || event).keyCode == 9) {
theform.getElementsByTagName("input")[3].focus();
return false
}
}
theform.firstChild.onkeyup = function(e) {
if (this.value[0] != this.value.charAt(0).toUpperCase()) this.value = this.value.charAt(0).toUpperCase() + this.value.substr(1);
e = (e || event).keyCode;
if (!(e >= 33 && e <= 40) && e != 8 && e != 46 && e != 27 && e != 16 && this.value) {
if (poslist.indexOf('\n' + this.value) > -1) {
if (this.setSelectionRange) this.setSelectionRange([this.value.length, this.value = poslist.match(RegExp(this.value + "[^\n]*"))][0], this.value.length);
else if (this.createTextRange) {
var z = this.createTextRange();
z.moveEnd('character', 0 - z.move('character', [this.value.length, this.value = poslist.match(RegExp(this.value + "[^\n]*"))][0]) + this.value.length);
z.select()
}
}
updateDocLink()
}
}
}
}
window.updateCategories = function(container, newvalue, oldvalue) {
var addedCategories = [],
removedCategories = []
new mw.Api().get({
action: "parse",
prop: "categorieshtml",
title: mw.config.get('wgPageName'),
text: newvalue
}).then(function(r) {
var e = r.parse.categorieshtml['*'].match(/\<li.*?\/li\>/g);
var d = container.lastChild.lastChild;
var f = document.createElement('ul');
for (var i = 0; e && i < e.length; i++) {
f.innerHTML = e[i];
if (d.innerHTML.indexOf(f.firstChild.innerHTML) == -1) {
addedCategories.push(d.insertBefore(f.lastChild, d.lastChild))
}
}
oldvalue && new mw.Api().get({
action: "parse",
prop: "categorieshtml",
title: mw.config.get('wgPageName'),
text: oldvalue
}).then(function(o) {
e = o.parse.categorieshtml['*'].match(/\<li.*?\/li\>/g)
for (var i = 0; e && i < e.length; i++) {
f.innerHTML = e[i];
if (r.parse.categorieshtml['*'].indexOf(f.firstChild.innerHTML) == -1 && d.innerHTML.indexOf(f.firstChild.innerHTML) != -1) {
var lis = d.getElementsByTagName('li')
for (var ii = 0; ii < lis.length; ii++) {
if (lis[ii].firstChild.innerHTML == f.firstChild.firstChild.innerHTML) {
removedCategories.push(lis[ii]);
lis[ii].style.display = 'none';
break;
}
}
}
}
})
});
return function() {
for (var i = 0; i < addedCategories.length; i++) {
container.lastChild.lastChild.removeChild(addedCategories[i])
}
for (var i = 0; i < removedCategories.length; i++) {
removedCategories[i].style.display = '';
}
}
}
function addgloss(qq) {
var senseidform;
findlang(qq.parentNode, function(findlang_) {
if (qq.addinggloss) {
return
}
qq.addinggloss = true;
editor = new Editor();
new AdderWrapper(editor, {
'createForm': function() {
return senseidform = newNode('form', {
'style': 'display:inline; padding-right:20px;'
}, newNode('input', {
'size': 30,
'name': 'senseid'
}), newNode('input', {
'type': 'submit',
'value': 'Add'
}), newNode('input', {
'type': 'button',
'value': 'Cancel',
'click': function() {
qq.removeChild(senseidform);
qq.addinggloss = false
}
}), newNode('a', {
href: '/wiki/Help:Glosses'
}, 'Help?!'))
},
'fields': {
'senseid': util.validateNoWikisyntax('gloss', true)
},
'onsubmit': function(values) {
var findnumberofheaders = findnumberofheaders_(qq)
var findnumberofdefs = findnumberofdefs_(qq)
senseidform.lastChild.innerHTML = '';
var nymB = qq.getElementsByTagName('li')[1]
editor.addEdit({
edit: function(wikitext) {
wikitext = wikitext.replace(RegExp("(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}" + findnumberofdefs + ") ?"), "$1{" + "{senseid|" + findlang_ + "|" + values.senseid + "}}");
return ccc = wikitext;
},
redo: function() {
senseidform.style.display = "none";
$([nymB.firstChild, nymB.nextSibling.firstChild]).removeClass('DSBinactive');
nymB.firstChild.title = nymB.nextSibling.firstChild.title = ''
},
undo: function() {
senseidform.style.display = "inline";
$([nymB.firstChild, nymB.nextSibling.firstChild]).addClass('DSBinactive');
nymB.firstChild.title = 'Please add a gloss before adding synonyms.';
nymB.nextSibling.firstChild.title = 'Please add a gloss before adding antonyms.'
},
summary: "+senseid: " + values.senseid
})
}
}, qq, qq.childNodes[1])
senseidform.firstChild.focus()
editor.withCurrentText(function(wikitext) {
wikitext = wikitext.match("==" + (window.loadedTabs ? tabbedLanguages[currentLanguageTab] : $(findtopli(qq).parentNode).prevUntil("h2").eq(-1).prev().find(".mw-headline").text()) + "==\n[\\s\\S]+?(----|$)")[0];
if (!wikitext) return;
var senses = '',
matches = wikitext.match(/(\{\{sense\||\{\{trans-top\|)[^\}]+(?=\}\})/g)
if (!matches) return;
for (var ms = 0; ms < matches.length; ms++) {
senses += "\n" + matches[ms].split("|")[1]
}
if (!senses) return;
senseidform.firstChild.onkeyup = function(e) {
e = (e || event).keyCode;
if (!(e >= 33 && e <= 40) && e != 8 && e != 46 && e != 27 && e != 16 && this.value) {
if (senses.indexOf('\n' + this.value) > -1) {
if (this.setSelectionRange) this.setSelectionRange([this.value.length, this.value = senses.match(RegExp("\n" + this.value + "[^\n]*")).toString().substr(1)][0], this.value.length);
else if (this.createTextRange) {
var z = this.createTextRange();
z.moveEnd('character', 0 - z.move('character', [this.value.length, this.value = senses.match(RegExp("\n" + this.value + "[^\n]*")).toString().substr(1)][0]) + this.value.length);
z.select()
}
}
}
}
})
})
}
function editgloss(qq) {
editor = new Editor()
editor.withCurrentText(function(wikitext) {
if (qq.editinggloss) {
return
}
qq.editinggloss = true;
var findnumberofheaders = findnumberofheaders_(qq)
var findnumberofdefs = findnumberofdefs_(qq)
var senseidform;
var oldsenseid;
new AdderWrapper(editor, {
'createForm': function() {
var thisId = "a" + String(Math.random()).replace(".", "")
oldsenseid = wikitext.replace(RegExp("(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}" + findnumberofdefs + "\\s?)"), '').match(/^\{\{senseid\|[^\\|]*\|([^\}]*)/)[1]
return senseidform = newNode('form', {
'style': 'display:inline; padding-right:20px; '
}, newNode('input', {
'size': 30,
'name': 'senseid',
'value': oldsenseid
}), newNode('input', {
'type': 'checkbox',
'id': thisId,
'name': 'changematching',
style: 'display:' + (window.tabbedLanguages ? 'inline' : 'none')
}), newNode('label', {
'for': thisId,
style: 'display:' + (window.tabbedLanguages ? 'inline' : 'none')
}, 'Edit matching glosses'), newNode('input', {
'type': 'submit',
'value': 'Change'
}), newNode('input', {
'type': 'button',
'value': 'Cancel',
'click': function() {
qq.editinggloss = false;
qq.removeChild(senseidform);
}
}), newNode('a', {
href: '/wiki/Help:Glosses'
}, 'Help?!'))
},
'fields': {
'senseid': util.validateNoWikisyntax('gloss', true),
'changematching': 'checkbox'
},
'onsubmit': function(values) {
var ttables = [],
sensetemps = [],
backups = [];
if (values.changematching) {
ttables = jQuery(".NavHead", languageContainers[currentLanguageTab]).filter(function() {
if (this.parentNode.getElementsByTagName("table")[0].className != "translations") return false;
for (var i = 0; i < this.childNodes.length; i++) {
if (this.childNodes[i].nodeType == 3 || this.childNodes[i].className == 'wt-edit-recurse') {
return (this.childNodes[i].nodeValue || this.childNodes[i].innerHTML) == oldsenseid;
}
}
})
sensetemps = jQuery(".qualifier-content", languageContainers[currentLanguageTab]).filter(function() {
return this.parentNode.nextSibling.nextSibling.className == "ib-colon" && this.innerHTML == oldsenseid;
})
}
editor.addEdit({
edit: function(wikitext) {
wikitext = wikitext.replace(RegExp("(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}" + findnumberofdefs + "\\s?(\{\{senseid\\|[^\\|]*\\|))[^\}]*"), "$1" + values.senseid);
if (values.changematching) {
var asd = wikitext.match('==' + tabbedLanguages[currentLanguageTab] + '==\n[\\s\\S]+?(\n==[^=]|$)');
wikitext = wikitext.replace(asd[0], asd[0].replace(RegExp("\\{\\{(sense|trans-top)\\|" + oldsenseid + "\\}\\}", "g"), "{\{$1\|" + values.senseid + "}}"))
}
return ccc = wikitext
},
redo: function() {
senseidform.style.display = "none";
for (var i = 0; i < ttables.length; i++) {
backups.push(document.createDocumentFragment());
while (ttables[i].childNodes[1]) {
backups[backups.length - 1].appendChild(ttables[i].childNodes[1]);
}
ttables[i].appendChild(document.createTextNode(values.senseid))
}
for (var i = 0; i < sensetemps.length; i++) {
backups.push(document.createDocumentFragment());
backups[backups.length - 1].appendChild(sensetemps[i].firstChild);
sensetemps[i].appendChild(document.createTextNode(values.senseid))
}
},
undo: function() {
senseidform.style.display = "inline";
for (var i = 0; i < ttables.length; i++) {
ttables[i].removeChild(ttables[i].childNodes[1]);
ttables[i].appendChild(backups.shift())
}
for (var i = 0; i < sensetemps.length; i++) {
sensetemps[i].removeChild(sensetemps[i].firstChild)
sensetemps[i].appendChild(backups.shift())
}
},
summary: "senseid: " + values.senseid
})
}
}, qq, qq.childNodes[1])
senseidform.firstChild.focus()
})
}
function setupnymadder(qq, nymtype) {
var passtheseheaders = "Usage notes|Inflection|Conjugation|Declension|Quotations",
nymtypelist = ["Synonyms", "Antonyms", "Hypernyms", "Hyponyms", "Meronyms", "Holonyms"]
for (var x = 0; nymtypelist[x] != nymtype; x++)
passtheseheaders += "|" + nymtypelist[x];
if (qq.parentNode.parentNode.nodeName.toLowerCase() == "li") qq = qq.parentNode.parentNode;
var findnumberofheaders = findnumberofheaders_(qq)
var findnumberofdefs = findnumberofdefs_(qq.parentNode.getElementsByTagName('li')[0])
for (var findheaderlevel = qq.parentNode; !/^h\d$/i.test(findheaderlevel.nodeName); findheaderlevel = findheaderlevel.previousSibling);
findheaderlevel = Number(findheaderlevel.nodeName.match(/\d/));
var senseidencoded = []
editor = new Editor()
editor.withCurrentText(function(wikitext) {
// Find nym header, or add it if it doesn't exist
var qqq = qq.parentNode;
while (!((qqq.className == "fakesubheader" || /h\d/i.test(qqq.nodeName)) && !qqq.innerHTML.match(passtheseheaders) || qqq.className == "printfooter" || qqq.className == "fakeheader" || qqq.className == "catlinks")) {
qqq = qqq.nextSibling
}
if (!qqq.innerHTML.match(nymtype)) {
qqq = qqq.parentNode.insertBefore(newNode('div', {
'class': 'fakesubheader'
}, newNode('h' + (findheaderlevel + 1), nymtype)), qqq);
}
// relevant vars:
// findheaderlevel is the the headerlevel of the most recent header above the ol
// qq is still the li itself
// qqq is the (possibly newly added) nym header
// find all relevant senseids, make an array of them, the whole template in each
var senseid = wikitext.replace(
RegExp("(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}" + findnumberofdefs + ")"), '').replace(/\n\n[\s\S]*$/, '').match(/\{\{senseid\|[^\\|]*\|[^\}]*/g)
if (!senseid) {
alert("No glosses were found. Click \"Add gloss\" to attach a gloss to the definition before adding " + nymtype.toLowerCase() + ".");
return
}
// run through senseids, checking for a corresponding span id'd nymtype-langcode-id, adding them encased in an li in a soon-to-be-parsed temp variable if not found, otherwise suppressing new form creation
temp = ""
var nymformexists = []
for (var i = 0; i < senseid.length; i++) {
if (document.getElementById(senseidencoded[i] = nymtype + "-" + encodeURIComponent(senseid[i].split("|")[1] + "-" + senseid[i].split("|")[2]).replace(/%20/g, '_').replace(/%3A/g, ':').replace(/%/g, '.'))) {
nymformexists[i] = true
} else {
temp += "* {" + "{sense|" + senseid[i].split("|")[2] + "}} <span id='" + nymtype + "-" + senseid[i].split("|")[1] + "-" + senseid[i].split("|")[2] + "'></span>\n"
}
}
new mw.Api().parse(temp, {
title: mw.config.get('wgPageName'),
pst: true, //pst makes subst work as expected
disablelimitreport: true
}).then(function(res) {
var qqqq = newNode('span')
qqqq.innerHTML = res
qqqq = qqq.parentNode.insertBefore(qqqq.firstChild, qqq.nextSibling) // the ul containing the form-holder spans, right below the nym header
for (var i = 0; i < senseid.length; i++) {
if (!nymformexists[i]) {
setupnymadder2(i, senseid, wikitext)
}
}
if (qqqq.getElementsByTagName && qqqq.getElementsByTagName('li').length == 0) {
qqqq.parentNode.removeChild(qqqq)
}
scroll(0, jQuery(qqq).offset().top)
});
})
function setupnymadder2(i, senseid, wikitext) {
var nymtype2 = nymtype.slice(0, -1).toLowerCase()
var x = document.getElementById(senseidencoded[i]);
var theform
new AdderWrapper(editor, {
'createForm': function() {
return theform = newNode('form', {
'style': 'display:inline;'
}, newNode('input', {
'name': 'nym'
}), newNode('input', {
'value': 'Add',
'type': 'submit'
}))
},
'fields': {
'nym': util.validateNoWikisyntax(nymtype2, true)
},
'onsubmit': function(values, render) {
theform.getElementsByTagName('input')[0].value = ""
var firstnym = !theform.parentNode.parentNode.getElementsByTagName('a')[0]
var addedwikitext = (firstnym ? '' : ', ') + "{" + "{l|" + senseid[i].split("|")[1] + "|" + values.nym + "}}"
render(addedwikitext, function(res2) {
var nodeToInsert = newNode('span')
nodeToInsert.innerHTML = res2
editor.addEdit({
edit: function(wikitext) {
// unindent
findnumberofdefs = findnumberofdefs_(qq.parentNode.getElementsByTagName('li')[0])
if (wikitext.substr(wikitext.length - 1, 1) != "\n") {
wikitext += "\n"
}
if (!wikitext.replace(RegExp("(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}" + findnumberofdefs + ")"), '').replace(RegExp("(\n(=|\\{\\{subst:=\\}\\})={" + (findheaderlevel - 1) + "}[^=][\\s\\S]*$)"), '').match(RegExp(nymtype + "={" + findheaderlevel + "}"))) {
wikitext = wikitext.replace(RegExp("(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}" + findnumberofdefs + "[\\s\\S]*?(\n(?=(\\{\\{subst:=\\}\\}|=)=+(?=[^=])(?!(" + passtheseheaders + ")))|$))"), "$1{" + "{subst:=}}===" + (findheaderlevel == 4 ? "=" : "") + nymtype + (findheaderlevel == 4 ? "=" : "") + "====\n\n")
}
if (firstnym) {
var passthesesenseids = [];
for (var ii = 0; ii < i; ii++) {
passthesesenseids.push("\\* \\{\\{sense\\|" + senseid[ii].split("|")[2] + "\\}\\}")
}
wikitext = wikitext.replace(RegExp("(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}" + findnumberofdefs + "[\\s\\S]*?=" + nymtype + "=+[\\s\\S]*?\n" + (passthesesenseids.length > 0 ? "(?!(" + passthesesenseids.join("|") + ")))" : ")")), "$1* {" + "{sense|" + senseid[i].split("|")[2] + "}} " + addedwikitext + "\n")
} else {
wikitext = wikitext.replace(RegExp("(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}[\\s\\S]*?=" + nymtype + "=+[\\s\\S]*?\\{\\{sense\\|" + senseid[i].split("|")[2] + "\\}\\}.*)"), "$1" + addedwikitext)
}
return ccc = wikitext;
},
redo: function() {
theform.parentNode.parentNode.insertBefore(nodeToInsert, theform.parentNode)
},
undo: function() {
theform.parentNode.parentNode.removeChild(nodeToInsert)
},
summary: "+" + nymtype2 + " [[" + values.nym + "]]"
}, nodeToInsert)
})
}
}, x)
// sort the lis
var passthesesenseids = [];
for (var ii = 0; ii < i; ii++) {
passthesesenseids.push(senseid[ii].split("|")[2])
}
for (var xx = x.parentNode.parentNode.nextSibling; xx.nextSibling && !/h\d/i.test(xx.nodeName) && xx.className != "fakesubheader" && xx.nodeName.toUpperCase() != "UL"; xx = xx.nextSibling);
if (xx.nodeName.toUpperCase() == "UL") {
var xxx = xx.getElementsByTagName('li')
for (var xxxx = xxx.length; xxxx--;) {
var xxxxx = xxx[xxxx].getElementsByTagName("span")[3]
if (xxxxx && xxxxx.innerHTML == senseid[i].split("|")[2]) {
x.parentNode.parentNode.removeChild(x.parentNode);
xxx[xxxx].appendChild(x);
break;
}
if (xxxxx && passthesesenseids.length && RegExp(passthesesenseids.join("|")).test(xxxxx.innerHTML)) {
xxx[xxxx + 1] ? xx.insertBefore(x.parentNode, xxx[xxxx]) : xx.appendChild(x.parentNode);
break;
}
}
if (xxxx == -1) {
xx.insertBefore(x.parentNode, xx.firstChild)
}
}
}
}
jQuery(document).ready(function() {
var disablebutton = document.getElementById('YRDefsideboxes-disable-button');
if (disablebutton) {
disablebutton.innerHTML = "";
var toggle = newNode('span', {
click: function() {
if ($.cookie("DisableYREdit")) {
$.cookie("DisableYREdit", null, {
path: "/"
})
toggle.innerHTML = "Disable definition editing options."
} else {
$.cookie("DisableYREdit", 1, {
expires: 30,
path: "/"
})
toggle.innerHTML = "Enable definition editing options."
}
}
}, ($.cookie("DisableYREdit") ? "Enable" : "Disable") + " definition editing options.")
disablebutton.appendChild(toggle);
}
if (mw.config.get('wgAction') == 'view' && (mw.config.get('wgNamespaceNumber') == 0 || mw.config.get('wgPageName') == "Wiktionary:Sandbox") && !/&printable=yes|&diff=|&oldid=/.test(window.location.search) && !$.cookie("DisableYREdit") && !window.DisableYREdit && !window.loadedYREdit) {
window.loadedYREdit = true;
for (var ols = document.getElementsByTagName('ol'), ii = 0; ii < ols.length; ii++) {
if (ols[ii].className != "references") {
for (var lis = ols[ii].getElementsByTagName('li'), i = 0; i < lis.length; i++) {
if (lis[i].parentNode == ols[ii] && ols[ii].parentNode.parentNode.nodeName != "UL") makedefsidebox(lis[i]);
}
}
}
mw.util.addPortletLink('p-tb', 'javascript:addPOSHeader()', 'Add part of speech');
defsidebuttons = [0]
defeditoptions = {
addingPOS: false
}
}
})
// </nowiki>
8oh79o98tyjo7ss59excfxpgp5m9e4o
89016
89015
2026-08-06T23:33:51Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-DefSideBoxes.js]]
89015
javascript
text/javascript
// Newer version of User:Yair rand/editor.js
// <nowiki>
// ******** imported from [[MediaWiki:Gadget-LegacyScriptsNewNode.js]] ********
var newNode = window.newNode = function newNode(tagname) {
var node = document.createElement(tagname);
for (var i = 1; i < arguments.length; i++) {
var argument = arguments[i];
if (typeof argument == 'string') { //Text
node.appendChild(document.createTextNode(argument));
} else if (typeof argument == 'object') {
if (argument instanceof Node) { // If it is a DOM Node
node.appendChild(argument);
} else { // Attributes (hopefully)
for (var j in argument) {
if (j === 'class') { // Classname different because...
node.className = argument[j];
} else if (j == 'style') { // Style is special
node.style.cssText = argument[j];
} else if (typeof argument[j] == 'function') { // Basic event handlers
node.addEventListener(j, argument[j], false);
} else {
node.setAttribute(j, argument[j]); //Normal attributes
}
}
}
}
}
return node;
};
// ****************************************************************************
var autoTranslitScripts = {
"Geor": true
};
window.makedefsidebox = function(q) {
function clickiconfunction() {
var moreoptions, originalHeight, buttons;
function uncollapse() {
$(moreoptions).slideUp();
$("body").off("click", uncollapse);
}
editdefinition(q);
if ($('>.addusexbutton', q).length === 0) {
buttons = q.insertBefore(newNode('div', {
'class': 'addusexbutton'
}, newNode('a', '+Add example sentence', {
'click': function() {
setupusexadder(q);
}
}), newNode('a', '+Add quotation', {
'click': function() {
setupquoteadder(q);
}
}), newNode('a', 'More ►', {
'click': function() {
$(moreoptions).slideDown();
setTimeout(function() {
$("body").click(uncollapse);
}, 16);
}
}), newNode('span', {
'style': 'display:inline-block;'
}, moreoptions = newNode('ul', newNode('li', q.className ? newNode('a', 'Edit gloss', {
'click': function() {
editgloss(q);
}
}) : newNode('a', 'Add gloss', {
'click': function() {
addgloss(q);
}
})), newNode('li', newNode('a', q.className ? undefined : {
'class': 'DSBinactive',
'title': 'Please add a gloss before adding synonyms.'
}, {
'click': function() {
setupnymadder(q, "Synonyms");
}
}, 'Add synonyms')), newNode('li', newNode('a', q.className ? undefined : {
'class': 'DSBinactive',
'title': 'Please add a gloss before adding antonyms.'
}, {
'click': function() {
setupnymadder(q, "Antonyms");
}
}, 'Add antonyms')), newNode('li', newNode('a', {
'href': mw.config.get('wgScript') + '?title=User_talk:Yair_rand/editor2.js&action=edit§ion=new'
}, 'Give feedback on this tool')), newNode('li', newNode('a', {
click: function() {
$.cookie("DisableYREdit", 1, {
path: '/'
});
location.href = location.href;
}
}, 'Disable this tool'))))), $('>dl,>ul,>ol', q)[0] || null);
}
}
q.insertBefore(
newNode('span', {
'class': 'defEditButton'
}, newNode('span', {
'click': clickiconfunction,
'title': 'Edit'
})), q.firstChild);
return clickiconfunction;
}
function setupusexadder(qq) {
var x, y;
var usexform;
var findsubdef = (qq.parentNode.parentNode.nodeName.toLowerCase() == "li" ? "#" : "") + (qq.parentNode.parentNode.parentNode.parentNode.nodeName.toLowerCase() == "li" ? "#" : "")
editor = new Editor()
findlang(qq.parentNode, function(findlang_) {
var findscript = (new LangMetadata()).getScripts(findlang_),
selectedscript = findscript[0] || "Latn";
if (qq.getElementsByTagName('dl')[0] && qq.getElementsByTagName('dl')[0].parentNode == qq) x = qq.getElementsByTagName('dl')[0];
else if ($(">ul", qq).length > 0) x = qq.insertBefore(newNode('dl'), $(">ul", qq)[0]);
else x = qq.appendChild(newNode('dl'));
x.appendChild(y = newNode('dd'))
new AdderWrapper(new Editor(), {
'createForm': function() {
var x, i = 0,
y = function() {
selectedscript = this.value;
usexform.getElementsByTagName('dd')[0].style.display = this.value == 'Latn' ? 'none' : 'block'
}
if (findscript.length > 1) {
for (x = newNode('select', {
'name': 'script',
'change': y
}); i < findscript.length; i++) {
x.appendChild(newNode('option', {
'value': findscript[i]
}, findscript[i]))
}
} else {
x = newNode('input', {
'name': 'script',
'value': findscript[0] || "Latn",
'style': "width:50px;",
'change': y
})
}
return usexform = newNode('form', {
'style': 'display:inline;'
}, newNode('input', {
'size': 100,
'placeholder': 'Example sentence',
'name': 'examplesentence'
}), newNode('span', {
'style': 'display:' + (findscript.length == 1 ? "none" : "inline") + ";"
}, 'Script: ', x), newNode('div', {
'class': "DSBRedButton",
'click': function() {
usexform.style.display = 'none'
}
}, newNode('div', 'Cancel')), newNode('div', {
'class': "DSBGreenButton",
'click': function() {
usexform.onsubmit()
}
}, newNode('div', "Preview ►")), newNode('input', {
'type': 'submit',
'style': 'visibility:hidden;width:0px;'
}), newNode('dl', (findlang_ == "en" ? {
'style': 'display:none;'
} : ''), newNode('dd', 'Transliteration: ', ((findscript[0] == 'Latn' || !findscript[0]) ? {
'style': 'display:none;'
} : ''), newNode('input', {
'size': '100px',
name: 'transliteration'
})), newNode('dd', 'Translation: ', newNode('input', {
'size': '100px',
name: 'translation'
}))))
},
'fields': {
'examplesentence': function(txt, error) {
return util.validateNoWikisyntax('example sentence', true)(txt, error)
},
'transliteration': function(txt, error) {
return txt || '';
},
'translation': function(txt, error) {
if (findlang_ != "en") {
return util.validateNoWikisyntax('translation', true)(txt, error);
}
return '';
},
'script': util.validateNoWikisyntax('script', true)
},
'onsubmit': function(values, render) {
var renderWikitext = "";
if (findlang_ === "en")
renderWikitext = "''" + values.examplesentence + "''";
else {
renderWikitext = "{" + "{ux|" + findlang_ + "|" + values.examplesentence + "|" + values.translation;
if (values.script) {
renderWikitext += "|sc=" + values.script;
if (!(values.script in autoTranslitScripts) && values.transliteration)
renderWikitext += "|tr=" + values.transliteration;
}
renderWikitext += "}}";
}
render(renderWikitext, function(newhtml) {
var findnumberofheaders = findnumberofheaders_(qq);
var findnumberofdefs = findnumberofdefs_(qq);
var usextobeadded = newNode('span')
usextobeadded.innerHTML = newhtml.replace(/<p>|<\/p>/g, '');
editor.addEdit({
edit: function(wikitext) {
var usexWikitext = renderWikitext;
wikitext = wikitext.replace(RegExp(
"(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}" + findnumberofdefs + "[\\s\\S]*?)\n(?!#" + findsubdef + ":)|$"), "$1\n#" + findsubdef + ":" + usexWikitext + "\n");
ccc = wikitext;
return wikitext;
},
redo: function() {
usexform.parentNode.appendChild(usextobeadded);
usextobeadded.style.display = 'block';
usexform.style.display = "none";
y.style.display = 'block'
},
undo: function() {
usexform.parentNode.removeChild(usextobeadded);
usexform.style.display = "inline";
},
summary: "+usex: " + values.examplesentence
}, usextobeadded)
})
}
}, y)
usexform.firstChild.focus()
})
}
window.editdefinition = function(qq) {
(new Editor).withCurrentText(function(wikitext) {
if (qq.YREditing) {
return
}
qq.YREditing = true;
var findnumberofheaders = findnumberofheaders_(qq),
findnumberofdefs = findnumberofdefs_(qq)
var temporarydefholder = qq.insertBefore(newNode('span', {
'style': 'display:none;'
}), qq.childNodes[1].nodeName.toLowerCase() != "form" ? qq.childNodes[1] : qq.childNodes[2])
while (qq.childNodes[3] && qq.childNodes[3].nodeName != "DL" && qq.childNodes[3].nodeName != "UL" && qq.childNodes[3].nodeName != "OL" && qq.childNodes[3].className != "HQToggle" && qq.childNodes[3].className != "addusexbutton")
temporarydefholder.appendChild(qq.childNodes[3]);
if (qq.childNodes[2] != temporarydefholder) temporarydefholder.insertBefore(qq.childNodes[2], temporarydefholder.firstChild);
var defform
var olddef = wikitext.replace(RegExp("(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}" + findnumberofdefs + "\\s?)"), '').match(/^(\{\{senseid\|[^\}]*\}\})?\s?(.*)/)[2]
new AdderWrapper(new Editor(), {
'createForm': function() {
return defform = newNode('form', {
'style': 'width:100%;display:inline-block;margin-bottom:-6px;'
}, newNode('table', {
'style': 'width:100%;'
}, newNode('tr', newNode('td', newNode('input', {
'style': 'height:17px;width:100%;',
'name': 'definition',
'size': 80,
'value': olddef
})
// newNode('input',{type:'submit',value:'Preview'}),
// newNode('input',{type:'button',value:'Cancel',click:function(){temporarydefholder.style.display='inline';defform.style.display='none';qq.YREditing = false}})
), newNode('td', {
'style': 'white-space:nowrap;'
}, newNode('div', {
'class': "DSBRedButton"
}, newNode('div', {
click: function() {
temporarydefholder.style.display = 'inline';
defform.style.display = 'none';
qq.YREditing = false
}
}, "Discard Changes")), newNode('div', {
'class': "DSBGreenButton"
}, newNode('div', {
click: function() {
defform.onsubmit()
}
}, "Preview Changes ►"))))))
},
'fields': {
'definition': function(txt, error) {
return txt || error("Please specify a definition.")
}
},
'onsubmit': function(values, render) {
render(values.definition, function(newhtml) {
editor = new Editor()
var findnumberofdefs = findnumberofdefs_(qq) // apparently this is sometimes necessary
var addedspan = newNode('span')
addedspan.innerHTML = newhtml
var updatecatscallback;
editor.addEdit({
edit: function(wikitext) {
wikitext = wikitext.replace(RegExp("(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}" + findnumberofdefs + "\\s?(\{\{senseid\\|[^\}]*\}\})*?)(?!{" + "{senseid\\|).*"), "$1" + values.definition);
ccc = wikitext;
return wikitext
},
redo: function() {
defform.parentNode.insertBefore(addedspan, defform);
defform.style.display = temporarydefholder.style.display = 'none';
updatecatscallback = window.tabbedLanguages && updateCategories(findtopli(defform.parentNode).parentNode.parentNode, values.definition, olddef)
qq.YREditing = false;
},
undo: function() {
defform.parentNode.removeChild(addedspan);
defform.style.display = "inline";
defform.firstChild.firstChild.firstChild.firstChild.value = olddef;
updatecatscallback && updatecatscallback();
qq.YREditing = true;
},
summary: "def: " + values.definition
}, addedspan)
})
}
}, qq, qq.childNodes[1].nodeName.toLowerCase() != "form" ? qq.childNodes[1] : qq.childNodes[2])
})
}
function findtopli(li) {
for (; li.parentNode.parentNode.nodeName.toLowerCase() == "li";) {
li = li.parentNode.parentNode;
}
return li;
}
function findnumberofheaders_(tosearchfrom) {
tosearchfrom = findtopli(tosearchfrom).parentNode;
while (!tosearchfrom.nodeName.match(/^h\d$/i))
tosearchfrom = tosearchfrom.previousSibling;
return Number(tosearchfrom.lastChild.getElementsByTagName('a')[0].href.match(/\d*$/))
}
function findnumberofdefs_(tosearchfrom) {
var findnumberofdefs = [],
tosearchfrom_, findnumberofpredefs = 0,
result;
// subdefinitions
while (tosearchfrom.nodeName.toLowerCase() == "li") {
tosearchfrom_ = tosearchfrom;
findnumberofdefs.splice(0, 0, 0);
while (tosearchfrom) {
if (tosearchfrom.nodeName.toLowerCase() == "li") findnumberofdefs[0]++;
tosearchfrom = tosearchfrom.previousSibling;
}
tosearchfrom = tosearchfrom_.parentNode.parentNode;
}
// in case of any def lists before the current def list under the same header
for (tosearchfrom_ = tosearchfrom_.parentNode.previousSibling; !tosearchfrom_.nodeName.match(/^h\d$/i); tosearchfrom_ = tosearchfrom_.previousSibling) {
if (tosearchfrom_.nodeName.toLowerCase() == "ol") findnumberofpredefs += 1;
}
result = "([\\s\\S]*?\n#[\\s\\S]*?\n(?!#)){" + findnumberofpredefs + "}";
for (var i = 0; i < findnumberofdefs.length; i++) {
result += "([\\s\\S]*?\n" + Array(i + 2).join("#") + "(?![:*#])){" + findnumberofdefs[i] + "}";
}
return result;
}
function findlang(tosearchfrom, callback) {
var tosearchfrom_, findlang__
if (tosearchfrom.parentNode.nodeName.toLowerCase() == "li") tosearchfrom = findtopli(tosearchfrom.parentNode).parentNode;
if (tosearchfrom.parentNode.className == "languageContainer") {
if (tosearchfrom.parentNode.id == "Englishcontainer") return callback("en");
} else {
tosearchfrom_ = tosearchfrom;
while (tosearchfrom_.nodeName.toLowerCase() != "h2")
tosearchfrom_ = tosearchfrom_.previousSibling;
if (tosearchfrom_.lastChild.innerHTML == "English") return callback("en");
}
tosearchfrom_ = tosearchfrom;
while (tosearchfrom_ && !/^h\d$/i.test(tosearchfrom_.nodeName) && tosearchfrom_.className != "fakeheader")
tosearchfrom_ = tosearchfrom_.previousSibling;
while (tosearchfrom_ && tosearchfrom_.nodeName.toLowerCase() != "p" && tosearchfrom_.className != "fakeheader")
tosearchfrom_ = tosearchfrom_.nextSibling;
if (tosearchfrom_ && tosearchfrom_.className != "fakeheader" && tosearchfrom_.firstChild && (tosearchfrom_.firstChild.lang || (tosearchfrom_.firstChild.firstChild && tosearchfrom_.firstChild.firstChild.lang) || (tosearchfrom_.firstChild.firstChild && tosearchfrom_.firstChild.firstChild.firstChild && tosearchfrom_.firstChild.firstChild.firstChild.lang))) {
return callback(tosearchfrom_.firstChild.lang || tosearchfrom_.firstChild.firstChild.lang || tosearchfrom_.firstChild.firstChild.firstChild.lang)
}
if (tosearchfrom.parentNode.className == "languageContainer") findlang__ = tosearchfrom.parentNode.id.split("container")[0];
else {
while (tosearchfrom.nodeName.toLowerCase() != "h2")
tosearchfrom = tosearchfrom.previousSibling;
findlang__ = (findlang__ = tosearchfrom.getElementsByTagName("span")[0]).innerText || findlang__.textContent
}
window.languagecodefinder_preload = window.languagecodefinder_preload || mw.loader.getScript('https://en.wiktionary.org/w/index.php?title=User:Yair_rand/languagecodefinder.js&action=raw&ctype=text/javascript');
window.languagecodefinder_preload.always( function() {
window.languagenametocode && languagenametocode[findlang__] && callback(languagenametocode[findlang__])
});
}
function setupquoteadder(q) {
findlang(q.parentNode, function(findlang_) {
var z;
if (!$(".HQToggle a", q)[0]) {
z = q.appendChild(document.createElement("ul"))
setupHiddenQuotes(q);
} else {
z = $(">ul", q)[0];
}
if (z.style.display == 'none') {
$(".HQToggle a", q).click();
}
var zz = z.appendChild(document.createElement("li")),
quoteform;
var findsubdef = q.parentNode.parentNode.nodeName.toLowerCase() == "li" ? "#" : "",
editor = new Editor();
new AdderWrapper(editor, {
'createForm': function() {
return quoteform = newNode('form', {
'style': 'display:inline;'
}, 'Year: ', newNode('input', {
'name': 'year',
'size': 4
}), ' Author: ', newNode('input', {
'name': 'author',
'size': 20
}), ' Source title: ', newNode('input', {
'name': 'source',
'size': 20
}), ' Publisher: ', newNode('input', {
'name': 'publisher',
'size': 20
}), ' Page(s): ', newNode('input', {
'name': 'page',
'size': 5
}), newNode('dl', newNode('dd', 'Quotation: ', newNode('input', {
'name': 'quotation',
'size': 40
}), newNode('div', {
'class': "DSBRedButton",
'click': function() {
z.removeChild(zz)
}
}, newNode('div', 'Cancel')), newNode('div', {
'class': "DSBGreenButton",
'click': function() {
quoteform.onsubmit()
}
}, newNode('div', "Preview ►")), newNode('input', {
'type': 'submit',
'style': 'visibility:hidden;width:0px;'
}), newNode('dl', {
'style': 'display:' + (findlang_ == "en" ? "none" : "block")
}, newNode('dd', 'Translation: ', newNode('input', {
'size': 40,
'name': 'translation'
}))))))
},
'fields': {
'quotation': util.validateNoWikisyntax('quotation', true),
'year': util.validateNoWikisyntax('year', true),
'author': function(t) {
return t
},
'source': function(t) {
return t ? ", ''" + t + "''" : ''
},
'publisher': function(t) {
return t ? ", " + t : ''
},
'page': function(t) {
return t ? (/\-/.test(t) ? "pages " + t : "page " + t) : t
},
'translation': function(t) {
return t
}
},
'onsubmit': function(values, render) {
var topline = "'''" + values.year + "'''" + (values.author ? ", " + values.author : '') + values.source + values.publisher + (values.page ? ", " + values.page : '')
render(topline + "\n:" + values.quotation + (values.translation ? "\n:" + values.translation : ""), function(newhtml) {
var findnumberofheaders = findnumberofheaders_(q),
findnumberofdefs = findnumberofdefs_(q),
quotetobeadded = newNode('span', {
'style': 'display:block;'
});
quotetobeadded.innerHTML = newhtml.replace(/\<p\>|\<\/p\>/g, '');
editor.addEdit({
'edit': function(wikitext) {
return ccc = wikitext.replace(RegExp("(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}" + findnumberofdefs + "[\\s\\S]*?)\n(?!#" + findsubdef + "[:\*])|$"), "$1\n#" + findsubdef + "*" + topline + "\n#" + findsubdef + "*:" + values.quotation + (values.translation ? "\n#" + findsubdef + "*:" + values.translation : '') + '\n')
},
'redo': function() {
quoteform.style.display = 'none';
quotetobeadded.style.display = 'block';
zz.appendChild(quotetobeadded)
},
'undo': function() {
quoteform.style.display = 'inline';
zz.removeChild(quotetobeadded)
},
'summary': '+quote: "' + values.quotation + '"- ' + values.source
}, quotetobeadded)
})
}
}, zz)
})
}
window.addPOSHeader = function(cancelhover) {
var bodyContent = $(".mw-content-ltr")[0];
if (!defeditoptions.addingPOS) {
defeditoptions.addingPOS = true;
editor = new Editor()
var headerLevel = (window.languageContainers && languageContainers[currentLanguageTab] && languageContainers[currentLanguageTab].firstChild) || bodyContent.firstChild;
var ii;
var temp, temp_;
var theform;
var findnumberofheaders;
var addedwikitext;
var prefakeheaders;
var currentFocus;
var langNames = [],
docLink, docExists = {};
if (window.loadedTabs) {
langNames = tabbedLanguages;
} else {
mw.util.$content.find("h2>.mw-headline").each(function() {
langNames.push($(this).text())
})
}
var langNameCodes;
new mw.Api().get({
"action": "expandtemplates",
"format": "json",
"text": "{{#invoke:languages/javascript-interface|GetAllData}}",
"prop": "wikitext"
}).done(function(r){
if(r.expandtemplates && r.expandtemplates.wikitext){
langNameCodes = JSON.parse(r.expandtemplates.wikitext);
}
});
function updateDocLink() {
var pos = theform.getElementsByTagName("input")[0].value;
pos = (({
Adjective: "adj",
Adverb: "adv",
Interjection: "interj",
Preposition: "prep",
Pronoun: "pron",
Conjunction: "con",
Contraction: "cont",
Determiner: "det"
})[pos] || pos.toLowerCase());
if (!langNameCodes || !pos || !theform.getElementsByTagName("input")[0].value) {
return;
}
var currentLang = window.loadedTabs ? theform.parentNode.id.split("container")[0] : (function() {
for (var s = theform; s && s.nodeName != "H2"; s = s.previousSibling);
return s && s.lastChild && (s.lastChild.innerText || s.lastChild.textContent)
})();
var docName = langNameCodes[currentLang] + "-" + pos;
if (docExists[docName] === false) {
return docLink.style.display = 'none';
}
if (docExists[docName] === true) {
docLink.style.display = 'inline';
docLink.href = mw.config.get("wgArticlePath").replace("$1", "Template:" + docName + "/doc");
return;
}
if (docExists[docName] === 'l') {
docLink.style.display = 'none';
return;
}
docExists[docName] = 'l';
new mw.Api().get({
action: 'query',
prop: 'info',
titles: 'Template:' + docName + '/documentation'
}).then(function(r) {
docExists[docName] = r.query.pages.length == 1;
updateDocLink();
});
}
var poslist = '\nNoun\nVerb\nAdjective\nAdverb\nPronoun\nConjunction\nInterjection\nPreposition\nProper noun\nArticle\nPrepositional phrase\nAcronym\nAbbreviation\nInitialism\nContraction\nPrefix\nSuffix\nSymbol\nLetter\nLigature\nIdiom\nPhrase\nNumber\nNumeral\nCardinal number\nOrdinal number\nCardinal numeral\nOrdinal numeral\nPersonal pronoun\nDeterminer\nDemonstrative determiner\nClitic\nInfix\nInterfix\nAffix\nCircumfix\nCircumposition\nCounter\nKanji\nKanji reading\nHanja reading\nHirigana letter\nKatakana letter\nPinyin\nHan character\nHanzi\nHanja\nProverb\nExpression\nParticle\nInfinitive particle\nPossesive adjective\nPostposition\nPhrasal verb\nParticiple\nGerund\nInterrogative auxiliary verb\nPronominal adverb\nAdnominal\nAbstract pronoun\nConjunction particle\nCombining form\nRoot\nPinyin syllable\nSyllable\nHirigana character\nKatakana character\nJyutping syllable\nGismu\nLujvo\nBrivla\nClassifier\nPredicative\nMeasure word\nCorrelative\nPreverb\nPrenoun\nNoun stem\nNoun class\nCombined-kana character\nCmavo\n';
while (headerLevel.nextSibling) {
if (/^h[34]$/i.test(headerLevel.nodeName) && !/Etymology|Pronunciation|Alternative\sforms/.test(headerLevel.innerHTML)) {
headerLevel = headerLevel.nodeName.toLowerCase();
break;
}
headerLevel = headerLevel.nextSibling
}
currentBoxToBeAdded = newNode('div', {
'style': 'position:fixed;top:500px;'
})
new AdderWrapper(editor, {
createForm: function() {
return theform = newNode('form', currentFocus = newNode('input', {
'style': 'font-size: 0.8em;font-size: ' + ((headerLevel == 'h3') ? '132%' : '116%') + ';font-weight: bold;font-family: sans-serif;width:auto;',
'placeholder': 'Part of speech',
'rows': 1,
'name': 'pos',
focus: function() {
currentFocus = this
}
}), newNode('input', {
'type': 'submit',
'value': 'Preview'
}), newNode('input', {
'type': 'button',
'value': 'Cancel',
click: function() {
theform.style.display = 'none';
hoverNewPOS(null);
defeditoptions.addingPOS = false;
document.onmousemove = null;
document.body.style.cursor = '';
}
}), newNode('br'), newNode('input', {
'style': 'width:300px;font-family:monospace;',
'placeholder': '{\{head}}',
'name': 'inflline',
focus: function() {
currentFocus = this
}
}), docLink = newNode('a', {
style: "margin-left:5px;display:none;",
'target': '_blank'
}, "Help?"), newNode('ol', newNode('li', newNode('input', {
'size': 100,
'disabled': 'disabled'
}))))
},
fields: {
'pos': function(txt, error) {
return poslist.indexOf('\n' + txt + '\n') > -1 ? txt : error('Please select a valid part of speech.')
},
'inflline': function(txt, error) {
return txt || "{" + "{head}}"
}
},
onsubmit: function(values, render) {
addedwikitext = '{' + '{subst:=}}==' + (headerLevel == 'h4' ? '=' : '') + values.pos + (headerLevel == 'h4' ? '=' : '') + '===\n' + values.inflline
hoverNewPOS(null);
document.onmousemove = null;
defeditoptions.addingPOS = false;
document.body.style.cursor = '';
render(addedwikitext, function(res) {
theform.style.display = 'none'
temp = document.createDocumentFragment();
temp.appendChild(temp_ = newNode('div', {
'class': 'fakeheader'
}));
temp_.innerHTML = res;
temp.appendChild(newNode('ol', newNode('li', ' ', newNode('span'))));
theform.parentNode.insertBefore(temp, theform);
prefakeheaders = 0
findnumberofheaders = theform.previousSibling.previousSibling;
while (true) {
if (findnumberofheaders.previousSibling) {
findnumberofheaders = findnumberofheaders.previousSibling;
if (/h[2-6]/i.test(findnumberofheaders.nodeName)) {
findnumberofheaders = Number(findnumberofheaders.lastChild.getElementsByTagName('a')[0].href.split("section=")[1]);
break;
}
findnumberofheaders.className == 'fakeheader' && prefakeheaders++;
} else {
findnumberofheaders.parentNode.insertBefore(newNode('h2', languageHeaderEditButtons[currentLanguageTab].cloneNode(true), {
'style': 'display:none;'
}), findnumberofheaders);
}
}
var updatecatscallback;
editor.addEdit({
'edit': function(wikitext) {
return ccc = wikitext.replace(RegExp("(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}([\\s\\S]*?\n\\{\\{subst:\\=\\}\\}==" + (headerLevel == 'h4' ? '=' : '') + "[^=]){" + prefakeheaders + "}[\\s\\S]*?(?=(\n=|\n----|\n\\{\\{subst:\\=\\}\\}==" + (headerLevel == 'h4' ? '=' : '') + "[^=]|$)))"), "$1\n" + addedwikitext + "\n\n# {" + "{rfdef}}\n")
},
'redo': function() {
theform.style.display = 'none';
theform.parentNode.insertBefore(temp, theform);
updatecatscallback = window.tabbedLanguages && updateCategories(theform.parentNode, addedwikitext)
},
'undo': function() {
theform.style.display = 'block';
temp.appendChild(temp_);
temp.appendChild(theform.previousSibling);
updatecatscallback && updatecatscallback()
},
'summary': '+pos ' + values.pos
}, temp_)
makedefsidebox(theform.previousSibling.firstChild)();
editor.withCurrentText(function() {
theform.previousSibling.getElementsByTagName('input')[0].value = '';
theform.previousSibling.getElementsByTagName('input')[0].focus()
})
})
}
}, currentBoxToBeAdded)
cancelhover || setUpBoxToBeAdded();
theform.firstChild.focus()
function hoverNewPOS(thefunction) {
for (var ii = bodyContent.getElementsByTagName(headerLevel), i = 0; i < ii.length; i++) {
if (!/^Etymology|Pronunciation|Alternative\sforms$/.test(ii[i].firstChild.innerHTML))(ii[i].parentNode.className == 'fakeheader' ? ii[i].parentNode : ii[i]).onmouseover = thefunction;
}
if (window.tabbedLanguages) {
for (var i = 0; i < languageContainers.length; i++)
languageContainers[i].lastChild.onmouseover = thefunction;
} else {
ii = bodyContent.getElementsByTagName('hr');
for (var i = 0; i < ii.length; i++)
ii[i].onmouseover = thefunction;
}
}
function hoverNewPOS_() {
currentBoxToBeAdded.style.display = 'none';
this.parentNode.insertBefore(theform, this);
currentFocus.focus();
updateDocLink()
}
cancelhover || hoverNewPOS(hoverNewPOS_);
// autocomplete pos field
theform.firstChild.onkeydown = function(e) {
if ((e || event).keyCode == 9) {
theform.getElementsByTagName("input")[3].focus();
return false
}
}
theform.firstChild.onkeyup = function(e) {
if (this.value[0] != this.value.charAt(0).toUpperCase()) this.value = this.value.charAt(0).toUpperCase() + this.value.substr(1);
e = (e || event).keyCode;
if (!(e >= 33 && e <= 40) && e != 8 && e != 46 && e != 27 && e != 16 && this.value) {
if (poslist.indexOf('\n' + this.value) > -1) {
if (this.setSelectionRange) this.setSelectionRange([this.value.length, this.value = poslist.match(RegExp(this.value + "[^\n]*"))][0], this.value.length);
else if (this.createTextRange) {
var z = this.createTextRange();
z.moveEnd('character', 0 - z.move('character', [this.value.length, this.value = poslist.match(RegExp(this.value + "[^\n]*"))][0]) + this.value.length);
z.select()
}
}
updateDocLink()
}
}
}
}
window.updateCategories = function(container, newvalue, oldvalue) {
var addedCategories = [],
removedCategories = []
new mw.Api().get({
action: "parse",
prop: "categorieshtml",
title: mw.config.get('wgPageName'),
text: newvalue
}).then(function(r) {
var e = r.parse.categorieshtml['*'].match(/\<li.*?\/li\>/g);
var d = container.lastChild.lastChild;
var f = document.createElement('ul');
for (var i = 0; e && i < e.length; i++) {
f.innerHTML = e[i];
if (d.innerHTML.indexOf(f.firstChild.innerHTML) == -1) {
addedCategories.push(d.insertBefore(f.lastChild, d.lastChild))
}
}
oldvalue && new mw.Api().get({
action: "parse",
prop: "categorieshtml",
title: mw.config.get('wgPageName'),
text: oldvalue
}).then(function(o) {
e = o.parse.categorieshtml['*'].match(/\<li.*?\/li\>/g)
for (var i = 0; e && i < e.length; i++) {
f.innerHTML = e[i];
if (r.parse.categorieshtml['*'].indexOf(f.firstChild.innerHTML) == -1 && d.innerHTML.indexOf(f.firstChild.innerHTML) != -1) {
var lis = d.getElementsByTagName('li')
for (var ii = 0; ii < lis.length; ii++) {
if (lis[ii].firstChild.innerHTML == f.firstChild.firstChild.innerHTML) {
removedCategories.push(lis[ii]);
lis[ii].style.display = 'none';
break;
}
}
}
}
})
});
return function() {
for (var i = 0; i < addedCategories.length; i++) {
container.lastChild.lastChild.removeChild(addedCategories[i])
}
for (var i = 0; i < removedCategories.length; i++) {
removedCategories[i].style.display = '';
}
}
}
function addgloss(qq) {
var senseidform;
findlang(qq.parentNode, function(findlang_) {
if (qq.addinggloss) {
return
}
qq.addinggloss = true;
editor = new Editor();
new AdderWrapper(editor, {
'createForm': function() {
return senseidform = newNode('form', {
'style': 'display:inline; padding-right:20px;'
}, newNode('input', {
'size': 30,
'name': 'senseid'
}), newNode('input', {
'type': 'submit',
'value': 'Add'
}), newNode('input', {
'type': 'button',
'value': 'Cancel',
'click': function() {
qq.removeChild(senseidform);
qq.addinggloss = false
}
}), newNode('a', {
href: '/wiki/Help:Glosses'
}, 'Help?!'))
},
'fields': {
'senseid': util.validateNoWikisyntax('gloss', true)
},
'onsubmit': function(values) {
var findnumberofheaders = findnumberofheaders_(qq)
var findnumberofdefs = findnumberofdefs_(qq)
senseidform.lastChild.innerHTML = '';
var nymB = qq.getElementsByTagName('li')[1]
editor.addEdit({
edit: function(wikitext) {
wikitext = wikitext.replace(RegExp("(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}" + findnumberofdefs + ") ?"), "$1{" + "{senseid|" + findlang_ + "|" + values.senseid + "}}");
return ccc = wikitext;
},
redo: function() {
senseidform.style.display = "none";
$([nymB.firstChild, nymB.nextSibling.firstChild]).removeClass('DSBinactive');
nymB.firstChild.title = nymB.nextSibling.firstChild.title = ''
},
undo: function() {
senseidform.style.display = "inline";
$([nymB.firstChild, nymB.nextSibling.firstChild]).addClass('DSBinactive');
nymB.firstChild.title = 'Please add a gloss before adding synonyms.';
nymB.nextSibling.firstChild.title = 'Please add a gloss before adding antonyms.'
},
summary: "+senseid: " + values.senseid
})
}
}, qq, qq.childNodes[1])
senseidform.firstChild.focus()
editor.withCurrentText(function(wikitext) {
wikitext = wikitext.match("==" + (window.loadedTabs ? tabbedLanguages[currentLanguageTab] : $(findtopli(qq).parentNode).prevUntil("h2").eq(-1).prev().find(".mw-headline").text()) + "==\n[\\s\\S]+?(----|$)")[0];
if (!wikitext) return;
var senses = '',
matches = wikitext.match(/(\{\{sense\||\{\{trans-top\|)[^\}]+(?=\}\})/g)
if (!matches) return;
for (var ms = 0; ms < matches.length; ms++) {
senses += "\n" + matches[ms].split("|")[1]
}
if (!senses) return;
senseidform.firstChild.onkeyup = function(e) {
e = (e || event).keyCode;
if (!(e >= 33 && e <= 40) && e != 8 && e != 46 && e != 27 && e != 16 && this.value) {
if (senses.indexOf('\n' + this.value) > -1) {
if (this.setSelectionRange) this.setSelectionRange([this.value.length, this.value = senses.match(RegExp("\n" + this.value + "[^\n]*")).toString().substr(1)][0], this.value.length);
else if (this.createTextRange) {
var z = this.createTextRange();
z.moveEnd('character', 0 - z.move('character', [this.value.length, this.value = senses.match(RegExp("\n" + this.value + "[^\n]*")).toString().substr(1)][0]) + this.value.length);
z.select()
}
}
}
}
})
})
}
function editgloss(qq) {
editor = new Editor()
editor.withCurrentText(function(wikitext) {
if (qq.editinggloss) {
return
}
qq.editinggloss = true;
var findnumberofheaders = findnumberofheaders_(qq)
var findnumberofdefs = findnumberofdefs_(qq)
var senseidform;
var oldsenseid;
new AdderWrapper(editor, {
'createForm': function() {
var thisId = "a" + String(Math.random()).replace(".", "")
oldsenseid = wikitext.replace(RegExp("(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}" + findnumberofdefs + "\\s?)"), '').match(/^\{\{senseid\|[^\\|]*\|([^\}]*)/)[1]
return senseidform = newNode('form', {
'style': 'display:inline; padding-right:20px; '
}, newNode('input', {
'size': 30,
'name': 'senseid',
'value': oldsenseid
}), newNode('input', {
'type': 'checkbox',
'id': thisId,
'name': 'changematching',
style: 'display:' + (window.tabbedLanguages ? 'inline' : 'none')
}), newNode('label', {
'for': thisId,
style: 'display:' + (window.tabbedLanguages ? 'inline' : 'none')
}, 'Edit matching glosses'), newNode('input', {
'type': 'submit',
'value': 'Change'
}), newNode('input', {
'type': 'button',
'value': 'Cancel',
'click': function() {
qq.editinggloss = false;
qq.removeChild(senseidform);
}
}), newNode('a', {
href: '/wiki/Help:Glosses'
}, 'Help?!'))
},
'fields': {
'senseid': util.validateNoWikisyntax('gloss', true),
'changematching': 'checkbox'
},
'onsubmit': function(values) {
var ttables = [],
sensetemps = [],
backups = [];
if (values.changematching) {
ttables = jQuery(".NavHead", languageContainers[currentLanguageTab]).filter(function() {
if (this.parentNode.getElementsByTagName("table")[0].className != "translations") return false;
for (var i = 0; i < this.childNodes.length; i++) {
if (this.childNodes[i].nodeType == 3 || this.childNodes[i].className == 'wt-edit-recurse') {
return (this.childNodes[i].nodeValue || this.childNodes[i].innerHTML) == oldsenseid;
}
}
})
sensetemps = jQuery(".qualifier-content", languageContainers[currentLanguageTab]).filter(function() {
return this.parentNode.nextSibling.nextSibling.className == "ib-colon" && this.innerHTML == oldsenseid;
})
}
editor.addEdit({
edit: function(wikitext) {
wikitext = wikitext.replace(RegExp("(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}" + findnumberofdefs + "\\s?(\{\{senseid\\|[^\\|]*\\|))[^\}]*"), "$1" + values.senseid);
if (values.changematching) {
var asd = wikitext.match('==' + tabbedLanguages[currentLanguageTab] + '==\n[\\s\\S]+?(\n==[^=]|$)');
wikitext = wikitext.replace(asd[0], asd[0].replace(RegExp("\\{\\{(sense|trans-top)\\|" + oldsenseid + "\\}\\}", "g"), "{\{$1\|" + values.senseid + "}}"))
}
return ccc = wikitext
},
redo: function() {
senseidform.style.display = "none";
for (var i = 0; i < ttables.length; i++) {
backups.push(document.createDocumentFragment());
while (ttables[i].childNodes[1]) {
backups[backups.length - 1].appendChild(ttables[i].childNodes[1]);
}
ttables[i].appendChild(document.createTextNode(values.senseid))
}
for (var i = 0; i < sensetemps.length; i++) {
backups.push(document.createDocumentFragment());
backups[backups.length - 1].appendChild(sensetemps[i].firstChild);
sensetemps[i].appendChild(document.createTextNode(values.senseid))
}
},
undo: function() {
senseidform.style.display = "inline";
for (var i = 0; i < ttables.length; i++) {
ttables[i].removeChild(ttables[i].childNodes[1]);
ttables[i].appendChild(backups.shift())
}
for (var i = 0; i < sensetemps.length; i++) {
sensetemps[i].removeChild(sensetemps[i].firstChild)
sensetemps[i].appendChild(backups.shift())
}
},
summary: "senseid: " + values.senseid
})
}
}, qq, qq.childNodes[1])
senseidform.firstChild.focus()
})
}
function setupnymadder(qq, nymtype) {
var passtheseheaders = "Usage notes|Inflection|Conjugation|Declension|Quotations",
nymtypelist = ["Synonyms", "Antonyms", "Hypernyms", "Hyponyms", "Meronyms", "Holonyms"]
for (var x = 0; nymtypelist[x] != nymtype; x++)
passtheseheaders += "|" + nymtypelist[x];
if (qq.parentNode.parentNode.nodeName.toLowerCase() == "li") qq = qq.parentNode.parentNode;
var findnumberofheaders = findnumberofheaders_(qq)
var findnumberofdefs = findnumberofdefs_(qq.parentNode.getElementsByTagName('li')[0])
for (var findheaderlevel = qq.parentNode; !/^h\d$/i.test(findheaderlevel.nodeName); findheaderlevel = findheaderlevel.previousSibling);
findheaderlevel = Number(findheaderlevel.nodeName.match(/\d/));
var senseidencoded = []
editor = new Editor()
editor.withCurrentText(function(wikitext) {
// Find nym header, or add it if it doesn't exist
var qqq = qq.parentNode;
while (!((qqq.className == "fakesubheader" || /h\d/i.test(qqq.nodeName)) && !qqq.innerHTML.match(passtheseheaders) || qqq.className == "printfooter" || qqq.className == "fakeheader" || qqq.className == "catlinks")) {
qqq = qqq.nextSibling
}
if (!qqq.innerHTML.match(nymtype)) {
qqq = qqq.parentNode.insertBefore(newNode('div', {
'class': 'fakesubheader'
}, newNode('h' + (findheaderlevel + 1), nymtype)), qqq);
}
// relevant vars:
// findheaderlevel is the the headerlevel of the most recent header above the ol
// qq is still the li itself
// qqq is the (possibly newly added) nym header
// find all relevant senseids, make an array of them, the whole template in each
var senseid = wikitext.replace(
RegExp("(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}" + findnumberofdefs + ")"), '').replace(/\n\n[\s\S]*$/, '').match(/\{\{senseid\|[^\\|]*\|[^\}]*/g)
if (!senseid) {
alert("No glosses were found. Click \"Add gloss\" to attach a gloss to the definition before adding " + nymtype.toLowerCase() + ".");
return
}
// run through senseids, checking for a corresponding span id'd nymtype-langcode-id, adding them encased in an li in a soon-to-be-parsed temp variable if not found, otherwise suppressing new form creation
temp = ""
var nymformexists = []
for (var i = 0; i < senseid.length; i++) {
if (document.getElementById(senseidencoded[i] = nymtype + "-" + encodeURIComponent(senseid[i].split("|")[1] + "-" + senseid[i].split("|")[2]).replace(/%20/g, '_').replace(/%3A/g, ':').replace(/%/g, '.'))) {
nymformexists[i] = true
} else {
temp += "* {" + "{sense|" + senseid[i].split("|")[2] + "}} <span id='" + nymtype + "-" + senseid[i].split("|")[1] + "-" + senseid[i].split("|")[2] + "'></span>\n"
}
}
new mw.Api().parse(temp, {
title: mw.config.get('wgPageName'),
pst: true, //pst makes subst work as expected
disablelimitreport: true
}).then(function(res) {
var qqqq = newNode('span')
qqqq.innerHTML = res
qqqq = qqq.parentNode.insertBefore(qqqq.firstChild, qqq.nextSibling) // the ul containing the form-holder spans, right below the nym header
for (var i = 0; i < senseid.length; i++) {
if (!nymformexists[i]) {
setupnymadder2(i, senseid, wikitext)
}
}
if (qqqq.getElementsByTagName && qqqq.getElementsByTagName('li').length == 0) {
qqqq.parentNode.removeChild(qqqq)
}
scroll(0, jQuery(qqq).offset().top)
});
})
function setupnymadder2(i, senseid, wikitext) {
var nymtype2 = nymtype.slice(0, -1).toLowerCase()
var x = document.getElementById(senseidencoded[i]);
var theform
new AdderWrapper(editor, {
'createForm': function() {
return theform = newNode('form', {
'style': 'display:inline;'
}, newNode('input', {
'name': 'nym'
}), newNode('input', {
'value': 'Add',
'type': 'submit'
}))
},
'fields': {
'nym': util.validateNoWikisyntax(nymtype2, true)
},
'onsubmit': function(values, render) {
theform.getElementsByTagName('input')[0].value = ""
var firstnym = !theform.parentNode.parentNode.getElementsByTagName('a')[0]
var addedwikitext = (firstnym ? '' : ', ') + "{" + "{l|" + senseid[i].split("|")[1] + "|" + values.nym + "}}"
render(addedwikitext, function(res2) {
var nodeToInsert = newNode('span')
nodeToInsert.innerHTML = res2
editor.addEdit({
edit: function(wikitext) {
// unindent
findnumberofdefs = findnumberofdefs_(qq.parentNode.getElementsByTagName('li')[0])
if (wikitext.substr(wikitext.length - 1, 1) != "\n") {
wikitext += "\n"
}
if (!wikitext.replace(RegExp("(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}" + findnumberofdefs + ")"), '').replace(RegExp("(\n(=|\\{\\{subst:=\\}\\})={" + (findheaderlevel - 1) + "}[^=][\\s\\S]*$)"), '').match(RegExp(nymtype + "={" + findheaderlevel + "}"))) {
wikitext = wikitext.replace(RegExp("(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}" + findnumberofdefs + "[\\s\\S]*?(\n(?=(\\{\\{subst:=\\}\\}|=)=+(?=[^=])(?!(" + passtheseheaders + ")))|$))"), "$1{" + "{subst:=}}===" + (findheaderlevel == 4 ? "=" : "") + nymtype + (findheaderlevel == 4 ? "=" : "") + "====\n\n")
}
if (firstnym) {
var passthesesenseids = [];
for (var ii = 0; ii < i; ii++) {
passthesesenseids.push("\\* \\{\\{sense\\|" + senseid[ii].split("|")[2] + "\\}\\}")
}
wikitext = wikitext.replace(RegExp("(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}" + findnumberofdefs + "[\\s\\S]*?=" + nymtype + "=+[\\s\\S]*?\n" + (passthesesenseids.length > 0 ? "(?!(" + passthesesenseids.join("|") + ")))" : ")")), "$1* {" + "{sense|" + senseid[i].split("|")[2] + "}} " + addedwikitext + "\n")
} else {
wikitext = wikitext.replace(RegExp("(^[\\s\\S]*?((^|\n)=[\\s\\S]*?){" + findnumberofheaders + "}[\\s\\S]*?=" + nymtype + "=+[\\s\\S]*?\\{\\{sense\\|" + senseid[i].split("|")[2] + "\\}\\}.*)"), "$1" + addedwikitext)
}
return ccc = wikitext;
},
redo: function() {
theform.parentNode.parentNode.insertBefore(nodeToInsert, theform.parentNode)
},
undo: function() {
theform.parentNode.parentNode.removeChild(nodeToInsert)
},
summary: "+" + nymtype2 + " [[" + values.nym + "]]"
}, nodeToInsert)
})
}
}, x)
// sort the lis
var passthesesenseids = [];
for (var ii = 0; ii < i; ii++) {
passthesesenseids.push(senseid[ii].split("|")[2])
}
for (var xx = x.parentNode.parentNode.nextSibling; xx.nextSibling && !/h\d/i.test(xx.nodeName) && xx.className != "fakesubheader" && xx.nodeName.toUpperCase() != "UL"; xx = xx.nextSibling);
if (xx.nodeName.toUpperCase() == "UL") {
var xxx = xx.getElementsByTagName('li')
for (var xxxx = xxx.length; xxxx--;) {
var xxxxx = xxx[xxxx].getElementsByTagName("span")[3]
if (xxxxx && xxxxx.innerHTML == senseid[i].split("|")[2]) {
x.parentNode.parentNode.removeChild(x.parentNode);
xxx[xxxx].appendChild(x);
break;
}
if (xxxxx && passthesesenseids.length && RegExp(passthesesenseids.join("|")).test(xxxxx.innerHTML)) {
xxx[xxxx + 1] ? xx.insertBefore(x.parentNode, xxx[xxxx]) : xx.appendChild(x.parentNode);
break;
}
}
if (xxxx == -1) {
xx.insertBefore(x.parentNode, xx.firstChild)
}
}
}
}
jQuery(document).ready(function() {
var disablebutton = document.getElementById('YRDefsideboxes-disable-button');
if (disablebutton) {
disablebutton.innerHTML = "";
var toggle = newNode('span', {
click: function() {
if ($.cookie("DisableYREdit")) {
$.cookie("DisableYREdit", null, {
path: "/"
})
toggle.innerHTML = "Disable definition editing options."
} else {
$.cookie("DisableYREdit", 1, {
expires: 30,
path: "/"
})
toggle.innerHTML = "Enable definition editing options."
}
}
}, ($.cookie("DisableYREdit") ? "Enable" : "Disable") + " definition editing options.")
disablebutton.appendChild(toggle);
}
if (mw.config.get('wgAction') == 'view' && (mw.config.get('wgNamespaceNumber') == 0 || mw.config.get('wgPageName') == "Wiktionary:Sandbox") && !/&printable=yes|&diff=|&oldid=/.test(window.location.search) && !$.cookie("DisableYREdit") && !window.DisableYREdit && !window.loadedYREdit) {
window.loadedYREdit = true;
for (var ols = document.getElementsByTagName('ol'), ii = 0; ii < ols.length; ii++) {
if (ols[ii].className != "references") {
for (var lis = ols[ii].getElementsByTagName('li'), i = 0; i < lis.length; i++) {
if (lis[i].parentNode == ols[ii] && ols[ii].parentNode.parentNode.nodeName != "UL") makedefsidebox(lis[i]);
}
}
}
mw.util.addPortletLink('p-tb', 'javascript:addPOSHeader()', 'Add part of speech');
defsidebuttons = [0]
defeditoptions = {
addingPOS: false
}
}
})
// </nowiki>
8oh79o98tyjo7ss59excfxpgp5m9e4o
MediaWiki:Gadget-DefSideBoxes.css
8
18007
89017
2012-10-22T17:39:09Z
en>Yair rand
0
89017
css
text/css
.defEditButton {
position: absolute;
margin-left: -45px;
width: 45px;
height: 20px;
}
ol li:hover .defEditButton span {
opacity: 1;
filter: alpha(opacity=100);
}
.defEditButton span {
display: block;
margin-top: 3px;
margin-left: 10px;
cursor: pointer;
height: 10px;
width: 10px;
opacity: 0;
filter: alpha(opacity=0);
transition: opacity .15s;
-webkit-transition: opacity .15s;
-moz-transition: opacity .15s;
-o-transition: opacity .15s;
background-image: url(//upload.wikimedia.org/wikipedia/commons/d/d3/InterlanguageLinks-Asset-Pencil.gif);
}
.defEditButton span:hover { background-image: url(//upload.wikimedia.org/wikipedia/commons/d/d1/InterlanguageLinks-Asset-Pencil-Hover.gif) }
.addusexbutton {
padding-left: 15px;
margin-top: 0px;
overflow: hidden;
-webkit-transition: all 0.3s 0.3s;
-moz-transition: all 0.3s 0.3s;
-ms-transition: all 0.3s 0.3s;
-o-transition: all 0.3s 0.3s;
transition: all 0.3s 0.3s;
height: 0px;
}
li:hover>.addusexbutton {
margin-top: -4px;
height: 19px;
}
.addusexbutton>a {
vertical-align: top;
cursor: pointer;
font-size: 10px;
}
.addusexbutton>a+a { margin-left: 15px }
.addusexbutton li a {
display: block;
cursor: pointer;
}
.addusexbutton li a.DSBinactive {
color: #AAAAAA;
cursor: default;
}
.addusexbutton ul {
display: none;
position: absolute;
background-color: #FFFFFF;
border: 1px solid #AAA;
list-style: none;
padding: 1px 10px;
margin: -12px 0 0 5px;
}
.DSBRedButton,
.DSBGreenButton {
display: inline-block;
padding: 0px;
border-radius: 5px;
border: #9D9D9D solid 1px;
}
.DSBRedButton {
margin-right: 5px;
margin-left: 3px;
}
.DSBRedButton div,
.DSBGreenButton div {
padding: 1px 5px;
display: inline-block;
font-size: 14px;
color: white;
font-weight: bold;
border-radius: 5px;
cursor: pointer;
}
.DSBRedButton div {
border-left: 1px solid #CA0000;
border-top: 1px solid #CA0000;
background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #C70000),color-stop(1, #7A0000));
background: -moz-linear-gradient(top, #C70000, #7A0000);
background: -o-linear-gradient(top, #C70000, #7A0000);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#C70000', endColorstr='#7A0000');
-ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#C70000', endColorstr='#7A0000');
background-color: #C70000;
}
.DSBGreenButton div {
border-left: 1px solid #3BB677;
border-top: 1px solid #3BB677;
background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #3BB475),color-stop(1, #246E48));
background: -moz-linear-gradient(top, #3BB475, #246E48);
background: -o-linear-gradient(top, #3BB475, #246E48);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#3BB475', endColorstr='#246E48');
-ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#3BB475', endColorstr='#246E48');
background-color: #3BB475;
}
mv6bxcbnp2yxxg7t6oodkz7m84s05xd
89018
89017
2026-08-06T23:33:56Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-DefSideBoxes.css]]
89017
css
text/css
.defEditButton {
position: absolute;
margin-left: -45px;
width: 45px;
height: 20px;
}
ol li:hover .defEditButton span {
opacity: 1;
filter: alpha(opacity=100);
}
.defEditButton span {
display: block;
margin-top: 3px;
margin-left: 10px;
cursor: pointer;
height: 10px;
width: 10px;
opacity: 0;
filter: alpha(opacity=0);
transition: opacity .15s;
-webkit-transition: opacity .15s;
-moz-transition: opacity .15s;
-o-transition: opacity .15s;
background-image: url(//upload.wikimedia.org/wikipedia/commons/d/d3/InterlanguageLinks-Asset-Pencil.gif);
}
.defEditButton span:hover { background-image: url(//upload.wikimedia.org/wikipedia/commons/d/d1/InterlanguageLinks-Asset-Pencil-Hover.gif) }
.addusexbutton {
padding-left: 15px;
margin-top: 0px;
overflow: hidden;
-webkit-transition: all 0.3s 0.3s;
-moz-transition: all 0.3s 0.3s;
-ms-transition: all 0.3s 0.3s;
-o-transition: all 0.3s 0.3s;
transition: all 0.3s 0.3s;
height: 0px;
}
li:hover>.addusexbutton {
margin-top: -4px;
height: 19px;
}
.addusexbutton>a {
vertical-align: top;
cursor: pointer;
font-size: 10px;
}
.addusexbutton>a+a { margin-left: 15px }
.addusexbutton li a {
display: block;
cursor: pointer;
}
.addusexbutton li a.DSBinactive {
color: #AAAAAA;
cursor: default;
}
.addusexbutton ul {
display: none;
position: absolute;
background-color: #FFFFFF;
border: 1px solid #AAA;
list-style: none;
padding: 1px 10px;
margin: -12px 0 0 5px;
}
.DSBRedButton,
.DSBGreenButton {
display: inline-block;
padding: 0px;
border-radius: 5px;
border: #9D9D9D solid 1px;
}
.DSBRedButton {
margin-right: 5px;
margin-left: 3px;
}
.DSBRedButton div,
.DSBGreenButton div {
padding: 1px 5px;
display: inline-block;
font-size: 14px;
color: white;
font-weight: bold;
border-radius: 5px;
cursor: pointer;
}
.DSBRedButton div {
border-left: 1px solid #CA0000;
border-top: 1px solid #CA0000;
background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #C70000),color-stop(1, #7A0000));
background: -moz-linear-gradient(top, #C70000, #7A0000);
background: -o-linear-gradient(top, #C70000, #7A0000);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#C70000', endColorstr='#7A0000');
-ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#C70000', endColorstr='#7A0000');
background-color: #C70000;
}
.DSBGreenButton div {
border-left: 1px solid #3BB677;
border-top: 1px solid #3BB677;
background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #3BB475),color-stop(1, #246E48));
background: -moz-linear-gradient(top, #3BB475, #246E48);
background: -o-linear-gradient(top, #3BB475, #246E48);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#3BB475', endColorstr='#246E48');
-ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#3BB475', endColorstr='#246E48');
background-color: #3BB475;
}
mv6bxcbnp2yxxg7t6oodkz7m84s05xd
MediaWiki:Gadget-DeveloperEditorTweaks.js
8
18008
89019
2019-01-18T00:08:34Z
en>Erutuon
0
a bit odd to have "advanced" section with only one thing in it
89019
javascript
text/javascript
'use strict'; /*jshint undef:true */
// {{documentation}}
/*global mw, jQuery */
if ((mw.config.get('wgAction') === 'edit') || (mw.config.get('wgAction') === 'submit'))
jQuery(document).ready(function () {
var isCoding = { 'javascript': true, 'css': true, 'Scribunto': true };
var wgPageContentModel = mw.config.get('wgPageContentModel');
var wpTextbox1 = jQuery('#wpTextbox1');
if (!wpTextbox1.length) {
console.error("det: failed to find WikiEditor entry point");
return;
}
if (!wpTextbox1.wikiEditor) {
console.error("det: WikiEditor not loaded?");
return;
}
if (wgPageContentModel !== 'wikitext') {
// remove irrelevant buttons
// XXX: this should be reported to Bugzilla, really
wpTextbox1.wikiEditor('removeFromToolbar', {
'section': 'main',
'group': 'insert'
});
wpTextbox1.wikiEditor('removeFromToolbar', {
'section': 'main',
'group': 'format'
});
wpTextbox1.wikiEditor('removeFromToolbar', {
'section': 'help'
});
wpTextbox1.wikiEditor('removeFromToolbar', {
'section': 'advanced',
'group': 'heading'
});
wpTextbox1.wikiEditor('removeFromToolbar', {
'section': 'advanced',
'group': 'format'
});
wpTextbox1.wikiEditor('removeFromToolbar', {
'section': 'advanced',
'group': 'size'
});
wpTextbox1.wikiEditor('removeFromToolbar', {
'section': 'advanced',
'group': 'insert'
});
}
// workaround [[gerrit:118993]]
mw.util.addCSS(
".group-codeeditor-tools," +
".codeEditor-ui-toolbar .group-insert," +
".codeEditor-ui-toolbar .group-format," +
".codeEditor-ui-toolbar .tabs span.tab-advanced," +
".codeEditor-ui-toolbar .tabs span.tab-characters," +
".codeEditor-ui-toolbar .tabs span.tab-help," +
".codeEditor-ui-toolbar .sections {" +
" display: block !important;" +
"}");
if (isCoding[wgPageContentModel]) {
wpTextbox1.wikiEditor('addToToolbar', {
'section': 'main',
'groups': {
'codefmt': {
'tools': {
'normws': {
'type': 'button',
'label': 'Normalise whitespace',
'icon': '//upload.wikimedia.org/wikipedia/commons/thumb/0/00/VisualEditor_-_Icon_-_Indent-list-ltr.svg/24px-VisualEditor_-_Icon_-_Indent-list-ltr.svg.png', // XXX
'action': {
'type': 'callback',
'execute': function (context) {
function normws(text, lineStart, lineEnd) {
text = text.replace(lineEnd ? /[ \t]+(\n|$)/g : /[ \t]+(\n)/g, '$1');
var step = 4; // XXX: guess it by inspecting the source?
text = text.replace(lineStart ? /(^|\n)([\t ]+)/g : /(\n)([\t ]+)/g, function (_, nl, indent) {
indent = indent.replace(/ /g, '\t').replace(/ +\t/g, '\t').replace(/\t/g, ' ');
var steps = Math.floor(indent.length / step); indent = '';
for (var i = 0; i < steps; ++i)
indent += '\t';
return nl + indent;
});
return text;
}
if (!context.$textarea.textSelection('getSelection')) {
if (context.codeEditor)
context.codeEditor.selectAll();
else
context.$textarea[0].select();
}
context.$textarea.textSelection('encapsulateSelection', {
replace: true, selectPeri: true,
peri: normws(context.$textarea[0].value, true, true)
});
}
}
}
}
}
}
});
}
if ((wgPageContentModel === 'javascript') || (mw.config.get('wgPageName') === 'MediaWiki:Gadgets-definition')) {
var moduleDescriptions = {
'mediawiki.Title': "<code>mw.Title</code> object",
'mediawiki.Uri' : "<code>mw.Uri</code> object",
'mediawiki.util' : "<code>mw.util</code>",
'mediawiki.user' : "<code>mw.user</code>",
'moment' : "Moment.js",
'site' : "Site-specific scripts: [[MediaWiki:Common.js]] and per-skin JS",
'es5-shim' : "ECMAScript 5 shim for older browsers",
'':""
};
wpTextbox1.wikiEditor('addToToolbar', {
'sections': {
'js': {
'type': 'booklet',
'label': "JavaScript reference",
'pages': {
'modules': {
'layout': 'table',
'label': "ResourceLoader modules",
'headings': [
{ 'text': "Identifier" },
{ 'text': "Description" }
],
'rows': mw.loader.getModuleNames().sort().filter(function (modname) {
// they are quite dull modules, but they are there
if (/^ext\.geshi\.language\./.test(modname))
return false;
if (/^ext\.math\.mathjax\.jax\.output\./.test(modname))
return false;
return true;
}).map(function (modname) {
return {
'id': {
'html': '<code>' + modname + '</code>',
},
'desc': {
'html': moduleDescriptions[modname] || '<small style="color:gray;">(no description)</small>'
}
};
})
}
}
}
}
});
}
if (wgPageContentModel === 'javascript') {
var jshintDescriptions = {
'bitwise' : "Prohibit bitwise operators",
'camelcase' : "Mandate <code>javaCamelCase</code> (deprecated)",
'curly' : "Mandate curly brackets in control structures",
'eqeqeq' : "Prohibit <code>==</code> and <code>!=</code>",
'es3' : "Mandate ECMAScript 3 compatibility (deprecated; use <code>esversion:3</code>)",
'es5' : "Mandate ECMAScript 5 compatibility (deprecated; use <code>esversion:5</code>)",
'esversion:<var>n</var>' : 'Mandate compatibility with an ECMAScript version (3, 5<span class="serial-comma">,</span> or 6)',
'forin' : "Mandate <code>hasOwnProperty</code> in <code>for..in</code> loops",
'freeze' : "Prohibit changing prototypes of built-in objects",
'futurehostile' : "Warn about identifiers defined in future versions of JavaScript",
'globals' : "Disable warnings for these globals",
'immed' : "Mandate parentheses around immediately-executed function expressions (deprecated)",
'indent:<var>n</var>' : "Set tab width (deprecated)",
'latedef' : "Prohibit using variables before their declaration",
'newcap' : "Mandate capitalisation of constructors (deprecated)",
'noarg' : "Prohibit use of <code>arguments.caller</code> and <code>arguments.callee</code>",
'noempty' : "Warn about empty blocks (deprecated)",
'nonbsp' : "Warn about non-breaking space",
'nonew' : "Prohibit constructor usage for side effects",
'plusplus' : "Prohibit unary increment and decrement operators",
'quotmark:true' : "Mandate consistency of quotation marks (deprecated)",
'quotmark:single' : "Mandate single quotation marks only (deprecated)",
'quotmark:double' : "Mandata double quotation marks only (deprecated)",
'undef' : "Prohibit use of variables not explicitly declared",
'unused' : "Warn about unused variables",
'varstmt' : "Prohibit <code>var</code> (use <code>let</code> or <code>const</code>)",
'strict' : "Mandate strict mode compatibility",
'maxparams:<var>n</var>' : "Enforce maximum number of formal parameters",
'maxdepth:<var>n</var>' : "Enforce maximum depth of nested blocks",
'maxstatements' : "Enforce maximum number of statements per function",
'maxcomplexity:<var>n</var>': "Enforce maximum cyclomatic complexity",
'maxlen' : "Enforce maximum length of a line (deprecated)",
'asi' : "Allow semicolon insertion",
'boss' : "Allow assignment-expressions",
'debug' : "Allow <code>debugger</code> statements",
'eqnull' : "Allow <code>== null</code> comparisons",
'esnext' : "Allow ES.next syntax (deprecated; use <code>esversion:6</code>)",
'evil' : "Allow <code>eval</code>",
'expr' : "Allow expressions where statements are expected",
'funcscope' : "Allow using variables outside the block where they are declared",
'globalstrict' : "Allow global strict mode (deprecated)",
'iterator' : "Allow using <code>__iterator__</code>",
'lastsemic' : "Allow omitting semicolons last statement",
'laxbreak' : "Allow lax line breaks (deprecated)",
'laxcomma' : "Allow comma-first coding style (deprecated)",
'loopfunc' : "Allow making functions within a loop",
'maxerr:<var>n</var>' : "Set maximum number of warnings",
'moz' : "Allow Mozilla extensions",
'multistr' : "Allow multi-line string literals (deprecated)",
'notypeof' : "Allow lax usage of <code>typeof</code>",
'proto' : "Allow using <code>__proto__</code>",
'scripturl' : "Allow <code>javascript:</code> URLs",
'shadow' : "Allow variable shadowing",
'sub' : "Allow bracket notation for object member access (deprecated)",
'supernew' : "Allow atypical constructor usage",
'validthis' : "<code>this</code> is valid in this function",
'noyield' : "Allow generators without <code>yield</code>",
'browser' : "Assume a browser environment",
'devel' : "Assume <code>console</code> is available",
'jquery' : "Assume jQuery is available",
'nonstandard' : "Assume non-standard global functions are available",
'worker' : "Assume Web Worker globals are available",
/* probably not useful: assume a framework is available
'couch' : "",
'dojo' : "",
'mootools' : "",
'node' : "",
'phantom' : "",
'prototypejs' : "",
'rhino' : "",
'wsh' : "",
'yui' : "",
*/
};
wpTextbox1.wikiEditor('addToToolbar', {
'section': 'js',
'pages': {
'jshint': {
'layout': 'table',
'label': "JSHint options",
'headings': [
{ 'text': "Option" },
{ 'text': "Description" }
],
'rows': Object.keys(jshintDescriptions).sort().map(function (option) {
return {
'option': {
'html': '<code>'
+ option.replace(
/^[^:]+/,
'<a href="https://jshint.com/docs/options/#$&" target="_blank">$&</a>')
+ '</code>'
},
'description': {
'html': jshintDescriptions[option]
}
};
})
}
}
});
}
if (wgPageContentModel === 'Scribunto') {
// XXX: move console upwards
}
});
mtyoqowtbn14jj0l34ztugvpd9j8an9
89020
89019
2026-08-06T23:34:03Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-DeveloperEditorTweaks.js]]
89019
javascript
text/javascript
'use strict'; /*jshint undef:true */
// {{documentation}}
/*global mw, jQuery */
if ((mw.config.get('wgAction') === 'edit') || (mw.config.get('wgAction') === 'submit'))
jQuery(document).ready(function () {
var isCoding = { 'javascript': true, 'css': true, 'Scribunto': true };
var wgPageContentModel = mw.config.get('wgPageContentModel');
var wpTextbox1 = jQuery('#wpTextbox1');
if (!wpTextbox1.length) {
console.error("det: failed to find WikiEditor entry point");
return;
}
if (!wpTextbox1.wikiEditor) {
console.error("det: WikiEditor not loaded?");
return;
}
if (wgPageContentModel !== 'wikitext') {
// remove irrelevant buttons
// XXX: this should be reported to Bugzilla, really
wpTextbox1.wikiEditor('removeFromToolbar', {
'section': 'main',
'group': 'insert'
});
wpTextbox1.wikiEditor('removeFromToolbar', {
'section': 'main',
'group': 'format'
});
wpTextbox1.wikiEditor('removeFromToolbar', {
'section': 'help'
});
wpTextbox1.wikiEditor('removeFromToolbar', {
'section': 'advanced',
'group': 'heading'
});
wpTextbox1.wikiEditor('removeFromToolbar', {
'section': 'advanced',
'group': 'format'
});
wpTextbox1.wikiEditor('removeFromToolbar', {
'section': 'advanced',
'group': 'size'
});
wpTextbox1.wikiEditor('removeFromToolbar', {
'section': 'advanced',
'group': 'insert'
});
}
// workaround [[gerrit:118993]]
mw.util.addCSS(
".group-codeeditor-tools," +
".codeEditor-ui-toolbar .group-insert," +
".codeEditor-ui-toolbar .group-format," +
".codeEditor-ui-toolbar .tabs span.tab-advanced," +
".codeEditor-ui-toolbar .tabs span.tab-characters," +
".codeEditor-ui-toolbar .tabs span.tab-help," +
".codeEditor-ui-toolbar .sections {" +
" display: block !important;" +
"}");
if (isCoding[wgPageContentModel]) {
wpTextbox1.wikiEditor('addToToolbar', {
'section': 'main',
'groups': {
'codefmt': {
'tools': {
'normws': {
'type': 'button',
'label': 'Normalise whitespace',
'icon': '//upload.wikimedia.org/wikipedia/commons/thumb/0/00/VisualEditor_-_Icon_-_Indent-list-ltr.svg/24px-VisualEditor_-_Icon_-_Indent-list-ltr.svg.png', // XXX
'action': {
'type': 'callback',
'execute': function (context) {
function normws(text, lineStart, lineEnd) {
text = text.replace(lineEnd ? /[ \t]+(\n|$)/g : /[ \t]+(\n)/g, '$1');
var step = 4; // XXX: guess it by inspecting the source?
text = text.replace(lineStart ? /(^|\n)([\t ]+)/g : /(\n)([\t ]+)/g, function (_, nl, indent) {
indent = indent.replace(/ /g, '\t').replace(/ +\t/g, '\t').replace(/\t/g, ' ');
var steps = Math.floor(indent.length / step); indent = '';
for (var i = 0; i < steps; ++i)
indent += '\t';
return nl + indent;
});
return text;
}
if (!context.$textarea.textSelection('getSelection')) {
if (context.codeEditor)
context.codeEditor.selectAll();
else
context.$textarea[0].select();
}
context.$textarea.textSelection('encapsulateSelection', {
replace: true, selectPeri: true,
peri: normws(context.$textarea[0].value, true, true)
});
}
}
}
}
}
}
});
}
if ((wgPageContentModel === 'javascript') || (mw.config.get('wgPageName') === 'MediaWiki:Gadgets-definition')) {
var moduleDescriptions = {
'mediawiki.Title': "<code>mw.Title</code> object",
'mediawiki.Uri' : "<code>mw.Uri</code> object",
'mediawiki.util' : "<code>mw.util</code>",
'mediawiki.user' : "<code>mw.user</code>",
'moment' : "Moment.js",
'site' : "Site-specific scripts: [[MediaWiki:Common.js]] and per-skin JS",
'es5-shim' : "ECMAScript 5 shim for older browsers",
'':""
};
wpTextbox1.wikiEditor('addToToolbar', {
'sections': {
'js': {
'type': 'booklet',
'label': "JavaScript reference",
'pages': {
'modules': {
'layout': 'table',
'label': "ResourceLoader modules",
'headings': [
{ 'text': "Identifier" },
{ 'text': "Description" }
],
'rows': mw.loader.getModuleNames().sort().filter(function (modname) {
// they are quite dull modules, but they are there
if (/^ext\.geshi\.language\./.test(modname))
return false;
if (/^ext\.math\.mathjax\.jax\.output\./.test(modname))
return false;
return true;
}).map(function (modname) {
return {
'id': {
'html': '<code>' + modname + '</code>',
},
'desc': {
'html': moduleDescriptions[modname] || '<small style="color:gray;">(no description)</small>'
}
};
})
}
}
}
}
});
}
if (wgPageContentModel === 'javascript') {
var jshintDescriptions = {
'bitwise' : "Prohibit bitwise operators",
'camelcase' : "Mandate <code>javaCamelCase</code> (deprecated)",
'curly' : "Mandate curly brackets in control structures",
'eqeqeq' : "Prohibit <code>==</code> and <code>!=</code>",
'es3' : "Mandate ECMAScript 3 compatibility (deprecated; use <code>esversion:3</code>)",
'es5' : "Mandate ECMAScript 5 compatibility (deprecated; use <code>esversion:5</code>)",
'esversion:<var>n</var>' : 'Mandate compatibility with an ECMAScript version (3, 5<span class="serial-comma">,</span> or 6)',
'forin' : "Mandate <code>hasOwnProperty</code> in <code>for..in</code> loops",
'freeze' : "Prohibit changing prototypes of built-in objects",
'futurehostile' : "Warn about identifiers defined in future versions of JavaScript",
'globals' : "Disable warnings for these globals",
'immed' : "Mandate parentheses around immediately-executed function expressions (deprecated)",
'indent:<var>n</var>' : "Set tab width (deprecated)",
'latedef' : "Prohibit using variables before their declaration",
'newcap' : "Mandate capitalisation of constructors (deprecated)",
'noarg' : "Prohibit use of <code>arguments.caller</code> and <code>arguments.callee</code>",
'noempty' : "Warn about empty blocks (deprecated)",
'nonbsp' : "Warn about non-breaking space",
'nonew' : "Prohibit constructor usage for side effects",
'plusplus' : "Prohibit unary increment and decrement operators",
'quotmark:true' : "Mandate consistency of quotation marks (deprecated)",
'quotmark:single' : "Mandate single quotation marks only (deprecated)",
'quotmark:double' : "Mandata double quotation marks only (deprecated)",
'undef' : "Prohibit use of variables not explicitly declared",
'unused' : "Warn about unused variables",
'varstmt' : "Prohibit <code>var</code> (use <code>let</code> or <code>const</code>)",
'strict' : "Mandate strict mode compatibility",
'maxparams:<var>n</var>' : "Enforce maximum number of formal parameters",
'maxdepth:<var>n</var>' : "Enforce maximum depth of nested blocks",
'maxstatements' : "Enforce maximum number of statements per function",
'maxcomplexity:<var>n</var>': "Enforce maximum cyclomatic complexity",
'maxlen' : "Enforce maximum length of a line (deprecated)",
'asi' : "Allow semicolon insertion",
'boss' : "Allow assignment-expressions",
'debug' : "Allow <code>debugger</code> statements",
'eqnull' : "Allow <code>== null</code> comparisons",
'esnext' : "Allow ES.next syntax (deprecated; use <code>esversion:6</code>)",
'evil' : "Allow <code>eval</code>",
'expr' : "Allow expressions where statements are expected",
'funcscope' : "Allow using variables outside the block where they are declared",
'globalstrict' : "Allow global strict mode (deprecated)",
'iterator' : "Allow using <code>__iterator__</code>",
'lastsemic' : "Allow omitting semicolons last statement",
'laxbreak' : "Allow lax line breaks (deprecated)",
'laxcomma' : "Allow comma-first coding style (deprecated)",
'loopfunc' : "Allow making functions within a loop",
'maxerr:<var>n</var>' : "Set maximum number of warnings",
'moz' : "Allow Mozilla extensions",
'multistr' : "Allow multi-line string literals (deprecated)",
'notypeof' : "Allow lax usage of <code>typeof</code>",
'proto' : "Allow using <code>__proto__</code>",
'scripturl' : "Allow <code>javascript:</code> URLs",
'shadow' : "Allow variable shadowing",
'sub' : "Allow bracket notation for object member access (deprecated)",
'supernew' : "Allow atypical constructor usage",
'validthis' : "<code>this</code> is valid in this function",
'noyield' : "Allow generators without <code>yield</code>",
'browser' : "Assume a browser environment",
'devel' : "Assume <code>console</code> is available",
'jquery' : "Assume jQuery is available",
'nonstandard' : "Assume non-standard global functions are available",
'worker' : "Assume Web Worker globals are available",
/* probably not useful: assume a framework is available
'couch' : "",
'dojo' : "",
'mootools' : "",
'node' : "",
'phantom' : "",
'prototypejs' : "",
'rhino' : "",
'wsh' : "",
'yui' : "",
*/
};
wpTextbox1.wikiEditor('addToToolbar', {
'section': 'js',
'pages': {
'jshint': {
'layout': 'table',
'label': "JSHint options",
'headings': [
{ 'text': "Option" },
{ 'text': "Description" }
],
'rows': Object.keys(jshintDescriptions).sort().map(function (option) {
return {
'option': {
'html': '<code>'
+ option.replace(
/^[^:]+/,
'<a href="https://jshint.com/docs/options/#$&" target="_blank">$&</a>')
+ '</code>'
},
'description': {
'html': jshintDescriptions[option]
}
};
})
}
}
});
}
if (wgPageContentModel === 'Scribunto') {
// XXX: move console upwards
}
});
mtyoqowtbn14jj0l34ztugvpd9j8an9
MediaWiki:Gadget-AjaxEdit.js
8
18009
89021
2026-07-23T22:16:54Z
en>Surjection
0
do not insert a comma on Vector-2022
89021
javascript
text/javascript
// AjaxEdit
// <nowiki>
"use strict";
// Description : With a new button "Ædit" one can edit specific sections without reloading the whole page.
// Supports TabbedLanguages gadget
// Dependencies: mediawiki.util, mediawiki.Uri, mediawiki.user, mediawiki.api
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, sub:true, esversion:5, esnext:false */
/* global mw, $ */
(function ajaxEditIife() {
window.AjaxEdit = {};
window.AjaxEdit.scriptLink = "";
if (mw.config.get("wgSiteName") !== "Wiktionary")
window.AjaxEdit.scriptLink += "wikt:";
if (mw.config.get("wgContentLanguage") !== "en")
window.AjaxEdit.scriptLink += "en:";
window.AjaxEdit.scriptLink += "MediaWiki:Gadget-AjaxEdit.js";
var $document = $(document);
window.AjaxEdit.FireEvents = function () {
// fire page load events like the [quotations ▼] thing
// see https://github.com/wikimedia/mediawiki/blob/master/resources/src/mediawiki.action/mediawiki.action.edit.preview.js
mw.hook("wikipage.content").fire($document);
mw.hook("wikipage.categories").fire($document);
};
var isVector2022 = mw.config.get("skin") === "vector-2022";
window.AjaxEdit.Click = function (ajaxEditAnchor) {
if (ajaxEditAnchor.hasEditBox) {
mw.notify("This AjaxEdit edit box is already open.");
return;
} else if (document.getElementById("ajaxedit-wrapper")) {
mw.notify("An AjaxEdit edit box is already open on this page.");
return;
} else {
ajaxEditAnchor.hasEditBox = true;
}
var elements = {};
var _query = new URL($(ajaxEditAnchor)
.parent()
.find("a[href*='action=edit']")
.attr("href"), location.href).searchParams,
title = _query.get("title"),
section = _query.get("section");
section = section.replace("T-", ""); //transclusions...
var hdr = $(ajaxEditAnchor).parent().parent();
var sectionName = hdr.children(":header").first().text();
if (window.tabbedLanguages && !hdr.is(":header")) {
sectionName = window.tabbedLanguages[window.currentLanguageTab];
}
var data = { action: 'raw', title: title, section: section };
$.get(mw.util.wikiScript('index'), data).then(function (wikitext) {
var rowHeight = Math.min(15, 1 + wikitext.split("\n").length);
elements.textarea = $("<textarea/>")
.attr({ rows: rowHeight })
.addClass("mw-editfont-monospace")
.text(wikitext);
elements.textareaSummary = $("<input/>")
.attr({
type: "text",
maxlength: 500,
spellcheck: true,
name: "wpSummary",
})
.keydown(event, function () {
if (event.keyCode == 13) { // on enter
elements.saveButton.click();
}
});
function checkboxAndLabel(id, text) {
var checkbox = $("<input/>")
.attr({
"id": id,
"type": "checkbox",
});
var label = $("<label/>")
.attr("for", id)
.text(text);
return [checkbox, label];
}
var minorEdit = checkboxAndLabel("ajaxedit-minor-edit-checkbox", "Minor edit");
elements.minorEditCheckbox = minorEdit[0], elements.minorEditLabel = minorEdit[1];
elements.saveButton = $("<button/>")
.text("Save")
.click(function () {
elements.textarea.attr('disabled', true);
elements.textareaSummary.attr('disabled', true);
window.AjaxEdit.Save(section, sectionName, elements.textarea.val(), title, elements.textareaSummary.val(), elements.minorEditCheckbox.prop("checked"))
.then(function (apiSaveResponse) {
if (apiSaveResponse.error) return; //if abusefilter was triggered
new mw.Api().get({ page: mw.config.get("wgPageName"), action: "parse", prop: "text|categorieshtml" })
.then(function (newHtml) {
$("#ajaxedit-wrapper").remove();
$("#mw-content-text").html(newHtml.parse.text["*"]);
$("#catlinks").replaceWith(newHtml.parse.categorieshtml["*"]);
window.AjaxEdit.FireEvents();
mw.loader.moduleRegistry["site"].state = "registered";
//mw.loader.moduleRegistry["site"].version="generate-unique-guid-here";
mw.loader.moduleRegistry["site"].script = undefined;
mw.loader.using("site", function () { });
window.AjaxEdit.Submain();
});
});
})
.css("margin-left", "3px");
elements.cancelButton = $('<button/>')
.text("Cancel")
.click(function () {
$("#ajaxedit-wrapper").remove();
ajaxEditAnchor.hasEditBox = false;
});
elements.previewDiv = $("<div/>")
.css({
"border-style": "solid", "border-width": "1px 1px 1px 4px",
"border-radius": "0.33em", "border-color": "#a3d3ff",
})
.hide();
function timestamp() {
return new Date().toISOString();
}
var previousText;
var previousTextTimestamp = timestamp();
function loadPreview() {
var newText = elements.textarea.val();
var textTimestamp = timestamp();
if (!previousText || previousText != newText) {
new mw.Api()
.parse(
newText,
{ title: mw.config.get("wgPageName"), pst: "true", preview: "true", sectionpreview: "true", disableeditsection: "true" }
)
.done(function (html) {
if (textTimestamp > previousTextTimestamp) {
elements.previewDiv.html(html);
window.AjaxEdit.FireEvents();
previousTextTimestamp = textTimestamp;
}
});
}
previousText = newText;
}
var livePreview = checkboxAndLabel("ajaxedit-live-preview-checkbox", "Live preview");
elements.livePreviewCheckbox = livePreview[0], elements.livePreviewLabel = livePreview[1];
elements.livePreviewCheckbox.on("change", function () {
if ($(this).prop("checked")) {
elements.previewDiv.show("slow");
loadPreview();
this.previewIntervalId = setInterval(loadPreview, 500);
} else {
clearInterval(this.previewIntervalId);
elements.previewDiv.hide("fast");
elements.previewDiv.empty();
}
});
var wrapper = $("<div/>")
.attr("id", "ajaxedit-wrapper")
.css({ width: "auto", "margin": 0, "overflow": "hidden" })
.append(elements.textarea)
.append(elements.textareaSummary)
.append(elements.saveButton)
.append(elements.livePreviewCheckbox)
.append(elements.livePreviewLabel)
.append(elements.minorEditCheckbox)
.append(elements.minorEditLabel)
.append(elements.cancelButton)
.append(elements.previewDiv);
//tabbed languages support
if ($(ajaxEditAnchor).is("#tabstable .editlangsection a")) {
$(".languageContainer:not(:hidden)")
.first()
.prepend(wrapper);
} else {
hdr.after(wrapper);
}
});
};
/*
* Define edit summary template with window.AjaxEditSummary. For instance:
window.AjaxEditSummary = "Æ $sectionlink $summary";
*/
window.AjaxEdit.Save = function (sectionID, sectionName, sectionText, title, summary, minorEdit) {
var finalSummary;
if (typeof window.AjaxEditSummary === "string") {
var usedSummary = false;
finalSummary = window.AjaxEditSummary.replace(
/\$([a-zA-Z_][a-zA-Z0-9_]*)/g,
function (fullMatch, name) {
switch (name) {
case "summary": {
usedSummary = true;
return summary;
}
case "scriptlink": return window.AjaxEdit.scriptLink;
case "sectionname": return sectionText;
case "sectionlink": return "/* " + sectionName + " */";
default: {
mw.notify(
"Your edit summary template (window.AjaxEditSummary) contains an unknown variable name. Variable name: "
+ name + ". Choose from summary, scriptlink, sectionname, sectionlink."
);
return "";
}
}
});
if (!usedSummary) {
mw.notify(
"Your edit summary template (window.AjaxEditSummary) doesn't use the edit summary you supplied, so your edit summary will be appended to the template: "
+ window.AjaxEditSummary);
finalSummary += " " + summary;
}
} else {
finalSummary = "/* " + sectionName + " */ " + summary + " (using [[" + window.AjaxEdit.scriptLink + "|AjaxEdit]])";
}
var data = {
format: 'json',
action: 'edit',
title: title,
section: sectionID,
summary: finalSummary,
text: sectionText,
token: mw.user.tokens.get('csrfToken'),
minor: minorEdit,
};
return new mw.Api().post(data).then(function (response) {
if (response && response.edit && response.edit.result == 'Success') {
mw.notify("successful");
} else if (response && response.error) {
mw.notify('Error: API returned error code "' + response.error.code + '": ' + response.error.info);
}
return response;
}).fail(function (xhr) {
mw.notify('Error: Request failed.');
$('#ajaxedit-wrapper *').attr('disabled', false);
});
};
window.AjaxEdit.Submain = function () {
$(".mw-body-content .mw-editsection > .mw-editsection-bracket:contains(']')").each(function () {
var btn = $('<a/>').text("Ædit").on("click", function () { window.AjaxEdit.Click(this) });
$(this).before(!isVector2022 ? ", " : "").before(btn);
});
};
window.AjaxEdit.Main = function () {
$(".mw-editsection > .mw-editsection-bracket:contains(']')").each(function () {
var btn = $('<a/>').text("Ædit").on("click", function () { window.AjaxEdit.Click(this) });
$(this).before(!isVector2022 ? ", " : "").before(btn);
});
};
if (mw.config.get('wgAction') == "view")
$(window.AjaxEdit.Main);
})();
// </nowiki>
eczkpa0tlqnt9ygeasfy4grqwh42u0e
89022
89021
2026-08-06T23:34:10Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-AjaxEdit.js]]
89021
javascript
text/javascript
// AjaxEdit
// <nowiki>
"use strict";
// Description : With a new button "Ædit" one can edit specific sections without reloading the whole page.
// Supports TabbedLanguages gadget
// Dependencies: mediawiki.util, mediawiki.Uri, mediawiki.user, mediawiki.api
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, sub:true, esversion:5, esnext:false */
/* global mw, $ */
(function ajaxEditIife() {
window.AjaxEdit = {};
window.AjaxEdit.scriptLink = "";
if (mw.config.get("wgSiteName") !== "Wiktionary")
window.AjaxEdit.scriptLink += "wikt:";
if (mw.config.get("wgContentLanguage") !== "en")
window.AjaxEdit.scriptLink += "en:";
window.AjaxEdit.scriptLink += "MediaWiki:Gadget-AjaxEdit.js";
var $document = $(document);
window.AjaxEdit.FireEvents = function () {
// fire page load events like the [quotations ▼] thing
// see https://github.com/wikimedia/mediawiki/blob/master/resources/src/mediawiki.action/mediawiki.action.edit.preview.js
mw.hook("wikipage.content").fire($document);
mw.hook("wikipage.categories").fire($document);
};
var isVector2022 = mw.config.get("skin") === "vector-2022";
window.AjaxEdit.Click = function (ajaxEditAnchor) {
if (ajaxEditAnchor.hasEditBox) {
mw.notify("This AjaxEdit edit box is already open.");
return;
} else if (document.getElementById("ajaxedit-wrapper")) {
mw.notify("An AjaxEdit edit box is already open on this page.");
return;
} else {
ajaxEditAnchor.hasEditBox = true;
}
var elements = {};
var _query = new URL($(ajaxEditAnchor)
.parent()
.find("a[href*='action=edit']")
.attr("href"), location.href).searchParams,
title = _query.get("title"),
section = _query.get("section");
section = section.replace("T-", ""); //transclusions...
var hdr = $(ajaxEditAnchor).parent().parent();
var sectionName = hdr.children(":header").first().text();
if (window.tabbedLanguages && !hdr.is(":header")) {
sectionName = window.tabbedLanguages[window.currentLanguageTab];
}
var data = { action: 'raw', title: title, section: section };
$.get(mw.util.wikiScript('index'), data).then(function (wikitext) {
var rowHeight = Math.min(15, 1 + wikitext.split("\n").length);
elements.textarea = $("<textarea/>")
.attr({ rows: rowHeight })
.addClass("mw-editfont-monospace")
.text(wikitext);
elements.textareaSummary = $("<input/>")
.attr({
type: "text",
maxlength: 500,
spellcheck: true,
name: "wpSummary",
})
.keydown(event, function () {
if (event.keyCode == 13) { // on enter
elements.saveButton.click();
}
});
function checkboxAndLabel(id, text) {
var checkbox = $("<input/>")
.attr({
"id": id,
"type": "checkbox",
});
var label = $("<label/>")
.attr("for", id)
.text(text);
return [checkbox, label];
}
var minorEdit = checkboxAndLabel("ajaxedit-minor-edit-checkbox", "Minor edit");
elements.minorEditCheckbox = minorEdit[0], elements.minorEditLabel = minorEdit[1];
elements.saveButton = $("<button/>")
.text("Save")
.click(function () {
elements.textarea.attr('disabled', true);
elements.textareaSummary.attr('disabled', true);
window.AjaxEdit.Save(section, sectionName, elements.textarea.val(), title, elements.textareaSummary.val(), elements.minorEditCheckbox.prop("checked"))
.then(function (apiSaveResponse) {
if (apiSaveResponse.error) return; //if abusefilter was triggered
new mw.Api().get({ page: mw.config.get("wgPageName"), action: "parse", prop: "text|categorieshtml" })
.then(function (newHtml) {
$("#ajaxedit-wrapper").remove();
$("#mw-content-text").html(newHtml.parse.text["*"]);
$("#catlinks").replaceWith(newHtml.parse.categorieshtml["*"]);
window.AjaxEdit.FireEvents();
mw.loader.moduleRegistry["site"].state = "registered";
//mw.loader.moduleRegistry["site"].version="generate-unique-guid-here";
mw.loader.moduleRegistry["site"].script = undefined;
mw.loader.using("site", function () { });
window.AjaxEdit.Submain();
});
});
})
.css("margin-left", "3px");
elements.cancelButton = $('<button/>')
.text("Cancel")
.click(function () {
$("#ajaxedit-wrapper").remove();
ajaxEditAnchor.hasEditBox = false;
});
elements.previewDiv = $("<div/>")
.css({
"border-style": "solid", "border-width": "1px 1px 1px 4px",
"border-radius": "0.33em", "border-color": "#a3d3ff",
})
.hide();
function timestamp() {
return new Date().toISOString();
}
var previousText;
var previousTextTimestamp = timestamp();
function loadPreview() {
var newText = elements.textarea.val();
var textTimestamp = timestamp();
if (!previousText || previousText != newText) {
new mw.Api()
.parse(
newText,
{ title: mw.config.get("wgPageName"), pst: "true", preview: "true", sectionpreview: "true", disableeditsection: "true" }
)
.done(function (html) {
if (textTimestamp > previousTextTimestamp) {
elements.previewDiv.html(html);
window.AjaxEdit.FireEvents();
previousTextTimestamp = textTimestamp;
}
});
}
previousText = newText;
}
var livePreview = checkboxAndLabel("ajaxedit-live-preview-checkbox", "Live preview");
elements.livePreviewCheckbox = livePreview[0], elements.livePreviewLabel = livePreview[1];
elements.livePreviewCheckbox.on("change", function () {
if ($(this).prop("checked")) {
elements.previewDiv.show("slow");
loadPreview();
this.previewIntervalId = setInterval(loadPreview, 500);
} else {
clearInterval(this.previewIntervalId);
elements.previewDiv.hide("fast");
elements.previewDiv.empty();
}
});
var wrapper = $("<div/>")
.attr("id", "ajaxedit-wrapper")
.css({ width: "auto", "margin": 0, "overflow": "hidden" })
.append(elements.textarea)
.append(elements.textareaSummary)
.append(elements.saveButton)
.append(elements.livePreviewCheckbox)
.append(elements.livePreviewLabel)
.append(elements.minorEditCheckbox)
.append(elements.minorEditLabel)
.append(elements.cancelButton)
.append(elements.previewDiv);
//tabbed languages support
if ($(ajaxEditAnchor).is("#tabstable .editlangsection a")) {
$(".languageContainer:not(:hidden)")
.first()
.prepend(wrapper);
} else {
hdr.after(wrapper);
}
});
};
/*
* Define edit summary template with window.AjaxEditSummary. For instance:
window.AjaxEditSummary = "Æ $sectionlink $summary";
*/
window.AjaxEdit.Save = function (sectionID, sectionName, sectionText, title, summary, minorEdit) {
var finalSummary;
if (typeof window.AjaxEditSummary === "string") {
var usedSummary = false;
finalSummary = window.AjaxEditSummary.replace(
/\$([a-zA-Z_][a-zA-Z0-9_]*)/g,
function (fullMatch, name) {
switch (name) {
case "summary": {
usedSummary = true;
return summary;
}
case "scriptlink": return window.AjaxEdit.scriptLink;
case "sectionname": return sectionText;
case "sectionlink": return "/* " + sectionName + " */";
default: {
mw.notify(
"Your edit summary template (window.AjaxEditSummary) contains an unknown variable name. Variable name: "
+ name + ". Choose from summary, scriptlink, sectionname, sectionlink."
);
return "";
}
}
});
if (!usedSummary) {
mw.notify(
"Your edit summary template (window.AjaxEditSummary) doesn't use the edit summary you supplied, so your edit summary will be appended to the template: "
+ window.AjaxEditSummary);
finalSummary += " " + summary;
}
} else {
finalSummary = "/* " + sectionName + " */ " + summary + " (using [[" + window.AjaxEdit.scriptLink + "|AjaxEdit]])";
}
var data = {
format: 'json',
action: 'edit',
title: title,
section: sectionID,
summary: finalSummary,
text: sectionText,
token: mw.user.tokens.get('csrfToken'),
minor: minorEdit,
};
return new mw.Api().post(data).then(function (response) {
if (response && response.edit && response.edit.result == 'Success') {
mw.notify("successful");
} else if (response && response.error) {
mw.notify('Error: API returned error code "' + response.error.code + '": ' + response.error.info);
}
return response;
}).fail(function (xhr) {
mw.notify('Error: Request failed.');
$('#ajaxedit-wrapper *').attr('disabled', false);
});
};
window.AjaxEdit.Submain = function () {
$(".mw-body-content .mw-editsection > .mw-editsection-bracket:contains(']')").each(function () {
var btn = $('<a/>').text("Ædit").on("click", function () { window.AjaxEdit.Click(this) });
$(this).before(!isVector2022 ? ", " : "").before(btn);
});
};
window.AjaxEdit.Main = function () {
$(".mw-editsection > .mw-editsection-bracket:contains(']')").each(function () {
var btn = $('<a/>').text("Ædit").on("click", function () { window.AjaxEdit.Click(this) });
$(this).before(!isVector2022 ? ", " : "").before(btn);
});
};
if (mw.config.get('wgAction') == "view")
$(window.AjaxEdit.Main);
})();
// </nowiki>
eczkpa0tlqnt9ygeasfy4grqwh42u0e
MediaWiki:Gadget-EditHere.js
8
18010
89023
2026-06-18T17:04:16Z
en>Fenakhay
0
Undid revision [[Special:Diff/91219463|91219463]] by [[Special:Contributions/Fenakhay|Fenakhay]] ([[User talk:Fenakhay|talk]])
89023
javascript
text/javascript
// <nowiki>
/** Edit Here **/
// Edit sections of a page without leaving the article
// Based on [[en:w:User:BrandonXLF/QuickEdit]] by [[en:w:User:BrandonXLF]]
(function () {
let mobile = mw.config.get('skin') === 'minerva';
let titleRegexp = new RegExp(
mw.config.get('wgArticlePath').replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\\\$1/, '([^?]+)') +
'|[?&]title=([^&#]*)'
);
let apiSingleton;
const LIVE_PREVIEW_KEY = 'edithere-live-preview';
function getLivePreviewPref() {
try {
return localStorage.getItem(LIVE_PREVIEW_KEY) === '1';
} catch (e) {
return false;
}
}
function setLivePreviewPref(value) {
try {
localStorage.setItem(LIVE_PREVIEW_KEY, value ? '1' : '0');
} catch (e) { /* empty */ }
}
function api(func, params) {
if (!apiSingleton) {
apiSingleton = new mw.Api();
}
$.extend(params, {
errorformat: 'html',
errorlang: mw.config.get('wgUserLanguage'),
errorsuselocal: true
});
return apiSingleton[func](params).fail(function (_, data) {
mw.notify(apiSingleton.getErrorMessage(data), {
type: 'error',
tag: 'edithere'
});
});
}
function getPageInfo(title, sectionID) {
return api('get', {
action: 'query',
curtimestamp: 1,
prop: 'revisions',
indexpageids: 1,
titles: title,
rvprop: ['timestamp', 'content'],
rvslots: 'main',
rvsection: sectionID
}).then(function (res) {
let rev = res.query.pages[res.query.pageids[0]].revisions[0];
return {
start: res.curtimestamp,
base: rev.timestamp,
full: rev.slots.main['*']
};
});
}
function getPreviewCallback(editor) {
editor.children('.preview').remove();
new OO.ui.ProgressBarWidget().$element.css({
maxWidth: '100%',
borderRadius: '0',
boxShadow: 'none',
margin: '8px 0'
}).addClass('preview').appendTo(editor);
return function (html) {
editor.children('.preview').remove();
const $preview = $('<div>')
.html(html)
.addClass('editHere-borderColorBase').css({
borderWidth: '1px',
borderStyle: 'solid',
padding: '8px',
overflowX: 'hidden'
})
.addClass('preview')
.appendTo(editor);
mw.hook('wikipage.content').fire($preview);
};
}
function showCompare(editor, title, from, to) {
mw.loader.load('mediawiki.diff.styles');
api('post', {
action: 'compare',
fromslots: 'main',
'fromtext-main': from,
fromtitle: title,
frompst: 'true',
toslots: 'main',
'totext-main': to,
totitle: title,
topst: 'true'
}).then(function (r) {
return r.compare['*'] ? $('<table>').addClass('diff').append(
$('<colgroup>').append(
$('<col>').addClass('diff-marker'),
$('<col>').addClass('diff-content'),
$('<col>').addClass('diff-marker'),
$('<col>').addClass('diff-content')
)
).append(r.compare['*']) : 'No differences.';
}).then(getPreviewCallback(editor));
}
// Parts taken from EditPage::extractSectionTitle and Parser::stripSectionName
function getSectionSummary(text) {
let match = text.match(/^(=+)(.+)\1\s*(\n|$)/);
return !match ? '' : '/* ' + match[2].trim()
// Strip internal link markup
.replace(/\[\[:?([^[|]+)\|([^[]+)\]\]/g, '$2')
.replace(/\[\[:?([^[]+)\|?\]\]/g, '$1')
// Strip external link markup
.replace(new RegExp('\\[(?:' + mw.config.get('wgUrlProtocols') + ')([^ ]+?) ([^\\[]+)\\]', 'ig'), '$2')
// Remove wikitext quotes
.replace(/(''|'''|''''')(?!')/g, '')
// Strip HTML tags
.replace(/<[^>]+?>/g, '') + ' */ ';
}
function showEditor($element) {
let progress = new OO.ui.ProgressBarWidget();
// https://www.mediawiki.org/wiki/Heading_HTML_changes
// Cannot use .closest() because DiscussionTools nests an h2 within a .mw-heading
let heading = $element.parents(':header, .mw-heading').last();
let matcher = heading.nextUntil.bind(heading);
let inserter = heading.after.bind(heading);
let targetEl = $element.siblings('.edithere-target').last();
let titleMatch = targetEl.attr('href').match(titleRegexp);
let title = decodeURIComponent(titleMatch[1] || titleMatch[2]);
let sectionID = /[?&]v?e?section=T?-?(\d*)/.exec(targetEl.attr('href'))[1];
if (!heading.closest('.mw-parser-output').length) {
let $articleContent = $('#mw-content-text .mw-parser-output');
matcher = function (selector) {
let $child = $articleContent.children(selector).first();
if ($child.length) {
return $child.prevAll();
}
return $articleContent.children();
};
inserter = $articleContent.prepend.bind($articleContent);
}
inserter(progress.$element.css({
maxWidth: '100%',
borderRadius: '0',
boxShadow: 'none'
}));
$element.addClass('edithere-loading');
$('.edithere-hide').removeClass('edithere-hide');
$('.edithere-heading').removeClass('edithere-heading');
$('#edithere-editor').remove();
getPageInfo(title, sectionID).then(function (r) {
var start = r.start,
base = r.base,
full = r.full,
saving = false,
expanded = false,
remainderStart = full.match(/\n=+.+=+(?:\n|$)/),
part = remainderStart ? full.substring(0, remainderStart.index) : full,
remainder = remainderStart ? full.substring(remainderStart.index) : '',
level = 0,
editor;
full.replace(/^(=+).+?(=+)(?:\n|$)/, function (m, a, b) {
level = Math.min(a.length, b.length);
return m;
});
var levelMatch = 'h1';
for (var i = 2; i <= level; i++)
levelMatch += ', h' + i + ':has(*), .mw-heading' + i;
var partSection = matcher(':header:has(*), .mw-heading'),
fullSection = matcher(levelMatch),
textarea = new OO.ui.MultilineTextInputWidget({
rows: 1,
maxRows: 20,
autosize: true,
value: part
}),
summary = new OO.ui.TextInputWidget({
value: getSectionSummary(part)
}),
minor = new OO.ui.CheckboxInputWidget({
accessKey: 'i',
}),
save = new OO.ui.ButtonInputWidget({
label: 'Save',
title: 'Save your changes',
flags: ['primary', 'progressive'],
accessKey: 's'
}),
preview = new OO.ui.ButtonInputWidget({
label: 'Preview',
title: 'Preview the new wikitext',
accessKey: 'p'
}),
compare = new OO.ui.ButtonInputWidget({
label: 'Compare',
title: 'View the difference between the current revision and your revision',
accessKey: 'v'
}),
cancel = new OO.ui.ButtonInputWidget({
useInputTag: true,
label: 'Cancel',
title: 'Close the edit form and discard changes',
flags: ['secondary', 'destructive']
}),
more = new OO.ui.ButtonInputWidget({
label: '+',
title: 'Edit the entire section (including subsections)'
}),
livePreview = new OO.ui.CheckboxInputWidget({
selected: getLivePreviewPref(),
title: 'Automatically update the preview as you type'
}),
livePreviewField = new OO.ui.FieldLayout(livePreview, {
label: 'Live preview',
align: 'inline'
}),
buttons = new OO.ui.HorizontalLayout({
items: [save, preview, livePreviewField, compare, cancel]
});
livePreviewField.$element.css({
display: 'flex',
alignItems: 'center'
});
if (part != full) {
buttons.addItems([more], 4);
}
partSection.addClass('edithere-hide');
heading.addClass('edithere-heading');
$element.removeClass('edithere-loading');
progress.$element.remove();
textarea.$input.css({
borderRadius: '0'
});
summary.on('enter', function () {
save.emit('click');
});
save.on('click', function () {
if (saving) return;
var fullText = textarea.getValue() + (expanded ? '' : remainder);
saving = true;
save.setLabel('Saving...');
compare.setDisabled(true);
preview.setDisabled(true);
cancel.setDisabled(true);
more.setDisabled(true);
api('postWithEditToken', {
action: 'edit',
title: title,
section: sectionID,
summary: summary.getValue(),
text: fullText,
minor: minor.isSelected() ? true : undefined,
notminor: minor.isSelected() ? undefined : true,
starttimestamp: start,
basetimestamp: base
}).then(function () {
api('get', {
action: 'parse',
page: mw.config.get('wgPageName'),
prop: ['text', 'categorieshtml']
}).then(function (r) {
var contentText = $('#mw-content-text'),
catLinks = $('#catlinks');
contentText.find('.mw-parser-output').replaceWith(r.parse.text['*']);
mw.hook('wikipage.content').fire(contentText);
catLinks.replaceWith(r.parse.categorieshtml['*']);
mw.hook('wikipage.categories').fire(catLinks);
saving = false;
});
}, function (code) {
if (code == 'editconflict') {
showEditConflict(editor, title, sectionID, fullText).then(function (r) {
start = r.start;
base = r.base;
textarea = r.textarea;
expanded = true;
});
}
compare.setDisabled(false);
preview.setDisabled(false);
cancel.setDisabled(false);
more.setDisabled(expanded);
saving = false;
save.setLabel('Save');
});
});
var livePreviewTimer;
function runPreview() {
return api('post', {
action: 'parse',
title: title,
prop: 'text',
pst: 'true',
disablelimitreport: 'true',
disableeditsection: 'true',
sectionpreview: 'true',
disabletoc: 'true',
text: textarea.getValue()
}).then(function (r) {
return r.parse.text['*'] + '<div style="clear:both;"></div>';
}).then(getPreviewCallback(editor));
}
preview.on('click', runPreview);
textarea.on('change', function () {
if (!livePreview.isSelected()) return;
clearTimeout(livePreviewTimer);
livePreviewTimer = setTimeout(runPreview, 300);
});
livePreview.on('change', function (selected) {
setLivePreviewPref(selected);
clearTimeout(livePreviewTimer);
if (selected) {
runPreview();
}
});
compare.on('click', function () {
showCompare(editor, title, part + (expanded ? remainder : ''), textarea.getValue());
});
cancel.on('click', function () {
editor.remove();
heading.removeClass('edithere-heading');
fullSection.removeClass('edithere-hide');
});
more.on('click', function () {
expanded = true;
textarea.setValue(textarea.getValue() + remainder);
fullSection.addClass('edithere-hide');
more.setDisabled(true);
});
editor = $('<div id="edithere-editor">').css({
overflowX: 'hidden'
}).append(
$('<div>').addClass('editHere-backgroundColorInteractive editHere-colorBase editHere-borderColorBase').css({
borderBottomWidth: '1px',
borderBottomStyle: 'solid',
marginBottom: '8px'
}).append(
textarea.$element.css({
width: '100%',
maxWidth: '100%',
fontFamily: 'monospace, monospace'
}).addClass('edithere-textarea'),
$('<div>').addClass('editHere-borderColorBase').css({
borderStyle: 'solid',
borderWidth: '0 1px'
}).append(
$('<div>').css({
padding: '8px 4px 8px 8px',
display: 'table-cell',
verticalAlign: 'middle'
}).html('Edit summary:'),
summary.$element.css({
width: '100%',
maxWidth: '100%',
padding: '8px 0px',
display: 'table-cell',
verticalAlign: 'middle'
}),
new OO.ui.FieldLayout(minor, {
label: new OO.ui.HtmlSnippet('Minor edit?'),
align: 'inline'
}).$element.css({
padding: '8px 8px 8px 4px',
display: 'table-cell',
verticalAlign: 'middle'
})
),
buttons.$element.addClass('editHere-borderColorBase').css({
borderStyle: 'solid',
borderWidth: '0 1px',
padding: '0 8px 8px'
}),
title !== mw.config.get('wgPageName') ? $('<div>').addClass('editHere-borderColorBase').css({
borderStyle: 'solid',
borderWidth: '0 1px',
padding: '0 8px 8px'
}).append(
'Editing page: ',
$('<a>').attr('href', mw.config.get('wgArticlePath').replace('$1', title)).css({
fontWeight: 'bold'
}).text(title.replace(/_/g, ' '))
) : undefined
)
);
inserter(editor);
// Fix cases when editing a section next to two floating images like at
// https://en.wikipedia.org/w/index.php?title=Elevation&oldid=1235703526#Aviation
textarea.updatePosition();
if (livePreview.isSelected()) {
runPreview();
}
}, function () {
$element.removeClass('edithere-loading');
progress.$element.remove();
});
}
function showEditConflict(editor, title, sectionID, text) {
return getPageInfo(title, sectionID).then(function (r) {
var textarea = new OO.ui.MultilineTextInputWidget({
rows: 1,
maxRows: 20,
autosize: true,
value: r.full
}),
textarea2 = new OO.ui.MultilineTextInputWidget({
rows: 1,
maxRows: 20,
autosize: true,
value: text,
});
function syncSize() {
textarea.styleHeight = -1;
textarea.adjustSize(true);
textarea2.styleHeight = -1;
textarea2.adjustSize(true);
var height = Math.max(textarea.$input.height(), textarea2.$input.height());
textarea.$input.height(height);
textarea2.$input.height(height);
}
textarea.$input.css({
borderRadius: '0'
});
editor.find('> :first-child > :first-child').remove();
$('<table>').addClass('editHere-borderColorBase').css({
width: '100%',
borderWidth: '1px',
borderStyle: 'solid',
borderBottom: 'none',
borderSpacing: '0',
margin: '0 !important'
}).append(
$('<tr>').append(
$('<th>').css({
width: '50%',
paddingTop: '4px'
}).text('Their version (to be saved)'),
$('<th>').css({
width: '50%',
paddingTop: '4px'
}).text('Your version')
),
$('<tr>').append(
$('<td>').css({
width: '50%',
padding: '4px 4px 0 8px'
}).append(
textarea.$element.css({
width: '100%',
maxWidth: '100%',
fontFamily: 'monospace, monospace'
})
),
$('<td>').css({
width: '50%',
padding: '4px 8px 0 4px'
}).append(
textarea2.$element.css({
width: '100%',
maxWidth: '100%',
fontFamily: 'monospace, monospace'
})
)
)
).prependTo(editor.find('> :first-child'));
textarea.on('change', syncSize);
textarea2.on('change', syncSize);
syncSize();
showCompare(editor, title, text, r.full);
r.textarea = textarea;
return r;
});
}
function clickHandler(event) {
const $element = $(event.target).closest('.edithere-editlink');
if (!$element.length || $element.hasClass('edithere-loading')) return;
event.preventDefault();
showEditor($element);
}
function addLinksToChildren($element) {
$element.find('#edithere-editor, .edithere-section').remove();
const $editHereLink = $('<a>')
.addClass('edithere-section edithere-editlink')
.attr('href', '#');
if (mobile) {
$editHereLink
.addClass('edithere-icon edithere-minerva-icon')
.attr('role', 'button')
.attr('tabindex', '0')
.append(
createSvg(20, 20)
.html('<path d="M20 14.4286L16.4 14.4286L16.4 7L14.6 7L14.6 14.4286L11 14.4286L15.5 20L20 14.4286Z" /><path fill-rule="evenodd" clip-rule="evenodd" d="M16.4 7.63119V7H15.7688L16.4 7.63119ZM14.6 10.1498L15.7094 9.04044L14.6 7.93109V10.1498ZM13.7688 5L18.4 5V6.31934L18.7092 6.00057C18.8955 5.81322 19 5.55978 19 5.2956C19 5.03143 18.8955 4.77799 18.7092 4.59063L15.3694 1.29078C15.182 1.10454 14.9286 1 14.6644 1C14.4002 1 14.1468 1.10454 13.9594 1.29078L11.9995 3.23069L13.7688 5ZM9.27453 15.4753L5.74979 19H1V14.2502L10.9596 4.29065L12.6 5.93109V12.1498L12.3212 12.4286H6.81374L9.27453 15.4753Z" />')
);
} else {
$editHereLink.text('edit here');
}
$element.find('.mw-editsection').each(function () {
$('[href*="section="]', this)
.last()
.after($editHereLink.clone(true))
.addClass('edithere-target');
});
}
function createSvg(
width,
height,
viewBoxWidth = width,
viewBoxHeight = height
) {
return $(document.createElementNS('http://www.w3.org/2000/svg', 'svg'))
.attr('width', width)
.attr('height', height)
.attr('viewBox', `0 0 ${viewBoxWidth} ${viewBoxHeight}`)
.attr('aria-hidden', 'true')
// https://en.wikipedia.org/wiki/Project:Dark_mode_(gadget)
.addClass('mw-invert');
}
$.when(mw.loader.using(['oojs-ui-core']), $.ready).done(function () {
const $body = $(document.body);
$body.on('click', clickHandler);
addLinksToChildren($body);
mw.hook('wikipage.content').add(addLinksToChildren);
});
mw.loader.addStyleTag(`
.editHere-backgroundColorInteractive {
background-color: var(--background-color-interactive, #eaecf0);
}
.editHere-colorBase {
color: var(--color-base, #202122);
}
.editHere-borderColorBase {
border-color: var(--border-color-base, #a2a9b1);
}
.skin-minerva .mw-editsection {
white-space: nowrap;
}
.skin-minerva .content .collapsible-heading .edithere-section {
visibility: hidden;
}
.skin-minerva .content .collapsible-heading.open-block .edithere-section {
visibility: visible;
}
.edithere-hide {
display: none !important;
}
.edithere-loading,
.edithere-heading {
color: #777;
}
.edithere-icon {
display: inline-flex;
align-items: center;
justify-content: center;
background-image: none;
svg {
fill: currentcolor;
}
}
.edithere-minerva-icon.edithere-minerva-icon {
color: var(--color-subtle, #54595d);
min-width: 44px;
min-height: 44px;
box-sizing: border-box;
border: 1px solid transparent;
border-radius: 2px;
}
.edithere-minerva-icon.edithere-minerva-icon:hover {
background-color: var(--background-color-button-quiet--hover,rgba(0, 24, 73, .027));
}
.edithere-minerva-icon.edithere-minerva-icon:active {
background-color: var(--background-color-button-quiet--active,rgba(0, 24, 73, .082));
border-color: var(--border-color-interactive, #72777d);
}
.edithere-minerva-icon.edithere-minerva-icon:focus {
outline: 1px solid transparent;
}
.edithere-minerva-icon.edithere-minerva-icon:focus:not(:active) {
border-color: var(--border-color-progressive--focus, #36c);
box-shadow: inset 0 0 0 1px var(--box-shadow-color-progressive--focus, #36c);
}`);
})();
// </nowiki>
919ikf58eonsxxpcy3ek8pkzunm1z6v
89024
89023
2026-08-06T23:34:20Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-EditHere.js]]
89023
javascript
text/javascript
// <nowiki>
/** Edit Here **/
// Edit sections of a page without leaving the article
// Based on [[en:w:User:BrandonXLF/QuickEdit]] by [[en:w:User:BrandonXLF]]
(function () {
let mobile = mw.config.get('skin') === 'minerva';
let titleRegexp = new RegExp(
mw.config.get('wgArticlePath').replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\\\$1/, '([^?]+)') +
'|[?&]title=([^&#]*)'
);
let apiSingleton;
const LIVE_PREVIEW_KEY = 'edithere-live-preview';
function getLivePreviewPref() {
try {
return localStorage.getItem(LIVE_PREVIEW_KEY) === '1';
} catch (e) {
return false;
}
}
function setLivePreviewPref(value) {
try {
localStorage.setItem(LIVE_PREVIEW_KEY, value ? '1' : '0');
} catch (e) { /* empty */ }
}
function api(func, params) {
if (!apiSingleton) {
apiSingleton = new mw.Api();
}
$.extend(params, {
errorformat: 'html',
errorlang: mw.config.get('wgUserLanguage'),
errorsuselocal: true
});
return apiSingleton[func](params).fail(function (_, data) {
mw.notify(apiSingleton.getErrorMessage(data), {
type: 'error',
tag: 'edithere'
});
});
}
function getPageInfo(title, sectionID) {
return api('get', {
action: 'query',
curtimestamp: 1,
prop: 'revisions',
indexpageids: 1,
titles: title,
rvprop: ['timestamp', 'content'],
rvslots: 'main',
rvsection: sectionID
}).then(function (res) {
let rev = res.query.pages[res.query.pageids[0]].revisions[0];
return {
start: res.curtimestamp,
base: rev.timestamp,
full: rev.slots.main['*']
};
});
}
function getPreviewCallback(editor) {
editor.children('.preview').remove();
new OO.ui.ProgressBarWidget().$element.css({
maxWidth: '100%',
borderRadius: '0',
boxShadow: 'none',
margin: '8px 0'
}).addClass('preview').appendTo(editor);
return function (html) {
editor.children('.preview').remove();
const $preview = $('<div>')
.html(html)
.addClass('editHere-borderColorBase').css({
borderWidth: '1px',
borderStyle: 'solid',
padding: '8px',
overflowX: 'hidden'
})
.addClass('preview')
.appendTo(editor);
mw.hook('wikipage.content').fire($preview);
};
}
function showCompare(editor, title, from, to) {
mw.loader.load('mediawiki.diff.styles');
api('post', {
action: 'compare',
fromslots: 'main',
'fromtext-main': from,
fromtitle: title,
frompst: 'true',
toslots: 'main',
'totext-main': to,
totitle: title,
topst: 'true'
}).then(function (r) {
return r.compare['*'] ? $('<table>').addClass('diff').append(
$('<colgroup>').append(
$('<col>').addClass('diff-marker'),
$('<col>').addClass('diff-content'),
$('<col>').addClass('diff-marker'),
$('<col>').addClass('diff-content')
)
).append(r.compare['*']) : 'No differences.';
}).then(getPreviewCallback(editor));
}
// Parts taken from EditPage::extractSectionTitle and Parser::stripSectionName
function getSectionSummary(text) {
let match = text.match(/^(=+)(.+)\1\s*(\n|$)/);
return !match ? '' : '/* ' + match[2].trim()
// Strip internal link markup
.replace(/\[\[:?([^[|]+)\|([^[]+)\]\]/g, '$2')
.replace(/\[\[:?([^[]+)\|?\]\]/g, '$1')
// Strip external link markup
.replace(new RegExp('\\[(?:' + mw.config.get('wgUrlProtocols') + ')([^ ]+?) ([^\\[]+)\\]', 'ig'), '$2')
// Remove wikitext quotes
.replace(/(''|'''|''''')(?!')/g, '')
// Strip HTML tags
.replace(/<[^>]+?>/g, '') + ' */ ';
}
function showEditor($element) {
let progress = new OO.ui.ProgressBarWidget();
// https://www.mediawiki.org/wiki/Heading_HTML_changes
// Cannot use .closest() because DiscussionTools nests an h2 within a .mw-heading
let heading = $element.parents(':header, .mw-heading').last();
let matcher = heading.nextUntil.bind(heading);
let inserter = heading.after.bind(heading);
let targetEl = $element.siblings('.edithere-target').last();
let titleMatch = targetEl.attr('href').match(titleRegexp);
let title = decodeURIComponent(titleMatch[1] || titleMatch[2]);
let sectionID = /[?&]v?e?section=T?-?(\d*)/.exec(targetEl.attr('href'))[1];
if (!heading.closest('.mw-parser-output').length) {
let $articleContent = $('#mw-content-text .mw-parser-output');
matcher = function (selector) {
let $child = $articleContent.children(selector).first();
if ($child.length) {
return $child.prevAll();
}
return $articleContent.children();
};
inserter = $articleContent.prepend.bind($articleContent);
}
inserter(progress.$element.css({
maxWidth: '100%',
borderRadius: '0',
boxShadow: 'none'
}));
$element.addClass('edithere-loading');
$('.edithere-hide').removeClass('edithere-hide');
$('.edithere-heading').removeClass('edithere-heading');
$('#edithere-editor').remove();
getPageInfo(title, sectionID).then(function (r) {
var start = r.start,
base = r.base,
full = r.full,
saving = false,
expanded = false,
remainderStart = full.match(/\n=+.+=+(?:\n|$)/),
part = remainderStart ? full.substring(0, remainderStart.index) : full,
remainder = remainderStart ? full.substring(remainderStart.index) : '',
level = 0,
editor;
full.replace(/^(=+).+?(=+)(?:\n|$)/, function (m, a, b) {
level = Math.min(a.length, b.length);
return m;
});
var levelMatch = 'h1';
for (var i = 2; i <= level; i++)
levelMatch += ', h' + i + ':has(*), .mw-heading' + i;
var partSection = matcher(':header:has(*), .mw-heading'),
fullSection = matcher(levelMatch),
textarea = new OO.ui.MultilineTextInputWidget({
rows: 1,
maxRows: 20,
autosize: true,
value: part
}),
summary = new OO.ui.TextInputWidget({
value: getSectionSummary(part)
}),
minor = new OO.ui.CheckboxInputWidget({
accessKey: 'i',
}),
save = new OO.ui.ButtonInputWidget({
label: 'Save',
title: 'Save your changes',
flags: ['primary', 'progressive'],
accessKey: 's'
}),
preview = new OO.ui.ButtonInputWidget({
label: 'Preview',
title: 'Preview the new wikitext',
accessKey: 'p'
}),
compare = new OO.ui.ButtonInputWidget({
label: 'Compare',
title: 'View the difference between the current revision and your revision',
accessKey: 'v'
}),
cancel = new OO.ui.ButtonInputWidget({
useInputTag: true,
label: 'Cancel',
title: 'Close the edit form and discard changes',
flags: ['secondary', 'destructive']
}),
more = new OO.ui.ButtonInputWidget({
label: '+',
title: 'Edit the entire section (including subsections)'
}),
livePreview = new OO.ui.CheckboxInputWidget({
selected: getLivePreviewPref(),
title: 'Automatically update the preview as you type'
}),
livePreviewField = new OO.ui.FieldLayout(livePreview, {
label: 'Live preview',
align: 'inline'
}),
buttons = new OO.ui.HorizontalLayout({
items: [save, preview, livePreviewField, compare, cancel]
});
livePreviewField.$element.css({
display: 'flex',
alignItems: 'center'
});
if (part != full) {
buttons.addItems([more], 4);
}
partSection.addClass('edithere-hide');
heading.addClass('edithere-heading');
$element.removeClass('edithere-loading');
progress.$element.remove();
textarea.$input.css({
borderRadius: '0'
});
summary.on('enter', function () {
save.emit('click');
});
save.on('click', function () {
if (saving) return;
var fullText = textarea.getValue() + (expanded ? '' : remainder);
saving = true;
save.setLabel('Saving...');
compare.setDisabled(true);
preview.setDisabled(true);
cancel.setDisabled(true);
more.setDisabled(true);
api('postWithEditToken', {
action: 'edit',
title: title,
section: sectionID,
summary: summary.getValue(),
text: fullText,
minor: minor.isSelected() ? true : undefined,
notminor: minor.isSelected() ? undefined : true,
starttimestamp: start,
basetimestamp: base
}).then(function () {
api('get', {
action: 'parse',
page: mw.config.get('wgPageName'),
prop: ['text', 'categorieshtml']
}).then(function (r) {
var contentText = $('#mw-content-text'),
catLinks = $('#catlinks');
contentText.find('.mw-parser-output').replaceWith(r.parse.text['*']);
mw.hook('wikipage.content').fire(contentText);
catLinks.replaceWith(r.parse.categorieshtml['*']);
mw.hook('wikipage.categories').fire(catLinks);
saving = false;
});
}, function (code) {
if (code == 'editconflict') {
showEditConflict(editor, title, sectionID, fullText).then(function (r) {
start = r.start;
base = r.base;
textarea = r.textarea;
expanded = true;
});
}
compare.setDisabled(false);
preview.setDisabled(false);
cancel.setDisabled(false);
more.setDisabled(expanded);
saving = false;
save.setLabel('Save');
});
});
var livePreviewTimer;
function runPreview() {
return api('post', {
action: 'parse',
title: title,
prop: 'text',
pst: 'true',
disablelimitreport: 'true',
disableeditsection: 'true',
sectionpreview: 'true',
disabletoc: 'true',
text: textarea.getValue()
}).then(function (r) {
return r.parse.text['*'] + '<div style="clear:both;"></div>';
}).then(getPreviewCallback(editor));
}
preview.on('click', runPreview);
textarea.on('change', function () {
if (!livePreview.isSelected()) return;
clearTimeout(livePreviewTimer);
livePreviewTimer = setTimeout(runPreview, 300);
});
livePreview.on('change', function (selected) {
setLivePreviewPref(selected);
clearTimeout(livePreviewTimer);
if (selected) {
runPreview();
}
});
compare.on('click', function () {
showCompare(editor, title, part + (expanded ? remainder : ''), textarea.getValue());
});
cancel.on('click', function () {
editor.remove();
heading.removeClass('edithere-heading');
fullSection.removeClass('edithere-hide');
});
more.on('click', function () {
expanded = true;
textarea.setValue(textarea.getValue() + remainder);
fullSection.addClass('edithere-hide');
more.setDisabled(true);
});
editor = $('<div id="edithere-editor">').css({
overflowX: 'hidden'
}).append(
$('<div>').addClass('editHere-backgroundColorInteractive editHere-colorBase editHere-borderColorBase').css({
borderBottomWidth: '1px',
borderBottomStyle: 'solid',
marginBottom: '8px'
}).append(
textarea.$element.css({
width: '100%',
maxWidth: '100%',
fontFamily: 'monospace, monospace'
}).addClass('edithere-textarea'),
$('<div>').addClass('editHere-borderColorBase').css({
borderStyle: 'solid',
borderWidth: '0 1px'
}).append(
$('<div>').css({
padding: '8px 4px 8px 8px',
display: 'table-cell',
verticalAlign: 'middle'
}).html('Edit summary:'),
summary.$element.css({
width: '100%',
maxWidth: '100%',
padding: '8px 0px',
display: 'table-cell',
verticalAlign: 'middle'
}),
new OO.ui.FieldLayout(minor, {
label: new OO.ui.HtmlSnippet('Minor edit?'),
align: 'inline'
}).$element.css({
padding: '8px 8px 8px 4px',
display: 'table-cell',
verticalAlign: 'middle'
})
),
buttons.$element.addClass('editHere-borderColorBase').css({
borderStyle: 'solid',
borderWidth: '0 1px',
padding: '0 8px 8px'
}),
title !== mw.config.get('wgPageName') ? $('<div>').addClass('editHere-borderColorBase').css({
borderStyle: 'solid',
borderWidth: '0 1px',
padding: '0 8px 8px'
}).append(
'Editing page: ',
$('<a>').attr('href', mw.config.get('wgArticlePath').replace('$1', title)).css({
fontWeight: 'bold'
}).text(title.replace(/_/g, ' '))
) : undefined
)
);
inserter(editor);
// Fix cases when editing a section next to two floating images like at
// https://en.wikipedia.org/w/index.php?title=Elevation&oldid=1235703526#Aviation
textarea.updatePosition();
if (livePreview.isSelected()) {
runPreview();
}
}, function () {
$element.removeClass('edithere-loading');
progress.$element.remove();
});
}
function showEditConflict(editor, title, sectionID, text) {
return getPageInfo(title, sectionID).then(function (r) {
var textarea = new OO.ui.MultilineTextInputWidget({
rows: 1,
maxRows: 20,
autosize: true,
value: r.full
}),
textarea2 = new OO.ui.MultilineTextInputWidget({
rows: 1,
maxRows: 20,
autosize: true,
value: text,
});
function syncSize() {
textarea.styleHeight = -1;
textarea.adjustSize(true);
textarea2.styleHeight = -1;
textarea2.adjustSize(true);
var height = Math.max(textarea.$input.height(), textarea2.$input.height());
textarea.$input.height(height);
textarea2.$input.height(height);
}
textarea.$input.css({
borderRadius: '0'
});
editor.find('> :first-child > :first-child').remove();
$('<table>').addClass('editHere-borderColorBase').css({
width: '100%',
borderWidth: '1px',
borderStyle: 'solid',
borderBottom: 'none',
borderSpacing: '0',
margin: '0 !important'
}).append(
$('<tr>').append(
$('<th>').css({
width: '50%',
paddingTop: '4px'
}).text('Their version (to be saved)'),
$('<th>').css({
width: '50%',
paddingTop: '4px'
}).text('Your version')
),
$('<tr>').append(
$('<td>').css({
width: '50%',
padding: '4px 4px 0 8px'
}).append(
textarea.$element.css({
width: '100%',
maxWidth: '100%',
fontFamily: 'monospace, monospace'
})
),
$('<td>').css({
width: '50%',
padding: '4px 8px 0 4px'
}).append(
textarea2.$element.css({
width: '100%',
maxWidth: '100%',
fontFamily: 'monospace, monospace'
})
)
)
).prependTo(editor.find('> :first-child'));
textarea.on('change', syncSize);
textarea2.on('change', syncSize);
syncSize();
showCompare(editor, title, text, r.full);
r.textarea = textarea;
return r;
});
}
function clickHandler(event) {
const $element = $(event.target).closest('.edithere-editlink');
if (!$element.length || $element.hasClass('edithere-loading')) return;
event.preventDefault();
showEditor($element);
}
function addLinksToChildren($element) {
$element.find('#edithere-editor, .edithere-section').remove();
const $editHereLink = $('<a>')
.addClass('edithere-section edithere-editlink')
.attr('href', '#');
if (mobile) {
$editHereLink
.addClass('edithere-icon edithere-minerva-icon')
.attr('role', 'button')
.attr('tabindex', '0')
.append(
createSvg(20, 20)
.html('<path d="M20 14.4286L16.4 14.4286L16.4 7L14.6 7L14.6 14.4286L11 14.4286L15.5 20L20 14.4286Z" /><path fill-rule="evenodd" clip-rule="evenodd" d="M16.4 7.63119V7H15.7688L16.4 7.63119ZM14.6 10.1498L15.7094 9.04044L14.6 7.93109V10.1498ZM13.7688 5L18.4 5V6.31934L18.7092 6.00057C18.8955 5.81322 19 5.55978 19 5.2956C19 5.03143 18.8955 4.77799 18.7092 4.59063L15.3694 1.29078C15.182 1.10454 14.9286 1 14.6644 1C14.4002 1 14.1468 1.10454 13.9594 1.29078L11.9995 3.23069L13.7688 5ZM9.27453 15.4753L5.74979 19H1V14.2502L10.9596 4.29065L12.6 5.93109V12.1498L12.3212 12.4286H6.81374L9.27453 15.4753Z" />')
);
} else {
$editHereLink.text('edit here');
}
$element.find('.mw-editsection').each(function () {
$('[href*="section="]', this)
.last()
.after($editHereLink.clone(true))
.addClass('edithere-target');
});
}
function createSvg(
width,
height,
viewBoxWidth = width,
viewBoxHeight = height
) {
return $(document.createElementNS('http://www.w3.org/2000/svg', 'svg'))
.attr('width', width)
.attr('height', height)
.attr('viewBox', `0 0 ${viewBoxWidth} ${viewBoxHeight}`)
.attr('aria-hidden', 'true')
// https://en.wikipedia.org/wiki/Project:Dark_mode_(gadget)
.addClass('mw-invert');
}
$.when(mw.loader.using(['oojs-ui-core']), $.ready).done(function () {
const $body = $(document.body);
$body.on('click', clickHandler);
addLinksToChildren($body);
mw.hook('wikipage.content').add(addLinksToChildren);
});
mw.loader.addStyleTag(`
.editHere-backgroundColorInteractive {
background-color: var(--background-color-interactive, #eaecf0);
}
.editHere-colorBase {
color: var(--color-base, #202122);
}
.editHere-borderColorBase {
border-color: var(--border-color-base, #a2a9b1);
}
.skin-minerva .mw-editsection {
white-space: nowrap;
}
.skin-minerva .content .collapsible-heading .edithere-section {
visibility: hidden;
}
.skin-minerva .content .collapsible-heading.open-block .edithere-section {
visibility: visible;
}
.edithere-hide {
display: none !important;
}
.edithere-loading,
.edithere-heading {
color: #777;
}
.edithere-icon {
display: inline-flex;
align-items: center;
justify-content: center;
background-image: none;
svg {
fill: currentcolor;
}
}
.edithere-minerva-icon.edithere-minerva-icon {
color: var(--color-subtle, #54595d);
min-width: 44px;
min-height: 44px;
box-sizing: border-box;
border: 1px solid transparent;
border-radius: 2px;
}
.edithere-minerva-icon.edithere-minerva-icon:hover {
background-color: var(--background-color-button-quiet--hover,rgba(0, 24, 73, .027));
}
.edithere-minerva-icon.edithere-minerva-icon:active {
background-color: var(--background-color-button-quiet--active,rgba(0, 24, 73, .082));
border-color: var(--border-color-interactive, #72777d);
}
.edithere-minerva-icon.edithere-minerva-icon:focus {
outline: 1px solid transparent;
}
.edithere-minerva-icon.edithere-minerva-icon:focus:not(:active) {
border-color: var(--border-color-progressive--focus, #36c);
box-shadow: inset 0 0 0 1px var(--box-shadow-color-progressive--focus, #36c);
}`);
})();
// </nowiki>
919ikf58eonsxxpcy3ek8pkzunm1z6v
MediaWiki:Gadget-FlagMovedPageForDeletion.js
8
18011
89025
2025-09-14T18:43:14Z
en>Ioaxxere
0
Update and cleanup to async/await
89025
javascript
text/javascript
// <nowiki>
// Idea: set a local storage item to remember which page got moved.
// Then, on the "other side", make an automated edit which replaces the redirect with {{d}}.
(async () => {
// Setup. Don't add the button if the user already has permissions to delete the page immediately.
if (document.querySelector(".movepage-wrapper") && !document.querySelector("#wpLeaveRedirect")) {
let firstCheckbox = document.querySelector(".movepage-wrapper .oo-ui-fieldLayout-align-inline");
firstCheckbox.insertAdjacentHTML("afterEnd", `<div id="flag-delete-field" class="oo-ui-layout oo-ui-labelElement oo-ui-fieldLayout oo-ui-fieldLayout-align-inline"><div class="oo-ui-fieldLayout-body"><span class="oo-ui-fieldLayout-field"><span id="flag-delete" class="oo-ui-widget oo-ui-widget-enabled oo-ui-inputWidget oo-ui-checkboxInputWidget"><input type="checkbox" tabindex="0" id="flag-delete-checkbox" class="oo-ui-inputWidget-input"><span class="oo-ui-checkboxInputWidget-checkIcon oo-ui-widget oo-ui-widget-enabled oo-ui-iconElement-icon oo-ui-icon-check oo-ui-iconElement oo-ui-labelElement-invisible oo-ui-iconWidget oo-ui-image-invert"></span></span></span><span class="oo-ui-fieldLayout-header"><label for="flag-delete-checkbox" class="oo-ui-labelElement-label">Flag the source page for deletion</label></span></div></div>`);
let flagDeleteCheckbox = document.querySelector("#flag-delete-checkbox");
let submitButton = document.querySelector(".movepage-wrapper button");
submitButton.addEventListener("click", () => {
if (flagDeleteCheckbox.checked) {
let oldPageTitle = document.querySelector(`[name="wpOldTitle"]`).value;
localStorage.setItem("flagDelete", oldPageTitle);
}
});
}
// Make the edit after the move has completed.
let sourcePageElem = document.querySelector("#movepage-oldlink");
let pageToFlagDelete = localStorage.getItem("flagDelete");
if (pageToFlagDelete && sourcePageElem && sourcePageElem.textContent === localStorage.getItem("flagDelete")) {
localStorage.removeItem("flagDelete");
let actionAPI = new mw.Api({ajax: {headers: { "Api-User-Agent": "Gadget developed by [[User:Ioaxxere]]"}}});
let targetPage = document.querySelector("#movepage-newlink").textContent;
let reason = "(no reason provided)";
let pageExists = false;
let ns = (new mw.Title(pageToFlagDelete)).namespace;
// 1. Make sure the moved page still exists (sometimes it gets deleted immediately)
let pageExistsQuery = await actionAPI.get({action: "query", titles: pageToFlagDelete, format: "json"});
if (pageExistsQuery.query.pages[-1] === undefined) {
pageExists = true;
// 2. Find out the edit summary through the logevents API.
let moveLogResponse = await actionAPI.get({
action: "query",
letitle: pageToFlagDelete,
list: "logevents",
letype: "move",
format: "json"
});
if (moveLogResponse.query.logevents[0] && moveLogResponse.query.logevents[0].comment)
reason = moveLogResponse.query.logevents[0].comment;
try {
// 3. Make the edit.
await actionAPI.postWithToken("csrf", {
action: "edit",
title: pageToFlagDelete,
summary: "Flagging for deletion after moving the page to [[:" + targetPage + "]]: " + reason + " ([[MediaWiki:Gadget-FlagMovedPageForDeletion.js|automated]])",
text: "{{d|Page moved to [[:" + targetPage + "]]: " + reason + "}}"
});
} catch (e) {
// 4. If there's an error, try again on /documentation.
if (ns === 8 || ns === 9 || ns === 10 || ns === 11 || ns === 828 || ns === 829) {
await actionAPI.postWithToken("csrf", {
action: "edit",
title: pageToFlagDelete + "/documentation",
summary: "Flagging for deletion after moving the page to [[:" + targetPage + "]]: " + reason + " ([[MediaWiki:Gadget-FlagMovedPageForDeletion.js|automated]])",
text: "{{d|Page moved to [[:" + targetPage + "]]: " + reason + "}}"
});
}
}
}
// 5. Display a message on success.
let a = document.createElement("a");
a.href = mw.util.getUrl(pageToFlagDelete);
a.textContent = pageToFlagDelete;
let message = document.createElement("span");
message.append(a, pageExists ? " has been successfully flagged for deletion." : " has been deleted.");
mw.notify(message);
}
})();
// </nowiki>
at6kg31pq5njx9y9yt3evgb0xu9goh0
89026
89025
2026-08-06T23:34:30Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:MediaWiki:Gadget-FlagMovedPageForDeletion.js]]
89025
javascript
text/javascript
// <nowiki>
// Idea: set a local storage item to remember which page got moved.
// Then, on the "other side", make an automated edit which replaces the redirect with {{d}}.
(async () => {
// Setup. Don't add the button if the user already has permissions to delete the page immediately.
if (document.querySelector(".movepage-wrapper") && !document.querySelector("#wpLeaveRedirect")) {
let firstCheckbox = document.querySelector(".movepage-wrapper .oo-ui-fieldLayout-align-inline");
firstCheckbox.insertAdjacentHTML("afterEnd", `<div id="flag-delete-field" class="oo-ui-layout oo-ui-labelElement oo-ui-fieldLayout oo-ui-fieldLayout-align-inline"><div class="oo-ui-fieldLayout-body"><span class="oo-ui-fieldLayout-field"><span id="flag-delete" class="oo-ui-widget oo-ui-widget-enabled oo-ui-inputWidget oo-ui-checkboxInputWidget"><input type="checkbox" tabindex="0" id="flag-delete-checkbox" class="oo-ui-inputWidget-input"><span class="oo-ui-checkboxInputWidget-checkIcon oo-ui-widget oo-ui-widget-enabled oo-ui-iconElement-icon oo-ui-icon-check oo-ui-iconElement oo-ui-labelElement-invisible oo-ui-iconWidget oo-ui-image-invert"></span></span></span><span class="oo-ui-fieldLayout-header"><label for="flag-delete-checkbox" class="oo-ui-labelElement-label">Flag the source page for deletion</label></span></div></div>`);
let flagDeleteCheckbox = document.querySelector("#flag-delete-checkbox");
let submitButton = document.querySelector(".movepage-wrapper button");
submitButton.addEventListener("click", () => {
if (flagDeleteCheckbox.checked) {
let oldPageTitle = document.querySelector(`[name="wpOldTitle"]`).value;
localStorage.setItem("flagDelete", oldPageTitle);
}
});
}
// Make the edit after the move has completed.
let sourcePageElem = document.querySelector("#movepage-oldlink");
let pageToFlagDelete = localStorage.getItem("flagDelete");
if (pageToFlagDelete && sourcePageElem && sourcePageElem.textContent === localStorage.getItem("flagDelete")) {
localStorage.removeItem("flagDelete");
let actionAPI = new mw.Api({ajax: {headers: { "Api-User-Agent": "Gadget developed by [[User:Ioaxxere]]"}}});
let targetPage = document.querySelector("#movepage-newlink").textContent;
let reason = "(no reason provided)";
let pageExists = false;
let ns = (new mw.Title(pageToFlagDelete)).namespace;
// 1. Make sure the moved page still exists (sometimes it gets deleted immediately)
let pageExistsQuery = await actionAPI.get({action: "query", titles: pageToFlagDelete, format: "json"});
if (pageExistsQuery.query.pages[-1] === undefined) {
pageExists = true;
// 2. Find out the edit summary through the logevents API.
let moveLogResponse = await actionAPI.get({
action: "query",
letitle: pageToFlagDelete,
list: "logevents",
letype: "move",
format: "json"
});
if (moveLogResponse.query.logevents[0] && moveLogResponse.query.logevents[0].comment)
reason = moveLogResponse.query.logevents[0].comment;
try {
// 3. Make the edit.
await actionAPI.postWithToken("csrf", {
action: "edit",
title: pageToFlagDelete,
summary: "Flagging for deletion after moving the page to [[:" + targetPage + "]]: " + reason + " ([[MediaWiki:Gadget-FlagMovedPageForDeletion.js|automated]])",
text: "{{d|Page moved to [[:" + targetPage + "]]: " + reason + "}}"
});
} catch (e) {
// 4. If there's an error, try again on /documentation.
if (ns === 8 || ns === 9 || ns === 10 || ns === 11 || ns === 828 || ns === 829) {
await actionAPI.postWithToken("csrf", {
action: "edit",
title: pageToFlagDelete + "/documentation",
summary: "Flagging for deletion after moving the page to [[:" + targetPage + "]]: " + reason + " ([[MediaWiki:Gadget-FlagMovedPageForDeletion.js|automated]])",
text: "{{d|Page moved to [[:" + targetPage + "]]: " + reason + "}}"
});
}
}
}
// 5. Display a message on success.
let a = document.createElement("a");
a.href = mw.util.getUrl(pageToFlagDelete);
a.textContent = pageToFlagDelete;
let message = document.createElement("span");
message.append(a, pageExists ? " has been successfully flagged for deletion." : " has been deleted.");
mw.notify(message);
}
})();
// </nowiki>
at6kg31pq5njx9y9yt3evgb0xu9goh0
Teamplaid:VectorTab
10
18012
89027
2025-09-09T16:26:26Z
en>Surjection
0
89027
wikitext
text/x-wiki
<templatestyles src="Template:VectorTab/style.css" /><span class="mw-parser-output"><span class="vectortab">{{{1|entry}}}</span></span><noinclude>{{documentation}}</noinclude>
fgq5vy1fsmsp8s911jec76ulnnfk4gq
89028
89027
2026-08-07T01:04:06Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Template:VectorTab]]
89027
wikitext
text/x-wiki
<templatestyles src="Template:VectorTab/style.css" /><span class="mw-parser-output"><span class="vectortab">{{{1|entry}}}</span></span><noinclude>{{documentation}}</noinclude>
fgq5vy1fsmsp8s911jec76ulnnfk4gq
Teamplaid:VectorTab/style.css
10
18013
89029
2025-09-09T16:28:46Z
en>Surjection
0
89029
sanitized-css
text/css
.vectortab {
padding-left: 0.4em;
padding-right: 0.4em;
background-color: var(--wikt-palette-lightblue, #d9ebff);
border: var(--wikt-palette-deepblue, #2f445c) 1px solid;
color: var(--wikt-palette-black, #202122);
}
body.skin-vector-legacy .vectortab {
background-color: #EDF6FB;
border-bottom: 1px solid #A7D7F9;
border-right: 1px solid #A7D7F9;
border-left: 1px solid #A7D7F9;
border-top: none;
color: #0645AD;
}
body.skin-vector-2022 .vectortab {
padding-left: 0em;
padding-right: 0em;
margin-left: 0.2em;
margin-right: 0.2em;
border: unset;
background-color: unset;
border-color: unset;
border-bottom: 2px solid;
font-size: 14px;
padding-bottom: 0.25em;
}
hv2a9rld85p7hif1xwco8tkq928wjvp
89030
89029
2026-08-07T01:04:20Z
Altronic
4137
Chaidh 1 mhùthadh ion-phortachadh o [[:en:Template:VectorTab/style.css]]
89029
sanitized-css
text/css
.vectortab {
padding-left: 0.4em;
padding-right: 0.4em;
background-color: var(--wikt-palette-lightblue, #d9ebff);
border: var(--wikt-palette-deepblue, #2f445c) 1px solid;
color: var(--wikt-palette-black, #202122);
}
body.skin-vector-legacy .vectortab {
background-color: #EDF6FB;
border-bottom: 1px solid #A7D7F9;
border-right: 1px solid #A7D7F9;
border-left: 1px solid #A7D7F9;
border-top: none;
color: #0645AD;
}
body.skin-vector-2022 .vectortab {
padding-left: 0em;
padding-right: 0em;
margin-left: 0.2em;
margin-right: 0.2em;
border: unset;
background-color: unset;
border-color: unset;
border-bottom: 2px solid;
font-size: 14px;
padding-bottom: 0.25em;
}
hv2a9rld85p7hif1xwco8tkq928wjvp