Deadfire_

Module:Navboxes Export

Feb 15th, 2022 (edited)
66
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
XML 235.79 KB | None | 0 0
  1. <mediawiki xmlns="http://www.mediawiki.org/xml/export-0.10/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.10/ http://www.mediawiki.org/xml/export-0.10.xsd" version="0.10" xml:lang="en">
  2.   <siteinfo>
  3.     <sitename>Wikipedia</sitename>
  4.     <dbname>enwiki</dbname>
  5.     <base>https://en.wikipedia.org/wiki/Main_Page</base>
  6.     <generator>MediaWiki 1.38.0-wmf.21</generator>
  7.     <case>first-letter</case>
  8.     <namespaces>
  9.       <namespace key="-2" case="first-letter">Media</namespace>
  10.       <namespace key="-1" case="first-letter">Special</namespace>
  11.       <namespace key="0" case="first-letter" />
  12.       <namespace key="1" case="first-letter">Talk</namespace>
  13.       <namespace key="2" case="first-letter">User</namespace>
  14.       <namespace key="3" case="first-letter">User talk</namespace>
  15.       <namespace key="4" case="first-letter">Wikipedia</namespace>
  16.       <namespace key="5" case="first-letter">Wikipedia talk</namespace>
  17.       <namespace key="6" case="first-letter">File</namespace>
  18.       <namespace key="7" case="first-letter">File talk</namespace>
  19.       <namespace key="8" case="first-letter">MediaWiki</namespace>
  20.       <namespace key="9" case="first-letter">MediaWiki talk</namespace>
  21.       <namespace key="10" case="first-letter">Template</namespace>
  22.       <namespace key="11" case="first-letter">Template talk</namespace>
  23.       <namespace key="12" case="first-letter">Help</namespace>
  24.       <namespace key="13" case="first-letter">Help talk</namespace>
  25.       <namespace key="14" case="first-letter">Category</namespace>
  26.       <namespace key="15" case="first-letter">Category talk</namespace>
  27.       <namespace key="100" case="first-letter">Portal</namespace>
  28.       <namespace key="101" case="first-letter">Portal talk</namespace>
  29.       <namespace key="118" case="first-letter">Draft</namespace>
  30.       <namespace key="119" case="first-letter">Draft talk</namespace>
  31.       <namespace key="710" case="first-letter">TimedText</namespace>
  32.       <namespace key="711" case="first-letter">TimedText talk</namespace>
  33.       <namespace key="828" case="first-letter">Module</namespace>
  34.       <namespace key="829" case="first-letter">Module talk</namespace>
  35.       <namespace key="2300" case="first-letter">Gadget</namespace>
  36.       <namespace key="2301" case="first-letter">Gadget talk</namespace>
  37.       <namespace key="2302" case="case-sensitive">Gadget definition</namespace>
  38.       <namespace key="2303" case="case-sensitive">Gadget definition talk</namespace>
  39.     </namespaces>
  40.   </siteinfo>
  41.   <page>
  42.     <title>Module:Navboxes</title>
  43.     <ns>828</ns>
  44.     <id>49336059</id>
  45.     <revision>
  46.       <id>880025066</id>
  47.       <parentid>863560331</parentid>
  48.       <timestamp>2019-01-24T21:20:05Z</timestamp>
  49.       <contributor>
  50.         <username>MusikAnimal</username>
  51.         <id>14882394</id>
  52.       </contributor>
  53.       <minor/>
  54.       <comment>Protected "[[Module:Navboxes]]": [[Wikipedia:High-risk templates|High-risk module]] ([Edit=Require template editor access] (indefinite))</comment>
  55.       <model>Scribunto</model>
  56.       <format>text/plain</format>
  57.       <text bytes="1513" xml:space="preserve">-- This implements Template:navboxes
  58. local p = {}
  59.  
  60. local Navbox = require('Module:Navbox')
  61.  
  62. local function isnotempty(s)
  63.     return s and s:match( '^%s*(.-)%s*$' ) ~= ''
  64. end
  65.  
  66. local function navboxes(args, list)
  67.     local navbar = (args['state'] and args['state'] == 'off') and 'off' or 'plain'
  68.     local title = args['title'] or 'Links to related articles'
  69.     local titlestyle = 'background:' .. (args['bg'] or '#e8e8ff') .. ';'
  70.         .. (isnotempty(args['fg']) and ('color:' .. args['fg'] .. ';') or '')
  71.         .. (isnotempty(args['bordercolor']) and ('border: 1px solid ' .. args['bordercolor'] .. ';') or '')
  72.         .. (args['titlestyle'] or '')
  73.     return Navbox._navbox({
  74.             navbar = navbar, title = title,
  75.             list1 = list,
  76.             state = args['state'] or 'collapsed',
  77.             titlestyle = titlestyle,
  78.             liststyle = 'font-size:114%',
  79.             listpadding = '0px',
  80.             tracking = 'no'
  81.             })
  82. end
  83.  
  84. function p.top(frame)      
  85.     local args = frame:getParent().args
  86.     local parts = mw.text.split(navboxes(args, '&lt;ADD LIST HERE&gt;'), '&lt;ADD LIST HERE&gt;')
  87.     return parts[1]
  88. end
  89.  
  90. function p.bottom(frame)       
  91.     local args = {}
  92.     local parts = mw.text.split(navboxes(args, '&lt;ADD LIST HERE&gt;'), '&lt;ADD LIST HERE&gt;')
  93.     return parts[2]
  94. end
  95.  
  96. function p.navbox(frame)
  97.     local args = frame:getParent().args
  98.     local list = args['list1'] or args['list'] or ''   
  99.     local track_cats = ''
  100.     if list == '' then
  101.         if mw.title.getCurrentTitle().namespace == 0 then
  102.             track_cats = '[[Category:Navboxes template with no content]]'
  103.         end
  104.     end
  105.     return navboxes(args, list) .. track_cats
  106. end
  107.  
  108. return p</text>
  109.       <sha1>9bbm2wredpidaz4k9qxoggz4ma1wrtb</sha1>
  110.     </revision>
  111.   </page>
  112.   <page>
  113.     <title>Template:High-use</title>
  114.     <ns>10</ns>
  115.     <id>17406187</id>
  116.     <revision>
  117.       <id>1051188483</id>
  118.       <parentid>1016519090</parentid>
  119.       <timestamp>2021-10-22T01:54:41Z</timestamp>
  120.       <contributor>
  121.         <username>MusikBot II</username>
  122.         <id>29539620</id>
  123.       </contributor>
  124.       <minor/>
  125.       <comment>Changed protection settings for "[[Template:High-use]]": [[Wikipedia:High-risk templates|High-risk template or module]]: 3343 transclusions ([[User:MusikBot II/TemplateProtector|more info]]) ([Edit=Require extended confirmed access] (indefinite) [Move=Require extended confirmed access] (indefinite))</comment>
  126.       <model>wikitext</model>
  127.       <format>text/x-wiki</format>
  128.       <text bytes="297" xml:space="preserve">{{#invoke:High-use|main|1={{{1|}}}|2={{{2|}}}|all-pages={{{all-pages|}}}|info={{{info|}}}|demo={{{demo|}}}|form={{{form|}}}|expiry={{{expiry|}}}|system={{{system|}}}}}&lt;noinclude&gt;
  129. {{Documentation}}
  130. &lt;!-- Add categories to the /doc subpage; interwiki links go to Wikidata, thank you! --&gt;
  131. &lt;/noinclude&gt;</text>
  132.       <sha1>pqp5tan0gkp2stociv4f3zgylzoarl5</sha1>
  133.     </revision>
  134.   </page>
  135.   <page>
  136.     <title>Template:Template link</title>
  137.     <ns>10</ns>
  138.     <id>1487430</id>
  139.     <revision>
  140.       <id>1014201710</id>
  141.       <parentid>1012903429</parentid>
  142.       <timestamp>2021-03-25T19:03:22Z</timestamp>
  143.       <contributor>
  144.         <username>Izno</username>
  145.         <id>2927383</id>
  146.       </contributor>
  147.       <comment>[[Wikipedia:Templates for discussion/Log/2021 March 18#Template:Tlu]] closed as keep ([[WP:XFDC#4.0.11|XFDcloser]])</comment>
  148.       <model>wikitext</model>
  149.       <format>text/x-wiki</format>
  150.       <text bytes="167" xml:space="preserve">&amp;#123;&amp;#123;[[Template:{{{1}}}|{{{1}}}]]&amp;#125;&amp;#125;&lt;noinclude&gt;{{documentation}}
  151. &lt;!-- Categories go on the /doc subpage and interwikis go on Wikidata. --&gt;
  152. &lt;/noinclude&gt;</text>
  153.       <sha1>rf3qx5cilaxlnjqq9fivevkah92ncil</sha1>
  154.     </revision>
  155.   </page>
  156.   <page>
  157.     <title>Template:Tl</title>
  158.     <ns>10</ns>
  159.     <id>66733012</id>
  160.     <redirect title="Template:Template link" />
  161.     <revision>
  162.       <id>1006436965</id>
  163.       <timestamp>2021-02-12T22:03:00Z</timestamp>
  164.       <contributor>
  165.         <username>Anthony Appleyard</username>
  166.         <id>119438</id>
  167.       </contributor>
  168.       <comment>Anthony Appleyard moved page [[Template:Tl]] to [[Template:Template link]]: [[Special:Permalink/1006428669|Requested]] by Buidhe at [[WP:RM/TR]]: RM closed as move</comment>
  169.       <model>wikitext</model>
  170.       <format>text/x-wiki</format>
  171.       <text bytes="83" xml:space="preserve">#REDIRECT [[Template:Template link]]
  172.  
  173. {{Redirect category shell|
  174. {{R from move}}
  175. }}</text>
  176.       <sha1>p1dn3i2ckb7gtckkrbn0xh6ckx8nei1</sha1>
  177.     </revision>
  178.   </page>
  179.   <page>
  180.     <title>Module:Arguments</title>
  181.     <ns>828</ns>
  182.     <id>41298065</id>
  183.     <revision>
  184.       <id>948472485</id>
  185.       <parentid>948472482</parentid>
  186.       <timestamp>2020-04-01T06:12:40Z</timestamp>
  187.       <contributor>
  188.         <username>MusikAnimal</username>
  189.         <id>14882394</id>
  190.       </contributor>
  191.       <minor/>
  192.       <comment>1 revision imported</comment>
  193.       <model>Scribunto</model>
  194.       <format>text/plain</format>
  195.       <text bytes="10054" xml:space="preserve">-- This module provides easy processing of arguments passed to Scribunto from
  196. -- #invoke. It is intended for use by other Lua modules, and should not be
  197. -- called from #invoke directly.
  198.  
  199. local libraryUtil = require('libraryUtil')
  200. local checkType = libraryUtil.checkType
  201.  
  202. local arguments = {}
  203.  
  204. -- Generate four different tidyVal functions, so that we don't have to check the
  205. -- options every time we call it.
  206.  
  207. local function tidyValDefault(key, val)
  208.     if type(val) == 'string' then
  209.         val = val:match('^%s*(.-)%s*$')
  210.         if val == '' then
  211.             return nil
  212.         else
  213.             return val
  214.         end
  215.     else
  216.         return val
  217.     end
  218. end
  219.  
  220. local function tidyValTrimOnly(key, val)
  221.     if type(val) == 'string' then
  222.         return val:match('^%s*(.-)%s*$')
  223.     else
  224.         return val
  225.     end
  226. end
  227.  
  228. local function tidyValRemoveBlanksOnly(key, val)
  229.     if type(val) == 'string' then
  230.         if val:find('%S') then
  231.             return val
  232.         else
  233.             return nil
  234.         end
  235.     else
  236.         return val
  237.     end
  238. end
  239.  
  240. local function tidyValNoChange(key, val)
  241.     return val
  242. end
  243.  
  244. local function matchesTitle(given, title)
  245.     local tp = type( given )
  246.     return (tp == 'string' or tp == 'number') and mw.title.new( given ).prefixedText == title
  247. end
  248.  
  249. local translate_mt = { __index = function(t, k) return k end }
  250.  
  251. function arguments.getArgs(frame, options)
  252.     checkType('getArgs', 1, frame, 'table', true)
  253.     checkType('getArgs', 2, options, 'table', true)
  254.     frame = frame or {}
  255.     options = options or {}
  256.  
  257.     --[[
  258.     -- Set up argument translation.
  259.     --]]
  260.     options.translate = options.translate or {}
  261.     if getmetatable(options.translate) == nil then
  262.         setmetatable(options.translate, translate_mt)
  263.     end
  264.     if options.backtranslate == nil then
  265.         options.backtranslate = {}
  266.         for k,v in pairs(options.translate) do
  267.             options.backtranslate[v] = k
  268.         end
  269.     end
  270.     if options.backtranslate and getmetatable(options.backtranslate) == nil then
  271.         setmetatable(options.backtranslate, {
  272.             __index = function(t, k)
  273.                 if options.translate[k] ~= k then
  274.                     return nil
  275.                 else
  276.                     return k
  277.                 end
  278.             end
  279.         })
  280.     end
  281.  
  282.     --[[
  283.     -- Get the argument tables. If we were passed a valid frame object, get the
  284.     -- frame arguments (fargs) and the parent frame arguments (pargs), depending
  285.     -- on the options set and on the parent frame's availability. If we weren't
  286.     -- passed a valid frame object, we are being called from another Lua module
  287.     -- or from the debug console, so assume that we were passed a table of args
  288.     -- directly, and assign it to a new variable (luaArgs).
  289.     --]]
  290.     local fargs, pargs, luaArgs
  291.     if type(frame.args) == 'table' and type(frame.getParent) == 'function' then
  292.         if options.wrappers then
  293.             --[[
  294.             -- The wrappers option makes Module:Arguments look up arguments in
  295.             -- either the frame argument table or the parent argument table, but
  296.             -- not both. This means that users can use either the #invoke syntax
  297.             -- or a wrapper template without the loss of performance associated
  298.             -- with looking arguments up in both the frame and the parent frame.
  299.             -- Module:Arguments will look up arguments in the parent frame
  300.             -- if it finds the parent frame's title in options.wrapper;
  301.             -- otherwise it will look up arguments in the frame object passed
  302.             -- to getArgs.
  303.             --]]
  304.             local parent = frame:getParent()
  305.             if not parent then
  306.                 fargs = frame.args
  307.             else
  308.                 local title = parent:getTitle():gsub('/sandbox$', '')
  309.                 local found = false
  310.                 if matchesTitle(options.wrappers, title) then
  311.                     found = true
  312.                 elseif type(options.wrappers) == 'table' then
  313.                     for _,v in pairs(options.wrappers) do
  314.                         if matchesTitle(v, title) then
  315.                             found = true
  316.                             break
  317.                         end
  318.                     end
  319.                 end
  320.  
  321.                 -- We test for false specifically here so that nil (the default) acts like true.
  322.                 if found or options.frameOnly == false then
  323.                     pargs = parent.args
  324.                 end
  325.                 if not found or options.parentOnly == false then
  326.                     fargs = frame.args
  327.                 end
  328.             end
  329.         else
  330.             -- options.wrapper isn't set, so check the other options.
  331.             if not options.parentOnly then
  332.                 fargs = frame.args
  333.             end
  334.             if not options.frameOnly then
  335.                 local parent = frame:getParent()
  336.                 pargs = parent and parent.args or nil
  337.             end
  338.         end
  339.         if options.parentFirst then
  340.             fargs, pargs = pargs, fargs
  341.         end
  342.     else
  343.         luaArgs = frame
  344.     end
  345.  
  346.     -- Set the order of precedence of the argument tables. If the variables are
  347.     -- nil, nothing will be added to the table, which is how we avoid clashes
  348.     -- between the frame/parent args and the Lua args.
  349.     local argTables = {fargs}
  350.     argTables[#argTables + 1] = pargs
  351.     argTables[#argTables + 1] = luaArgs
  352.  
  353.     --[[
  354.     -- Generate the tidyVal function. If it has been specified by the user, we
  355.     -- use that; if not, we choose one of four functions depending on the
  356.     -- options chosen. This is so that we don't have to call the options table
  357.     -- every time the function is called.
  358.     --]]
  359.     local tidyVal = options.valueFunc
  360.     if tidyVal then
  361.         if type(tidyVal) ~= 'function' then
  362.             error(
  363.                 "bad value assigned to option 'valueFunc'"
  364.                     .. '(function expected, got '
  365.                     .. type(tidyVal)
  366.                     .. ')',
  367.                 2
  368.             )
  369.         end
  370.     elseif options.trim ~= false then
  371.         if options.removeBlanks ~= false then
  372.             tidyVal = tidyValDefault
  373.         else
  374.             tidyVal = tidyValTrimOnly
  375.         end
  376.     else
  377.         if options.removeBlanks ~= false then
  378.             tidyVal = tidyValRemoveBlanksOnly
  379.         else
  380.             tidyVal = tidyValNoChange
  381.         end
  382.     end
  383.  
  384.     --[[
  385.     -- Set up the args, metaArgs and nilArgs tables. args will be the one
  386.     -- accessed from functions, and metaArgs will hold the actual arguments. Nil
  387.     -- arguments are memoized in nilArgs, and the metatable connects all of them
  388.     -- together.
  389.     --]]
  390.     local args, metaArgs, nilArgs, metatable = {}, {}, {}, {}
  391.     setmetatable(args, metatable)
  392.  
  393.     local function mergeArgs(tables)
  394.         --[[
  395.         -- Accepts multiple tables as input and merges their keys and values
  396.         -- into one table. If a value is already present it is not overwritten;
  397.         -- tables listed earlier have precedence. We are also memoizing nil
  398.         -- values, which can be overwritten if they are 's' (soft).
  399.         --]]
  400.         for _, t in ipairs(tables) do
  401.             for key, val in pairs(t) do
  402.                 if metaArgs[key] == nil and nilArgs[key] ~= 'h' then
  403.                     local tidiedVal = tidyVal(key, val)
  404.                     if tidiedVal == nil then
  405.                         nilArgs[key] = 's'
  406.                     else
  407.                         metaArgs[key] = tidiedVal
  408.                     end
  409.                 end
  410.             end
  411.         end
  412.     end
  413.  
  414.     --[[
  415.     -- Define metatable behaviour. Arguments are memoized in the metaArgs table,
  416.     -- and are only fetched from the argument tables once. Fetching arguments
  417.     -- from the argument tables is the most resource-intensive step in this
  418.     -- module, so we try and avoid it where possible. For this reason, nil
  419.     -- arguments are also memoized, in the nilArgs table. Also, we keep a record
  420.     -- in the metatable of when pairs and ipairs have been called, so we do not
  421.     -- run pairs and ipairs on the argument tables more than once. We also do
  422.     -- not run ipairs on fargs and pargs if pairs has already been run, as all
  423.     -- the arguments will already have been copied over.
  424.     --]]
  425.  
  426.     metatable.__index = function (t, key)
  427.         --[[
  428.         -- Fetches an argument when the args table is indexed. First we check
  429.         -- to see if the value is memoized, and if not we try and fetch it from
  430.         -- the argument tables. When we check memoization, we need to check
  431.         -- metaArgs before nilArgs, as both can be non-nil at the same time.
  432.         -- If the argument is not present in metaArgs, we also check whether
  433.         -- pairs has been run yet. If pairs has already been run, we return nil.
  434.         -- This is because all the arguments will have already been copied into
  435.         -- metaArgs by the mergeArgs function, meaning that any other arguments
  436.         -- must be nil.
  437.         --]]
  438.         if type(key) == 'string' then
  439.             key = options.translate[key]
  440.         end
  441.         local val = metaArgs[key]
  442.         if val ~= nil then
  443.             return val
  444.         elseif metatable.donePairs or nilArgs[key] then
  445.             return nil
  446.         end
  447.         for _, argTable in ipairs(argTables) do
  448.             local argTableVal = tidyVal(key, argTable[key])
  449.             if argTableVal ~= nil then
  450.                 metaArgs[key] = argTableVal
  451.                 return argTableVal
  452.             end
  453.         end
  454.         nilArgs[key] = 'h'
  455.         return nil
  456.     end
  457.  
  458.     metatable.__newindex = function (t, key, val)
  459.         -- This function is called when a module tries to add a new value to the
  460.         -- args table, or tries to change an existing value.
  461.         if type(key) == 'string' then
  462.             key = options.translate[key]
  463.         end
  464.         if options.readOnly then
  465.             error(
  466.                 'could not write to argument table key "'
  467.                     .. tostring(key)
  468.                     .. '"; the table is read-only',
  469.                 2
  470.             )
  471.         elseif options.noOverwrite and args[key] ~= nil then
  472.             error(
  473.                 'could not write to argument table key "'
  474.                     .. tostring(key)
  475.                     .. '"; overwriting existing arguments is not permitted',
  476.                 2
  477.             )
  478.         elseif val == nil then
  479.             --[[
  480.             -- If the argument is to be overwritten with nil, we need to erase
  481.             -- the value in metaArgs, so that __index, __pairs and __ipairs do
  482.             -- not use a previous existing value, if present; and we also need
  483.             -- to memoize the nil in nilArgs, so that the value isn't looked
  484.             -- up in the argument tables if it is accessed again.
  485.             --]]
  486.             metaArgs[key] = nil
  487.             nilArgs[key] = 'h'
  488.         else
  489.             metaArgs[key] = val
  490.         end
  491.     end
  492.  
  493.     local function translatenext(invariant)
  494.         local k, v = next(invariant.t, invariant.k)
  495.         invariant.k = k
  496.         if k == nil then
  497.             return nil
  498.         elseif type(k) ~= 'string' or not options.backtranslate then
  499.             return k, v
  500.         else
  501.             local backtranslate = options.backtranslate[k]
  502.             if backtranslate == nil then
  503.                 -- Skip this one. This is a tail call, so this won't cause stack overflow
  504.                 return translatenext(invariant)
  505.             else
  506.                 return backtranslate, v
  507.             end
  508.         end
  509.     end
  510.  
  511.     metatable.__pairs = function ()
  512.         -- Called when pairs is run on the args table.
  513.         if not metatable.donePairs then
  514.             mergeArgs(argTables)
  515.             metatable.donePairs = true
  516.         end
  517.         return translatenext, { t = metaArgs }
  518.     end
  519.  
  520.     local function inext(t, i)
  521.         -- This uses our __index metamethod
  522.         local v = t[i + 1]
  523.         if v ~= nil then
  524.             return i + 1, v
  525.         end
  526.     end
  527.  
  528.     metatable.__ipairs = function (t)
  529.         -- Called when ipairs is run on the args table.
  530.         return inext, t, 0
  531.     end
  532.  
  533.     return args
  534. end
  535.  
  536. return arguments</text>
  537.       <sha1>5qx9tzlul9ser30uxj9nbasjt92cevn</sha1>
  538.     </revision>
  539.   </page>
  540.   <page>
  541.     <title>Module:Documentation</title>
  542.     <ns>828</ns>
  543.     <id>40256557</id>
  544.     <revision>
  545.       <id>1065934020</id>
  546.       <parentid>1061447122</parentid>
  547.       <timestamp>2022-01-16T01:32:10Z</timestamp>
  548.       <contributor>
  549.         <username>Matt Fitzpatrick</username>
  550.         <id>291848</id>
  551.       </contributor>
  552.       <comment>sync from sandbox, add aria role and label to container</comment>
  553.       <model>Scribunto</model>
  554.       <format>text/plain</format>
  555.       <text bytes="35027" xml:space="preserve">-- This module implements {{documentation}}.
  556.  
  557. -- Get required modules.
  558. local getArgs = require('Module:Arguments').getArgs
  559.  
  560. -- Get the config table.
  561. local cfg = mw.loadData('Module:Documentation/config')
  562.  
  563. local p = {}
  564.  
  565. -- Often-used functions.
  566. local ugsub = mw.ustring.gsub
  567.  
  568. ----------------------------------------------------------------------------
  569. -- Helper functions
  570. --
  571. -- These are defined as local functions, but are made available in the p
  572. -- table for testing purposes.
  573. ----------------------------------------------------------------------------
  574.  
  575. local function message(cfgKey, valArray, expectType)
  576.     --[[
  577.     -- Gets a message from the cfg table and formats it if appropriate.
  578.     -- The function raises an error if the value from the cfg table is not
  579.     -- of the type expectType. The default type for expectType is 'string'.
  580.     -- If the table valArray is present, strings such as $1, $2 etc. in the
  581.     -- message are substituted with values from the table keys [1], [2] etc.
  582.     -- For example, if the message "foo-message" had the value 'Foo $2 bar $1.',
  583.     -- message('foo-message', {'baz', 'qux'}) would return "Foo qux bar baz."
  584.     --]]
  585.     local msg = cfg[cfgKey]
  586.     expectType = expectType or 'string'
  587.     if type(msg) ~= expectType then
  588.         error('message: type error in message cfg.' .. cfgKey .. ' (' .. expectType .. ' expected, got ' .. type(msg) .. ')', 2)
  589.     end
  590.     if not valArray then
  591.         return msg
  592.     end
  593.  
  594.     local function getMessageVal(match)
  595.         match = tonumber(match)
  596.         return valArray[match] or error('message: no value found for key $' .. match .. ' in message cfg.' .. cfgKey, 4)
  597.     end
  598.  
  599.     return ugsub(msg, '$([1-9][0-9]*)', getMessageVal)
  600. end
  601.  
  602. p.message = message
  603.  
  604. local function makeWikilink(page, display)
  605.     if display then
  606.         return mw.ustring.format('[[%s|%s]]', page, display)
  607.     else
  608.         return mw.ustring.format('[[%s]]', page)
  609.     end
  610. end
  611.  
  612. p.makeWikilink = makeWikilink
  613.  
  614. local function makeCategoryLink(cat, sort)
  615.     local catns = mw.site.namespaces[14].name
  616.     return makeWikilink(catns .. ':' .. cat, sort)
  617. end
  618.  
  619. p.makeCategoryLink = makeCategoryLink
  620.  
  621. local function makeUrlLink(url, display)
  622.     return mw.ustring.format('[%s %s]', url, display)
  623. end
  624.  
  625. p.makeUrlLink = makeUrlLink
  626.  
  627. local function makeToolbar(...)
  628.     local ret = {}
  629.     local lim = select('#', ...)
  630.     if lim &lt; 1 then
  631.         return nil
  632.     end
  633.     for i = 1, lim do
  634.         ret[#ret + 1] = select(i, ...)
  635.     end
  636.     -- 'documentation-toolbar'
  637.     return '&lt;span class="' .. message('toolbar-class') .. '"&gt;('
  638.         .. table.concat(ret, ' &amp;#124; ') .. ')&lt;/span&gt;'
  639. end
  640.  
  641. p.makeToolbar = makeToolbar
  642.  
  643. ----------------------------------------------------------------------------
  644. -- Argument processing
  645. ----------------------------------------------------------------------------
  646.  
  647. local function makeInvokeFunc(funcName)
  648.     return function (frame)
  649.         local args = getArgs(frame, {
  650.             valueFunc = function (key, value)
  651.                 if type(value) == 'string' then
  652.                     value = value:match('^%s*(.-)%s*$') -- Remove whitespace.
  653.                     if key == 'heading' or value ~= '' then
  654.                         return value
  655.                     else
  656.                         return nil
  657.                     end
  658.                 else
  659.                     return value
  660.                 end
  661.             end
  662.         })
  663.         return p[funcName](args)
  664.     end
  665. end
  666.  
  667. ----------------------------------------------------------------------------
  668. -- Entry points
  669. ----------------------------------------------------------------------------
  670.  
  671. function p.nonexistent(frame)
  672.     if mw.title.getCurrentTitle().subpageText == 'testcases' then
  673.         return frame:expandTemplate{title = 'module test cases notice'}
  674.     else
  675.         return p.main(frame)
  676.     end
  677. end
  678.  
  679. p.main = makeInvokeFunc('_main')
  680.  
  681. function p._main(args)
  682.     --[[
  683.     -- This function defines logic flow for the module.
  684.     -- @args - table of arguments passed by the user
  685.     --]]
  686.     local env = p.getEnvironment(args)
  687.     local root = mw.html.create()
  688.     root
  689.         :wikitext(p._getModuleWikitext(args, env))
  690.         :wikitext(p.protectionTemplate(env))
  691.         :wikitext(p.sandboxNotice(args, env))
  692.         :tag('div')
  693.             -- 'documentation-container'
  694.             :addClass(message('container'))
  695.             :attr('role', 'complementary')
  696.             :attr('aria-labelledby', args.heading ~= '' and 'documentation-heading' or nil)
  697.             :attr('aria-label', args.heading == '' and 'Documentation' or nil)
  698.             :newline()
  699.             :tag('div')
  700.                 -- 'documentation'
  701.                 :addClass(message('main-div-classes'))
  702.                 :newline()
  703.                 :wikitext(p._startBox(args, env))
  704.                 :wikitext(p._content(args, env))
  705.                 :tag('div')
  706.                     -- 'documentation-clear'
  707.                     :addClass(message('clear'))
  708.                     :done()
  709.                 :newline()
  710.                 :done()
  711.             :wikitext(p._endBox(args, env))
  712.             :done()
  713.         :wikitext(p.addTrackingCategories(env))
  714.     -- 'Module:Documentation/styles.css'
  715.     return mw.getCurrentFrame():extensionTag (
  716.         'templatestyles', '', {src=cfg['templatestyles']
  717.     }) .. tostring(root)
  718. end
  719.  
  720. ----------------------------------------------------------------------------
  721. -- Environment settings
  722. ----------------------------------------------------------------------------
  723.  
  724. function p.getEnvironment(args)
  725.     --[[
  726.     -- Returns a table with information about the environment, including title
  727.     -- objects and other namespace- or path-related data.
  728.     -- @args - table of arguments passed by the user
  729.     --
  730.     -- Title objects include:
  731.     -- env.title - the page we are making documentation for (usually the current title)
  732.     -- env.templateTitle - the template (or module, file, etc.)
  733.     -- env.docTitle - the /doc subpage.
  734.     -- env.sandboxTitle - the /sandbox subpage.
  735.     -- env.testcasesTitle - the /testcases subpage.
  736.     --
  737.     -- Data includes:
  738.     -- env.protectionLevels - the protection levels table of the title object.
  739.     -- env.subjectSpace - the number of the title's subject namespace.
  740.     -- env.docSpace - the number of the namespace the title puts its documentation in.
  741.     -- env.docpageBase - the text of the base page of the /doc, /sandbox and /testcases pages, with namespace.
  742.     -- env.compareUrl - URL of the Special:ComparePages page comparing the sandbox with the template.
  743.     --
  744.     -- All table lookups are passed through pcall so that errors are caught. If an error occurs, the value
  745.     -- returned will be nil.
  746.     --]]
  747.    
  748.     local env, envFuncs = {}, {}
  749.  
  750.     -- Set up the metatable. If triggered we call the corresponding function in the envFuncs table. The value
  751.     -- returned by that function is memoized in the env table so that we don't call any of the functions
  752.     -- more than once. (Nils won't be memoized.)
  753.     setmetatable(env, {
  754.         __index = function (t, key)
  755.             local envFunc = envFuncs[key]
  756.             if envFunc then
  757.                 local success, val = pcall(envFunc)
  758.                 if success then
  759.                     env[key] = val -- Memoise the value.
  760.                     return val
  761.                 end
  762.             end
  763.             return nil
  764.         end
  765.     }) 
  766.  
  767.     function envFuncs.title()
  768.         -- The title object for the current page, or a test page passed with args.page.
  769.         local title
  770.         local titleArg = args.page
  771.         if titleArg then
  772.             title = mw.title.new(titleArg)
  773.         else
  774.             title = mw.title.getCurrentTitle()
  775.         end
  776.         return title
  777.     end
  778.  
  779.     function envFuncs.templateTitle()
  780.         --[[
  781.         -- The template (or module, etc.) title object.
  782.         -- Messages:
  783.         -- 'sandbox-subpage' --&gt; 'sandbox'
  784.         -- 'testcases-subpage' --&gt; 'testcases'
  785.         --]]
  786.         local subjectSpace = env.subjectSpace
  787.         local title = env.title
  788.         local subpage = title.subpageText
  789.         if subpage == message('sandbox-subpage') or subpage == message('testcases-subpage') then
  790.             return mw.title.makeTitle(subjectSpace, title.baseText)
  791.         else
  792.             return mw.title.makeTitle(subjectSpace, title.text)
  793.         end
  794.     end
  795.  
  796.     function envFuncs.docTitle()
  797.         --[[
  798.         -- Title object of the /doc subpage.
  799.         -- Messages:
  800.         -- 'doc-subpage' --&gt; 'doc'
  801.         --]]
  802.         local title = env.title
  803.         local docname = args[1] -- User-specified doc page.
  804.         local docpage
  805.         if docname then
  806.             docpage = docname
  807.         else
  808.             docpage = env.docpageBase .. '/' .. message('doc-subpage')
  809.         end
  810.         return mw.title.new(docpage)
  811.     end
  812.    
  813.     function envFuncs.sandboxTitle()
  814.         --[[
  815.         -- Title object for the /sandbox subpage.
  816.         -- Messages:
  817.         -- 'sandbox-subpage' --&gt; 'sandbox'
  818.         --]]
  819.         return mw.title.new(env.docpageBase .. '/' .. message('sandbox-subpage'))
  820.     end
  821.    
  822.     function envFuncs.testcasesTitle()
  823.         --[[
  824.         -- Title object for the /testcases subpage.
  825.         -- Messages:
  826.         -- 'testcases-subpage' --&gt; 'testcases'
  827.         --]]
  828.         return mw.title.new(env.docpageBase .. '/' .. message('testcases-subpage'))
  829.     end
  830.  
  831.     function envFuncs.protectionLevels()
  832.         -- The protection levels table of the title object.
  833.         return env.title.protectionLevels
  834.     end
  835.  
  836.     function envFuncs.subjectSpace()
  837.         -- The subject namespace number.
  838.         return mw.site.namespaces[env.title.namespace].subject.id
  839.     end
  840.  
  841.     function envFuncs.docSpace()
  842.         -- The documentation namespace number. For most namespaces this is the
  843.         -- same as the subject namespace. However, pages in the Article, File,
  844.         -- MediaWiki or Category namespaces must have their /doc, /sandbox and
  845.         -- /testcases pages in talk space.
  846.         local subjectSpace = env.subjectSpace
  847.         if subjectSpace == 0 or subjectSpace == 6 or subjectSpace == 8 or subjectSpace == 14 then
  848.             return subjectSpace + 1
  849.         else
  850.             return subjectSpace
  851.         end
  852.     end
  853.  
  854.     function envFuncs.docpageBase()
  855.         -- The base page of the /doc, /sandbox, and /testcases subpages.
  856.         -- For some namespaces this is the talk page, rather than the template page.
  857.         local templateTitle = env.templateTitle
  858.         local docSpace = env.docSpace
  859.         local docSpaceText = mw.site.namespaces[docSpace].name
  860.         -- Assemble the link. docSpace is never the main namespace, so we can hardcode the colon.
  861.         return docSpaceText .. ':' .. templateTitle.text
  862.     end
  863.    
  864.     function envFuncs.compareUrl()
  865.         -- Diff link between the sandbox and the main template using [[Special:ComparePages]].
  866.         local templateTitle = env.templateTitle
  867.         local sandboxTitle = env.sandboxTitle
  868.         if templateTitle.exists and sandboxTitle.exists then
  869.             local compareUrl = mw.uri.fullUrl(
  870.                 'Special:ComparePages',
  871.                 { page1 = templateTitle.prefixedText, page2 = sandboxTitle.prefixedText}
  872.             )
  873.             return tostring(compareUrl)
  874.         else
  875.             return nil
  876.         end
  877.     end    
  878.  
  879.     return env
  880. end
  881.  
  882. ----------------------------------------------------------------------------
  883. -- Auxiliary templates
  884. ----------------------------------------------------------------------------
  885.  
  886. p.getModuleWikitext = makeInvokeFunc('_getModuleWikitext')
  887.  
  888. function p._getModuleWikitext(args, env)
  889.     local currentTitle = mw.title.getCurrentTitle()
  890.     if currentTitle.contentModel ~= 'Scribunto' then return end
  891.     pcall(require, currentTitle.prefixedText) -- if it fails, we don't care
  892.     local moduleWikitext =  package.loaded["Module:Module wikitext"]
  893.     if moduleWikitext then
  894.         return moduleWikitext.main()
  895.     end
  896. end
  897.  
  898. function p.sandboxNotice(args, env)
  899.     --[=[
  900.     -- Generates a sandbox notice for display above sandbox pages.
  901.     -- @args - a table of arguments passed by the user
  902.     -- @env - environment table containing title objects, etc., generated with p.getEnvironment
  903.     --
  904.     -- Messages:
  905.     -- 'sandbox-notice-image' --&gt; '[[Image:Sandbox.svg|50px|alt=|link=]]'
  906.     -- 'sandbox-notice-blurb' --&gt; 'This is the $1 for $2.'
  907.     -- 'sandbox-notice-diff-blurb' --&gt; 'This is the $1 for $2 ($3).'
  908.     -- 'sandbox-notice-pagetype-template' --&gt; '[[Wikipedia:Template test cases|template sandbox]] page'
  909.     -- 'sandbox-notice-pagetype-module' --&gt; '[[Wikipedia:Template test cases|module sandbox]] page'
  910.     -- 'sandbox-notice-pagetype-other' --&gt; 'sandbox page'
  911.     -- 'sandbox-notice-compare-link-display' --&gt; 'diff'
  912.     -- 'sandbox-notice-testcases-blurb' --&gt; 'See also the companion subpage for $1.'
  913.     -- 'sandbox-notice-testcases-link-display' --&gt; 'test cases'
  914.     -- 'sandbox-category' --&gt; 'Template sandboxes'
  915.     --]=]
  916.     local title = env.title
  917.     local sandboxTitle = env.sandboxTitle
  918.     local templateTitle = env.templateTitle
  919.     local subjectSpace = env.subjectSpace
  920.     if not (subjectSpace and title and sandboxTitle and templateTitle
  921.         and mw.title.equals(title, sandboxTitle)) then
  922.         return nil
  923.     end
  924.     -- Build the table of arguments to pass to {{ombox}}. We need just two fields, "image" and "text".
  925.     local omargs = {}
  926.     omargs.image = message('sandbox-notice-image')
  927.     -- Get the text. We start with the opening blurb, which is something like
  928.     -- "This is the template sandbox for [[Template:Foo]] (diff)."
  929.     local text = ''
  930.     local pagetype
  931.     if subjectSpace == 10 then
  932.         pagetype = message('sandbox-notice-pagetype-template')
  933.     elseif subjectSpace == 828 then
  934.         pagetype = message('sandbox-notice-pagetype-module')
  935.     else
  936.         pagetype = message('sandbox-notice-pagetype-other')
  937.     end
  938.     local templateLink = makeWikilink(templateTitle.prefixedText)
  939.     local compareUrl = env.compareUrl
  940.     if compareUrl then
  941.         local compareDisplay = message('sandbox-notice-compare-link-display')
  942.         local compareLink = makeUrlLink(compareUrl, compareDisplay)
  943.         text = text .. message('sandbox-notice-diff-blurb', {pagetype, templateLink, compareLink})
  944.     else
  945.         text = text .. message('sandbox-notice-blurb', {pagetype, templateLink})
  946.     end
  947.     -- Get the test cases page blurb if the page exists. This is something like
  948.     -- "See also the companion subpage for [[Template:Foo/testcases|test cases]]."
  949.     local testcasesTitle = env.testcasesTitle
  950.     if testcasesTitle and testcasesTitle.exists then
  951.         if testcasesTitle.contentModel == "Scribunto" then
  952.             local testcasesLinkDisplay = message('sandbox-notice-testcases-link-display')
  953.             local testcasesRunLinkDisplay = message('sandbox-notice-testcases-run-link-display')
  954.             local testcasesLink = makeWikilink(testcasesTitle.prefixedText, testcasesLinkDisplay)
  955.             local testcasesRunLink = makeWikilink(testcasesTitle.talkPageTitle.prefixedText, testcasesRunLinkDisplay)
  956.             text = text .. '&lt;br /&gt;' .. message('sandbox-notice-testcases-run-blurb', {testcasesLink, testcasesRunLink})
  957.         else
  958.             local testcasesLinkDisplay = message('sandbox-notice-testcases-link-display')
  959.             local testcasesLink = makeWikilink(testcasesTitle.prefixedText, testcasesLinkDisplay)
  960.             text = text .. '&lt;br /&gt;' .. message('sandbox-notice-testcases-blurb', {testcasesLink})
  961.         end
  962.     end
  963.     -- Add the sandbox to the sandbox category.
  964.     omargs.text = text .. makeCategoryLink(message('sandbox-category'))
  965.  
  966.     -- 'documentation-clear'
  967.     return '&lt;div class="' .. message('clear') .. '"&gt;&lt;/div&gt;'
  968.         .. require('Module:Message box').main('ombox', omargs)
  969. end
  970.  
  971. function p.protectionTemplate(env)
  972.     -- Generates the padlock icon in the top right.
  973.     -- @env - environment table containing title objects, etc., generated with p.getEnvironment
  974.     -- Messages:
  975.     -- 'protection-template' --&gt; 'pp-template'
  976.     -- 'protection-template-args' --&gt; {docusage = 'yes'}
  977.     local protectionLevels = env.protectionLevels
  978.     if not protectionLevels then
  979.         return nil
  980.     end
  981.     local editProt = protectionLevels.edit and protectionLevels.edit[1]
  982.     local moveProt = protectionLevels.move and protectionLevels.move[1]
  983.     if editProt then
  984.         -- The page is edit-protected.
  985.         return require('Module:Protection banner')._main{
  986.             message('protection-reason-edit'), small = true
  987.         }
  988.     elseif moveProt and moveProt ~= 'autoconfirmed' then
  989.         -- The page is move-protected but not edit-protected. Exclude move
  990.         -- protection with the level "autoconfirmed", as this is equivalent to
  991.         -- no move protection at all.
  992.         return require('Module:Protection banner')._main{
  993.             action = 'move', small = true
  994.         }
  995.     else
  996.         return nil
  997.     end
  998. end
  999.  
  1000. ----------------------------------------------------------------------------
  1001. -- Start box
  1002. ----------------------------------------------------------------------------
  1003.  
  1004. p.startBox = makeInvokeFunc('_startBox')
  1005.  
  1006. function p._startBox(args, env)
  1007.     --[[
  1008.     -- This function generates the start box.
  1009.     -- @args - a table of arguments passed by the user
  1010.     -- @env - environment table containing title objects, etc., generated with p.getEnvironment
  1011.     --
  1012.     -- The actual work is done by p.makeStartBoxLinksData and p.renderStartBoxLinks which make
  1013.     -- the [view] [edit] [history] [purge] links, and by p.makeStartBoxData and p.renderStartBox
  1014.     -- which generate the box HTML.
  1015.     --]]
  1016.     env = env or p.getEnvironment(args)
  1017.     local links
  1018.     local content = args.content
  1019.     if not content or args[1] then
  1020.         -- No need to include the links if the documentation is on the template page itself.
  1021.         local linksData = p.makeStartBoxLinksData(args, env)
  1022.         if linksData then
  1023.             links = p.renderStartBoxLinks(linksData)
  1024.         end
  1025.     end
  1026.     -- Generate the start box html.
  1027.     local data = p.makeStartBoxData(args, env, links)
  1028.     if data then
  1029.         return p.renderStartBox(data)
  1030.     else
  1031.         -- User specified no heading.
  1032.         return nil
  1033.     end
  1034. end
  1035.  
  1036. function p.makeStartBoxLinksData(args, env)
  1037.     --[[
  1038.     -- Does initial processing of data to make the [view] [edit] [history] [purge] links.
  1039.     -- @args - a table of arguments passed by the user
  1040.     -- @env - environment table containing title objects, etc., generated with p.getEnvironment
  1041.     --
  1042.     -- Messages:
  1043.     -- 'view-link-display' --&gt; 'view'
  1044.     -- 'edit-link-display' --&gt; 'edit'
  1045.     -- 'history-link-display' --&gt; 'history'
  1046.     -- 'purge-link-display' --&gt; 'purge'
  1047.     -- 'module-preload' --&gt; 'Template:Documentation/preload-module-doc'
  1048.     -- 'docpage-preload' --&gt; 'Template:Documentation/preload'
  1049.     -- 'create-link-display' --&gt; 'create'
  1050.     --]]
  1051.     local subjectSpace = env.subjectSpace
  1052.     local title = env.title
  1053.     local docTitle = env.docTitle
  1054.     if not title or not docTitle then
  1055.         return nil
  1056.     end
  1057.     if docTitle.isRedirect then
  1058.         docTitle = docTitle.redirectTarget
  1059.     end
  1060.  
  1061.     local data = {}
  1062.     data.title = title
  1063.     data.docTitle = docTitle
  1064.     -- View, display, edit, and purge links if /doc exists.
  1065.     data.viewLinkDisplay = message('view-link-display')
  1066.     data.editLinkDisplay = message('edit-link-display')
  1067.     data.historyLinkDisplay = message('history-link-display')
  1068.     data.purgeLinkDisplay = message('purge-link-display')
  1069.     -- Create link if /doc doesn't exist.
  1070.     local preload = args.preload
  1071.     if not preload then
  1072.         if subjectSpace == 828 then -- Module namespace
  1073.             preload = message('module-preload')
  1074.         else
  1075.             preload = message('docpage-preload')
  1076.         end
  1077.     end
  1078.     data.preload = preload
  1079.     data.createLinkDisplay = message('create-link-display')
  1080.     return data
  1081. end
  1082.  
  1083. function p.renderStartBoxLinks(data)
  1084.     --[[
  1085.     -- Generates the [view][edit][history][purge] or [create][purge] links from the data table.
  1086.     -- @data - a table of data generated by p.makeStartBoxLinksData
  1087.     --]]
  1088.    
  1089.     local function escapeBrackets(s)
  1090.         -- Escapes square brackets with HTML entities.
  1091.         s = s:gsub('%[', '&amp;#91;') -- Replace square brackets with HTML entities.
  1092.         s = s:gsub('%]', '&amp;#93;')
  1093.         return s
  1094.     end
  1095.  
  1096.     local ret
  1097.     local docTitle = data.docTitle
  1098.     local title = data.title
  1099.     local purgeLink = makeUrlLink(title:fullUrl{action = 'purge'}, data.purgeLinkDisplay)
  1100.     if docTitle.exists then
  1101.         local viewLink = makeWikilink(docTitle.prefixedText, data.viewLinkDisplay)
  1102.         local editLink = makeUrlLink(docTitle:fullUrl{action = 'edit'}, data.editLinkDisplay)
  1103.         local historyLink = makeUrlLink(docTitle:fullUrl{action = 'history'}, data.historyLinkDisplay)
  1104.         ret = '[%s] [%s] [%s] [%s]'
  1105.         ret = escapeBrackets(ret)
  1106.         ret = mw.ustring.format(ret, viewLink, editLink, historyLink, purgeLink)
  1107.     else
  1108.         local createLink = makeUrlLink(docTitle:fullUrl{action = 'edit', preload = data.preload}, data.createLinkDisplay)
  1109.         ret = '[%s] [%s]'
  1110.         ret = escapeBrackets(ret)
  1111.         ret = mw.ustring.format(ret, createLink, purgeLink)
  1112.     end
  1113.     return ret
  1114. end
  1115.  
  1116. function p.makeStartBoxData(args, env, links)
  1117.     --[=[
  1118.     -- Does initial processing of data to pass to the start-box render function, p.renderStartBox.
  1119.     -- @args - a table of arguments passed by the user
  1120.     -- @env - environment table containing title objects, etc., generated with p.getEnvironment
  1121.     -- @links - a string containing the [view][edit][history][purge] links - could be nil if there's an error.
  1122.     --
  1123.     -- Messages:
  1124.     -- 'documentation-icon-wikitext' --&gt; '[[File:Test Template Info-Icon - Version (2).svg|50px|link=|alt=]]'
  1125.     -- 'template-namespace-heading' --&gt; 'Template documentation'
  1126.     -- 'module-namespace-heading' --&gt; 'Module documentation'
  1127.     -- 'file-namespace-heading' --&gt; 'Summary'
  1128.     -- 'other-namespaces-heading' --&gt; 'Documentation'
  1129.     -- 'testcases-create-link-display' --&gt; 'create'
  1130.     --]=]
  1131.     local subjectSpace = env.subjectSpace
  1132.     if not subjectSpace then
  1133.         -- Default to an "other namespaces" namespace, so that we get at least some output
  1134.         -- if an error occurs.
  1135.         subjectSpace = 2
  1136.     end
  1137.     local data = {}
  1138.    
  1139.     -- Heading
  1140.     local heading = args.heading -- Blank values are not removed.
  1141.     if heading == '' then
  1142.         -- Don't display the start box if the heading arg is defined but blank.
  1143.         return nil
  1144.     end
  1145.     if heading then
  1146.         data.heading = heading
  1147.     elseif subjectSpace == 10 then -- Template namespace
  1148.         data.heading = message('documentation-icon-wikitext') .. ' ' .. message('template-namespace-heading')
  1149.     elseif subjectSpace == 828 then -- Module namespace
  1150.         data.heading = message('documentation-icon-wikitext') .. ' ' .. message('module-namespace-heading')
  1151.     elseif subjectSpace == 6 then -- File namespace
  1152.         data.heading = message('file-namespace-heading')
  1153.     else
  1154.         data.heading = message('other-namespaces-heading')
  1155.     end
  1156.    
  1157.     -- Heading CSS
  1158.     local headingStyle = args['heading-style']
  1159.     if headingStyle then
  1160.         data.headingStyleText = headingStyle
  1161.     else
  1162.         -- 'documentation-heading'
  1163.         data.headingClass = message('main-div-heading-class')
  1164.     end
  1165.    
  1166.     -- Data for the [view][edit][history][purge] or [create] links.
  1167.     if links then
  1168.         -- 'mw-editsection-like plainlinks'
  1169.         data.linksClass = message('start-box-link-classes')
  1170.         data.links = links
  1171.     end
  1172.    
  1173.     return data
  1174. end
  1175.  
  1176. function p.renderStartBox(data)
  1177.     -- Renders the start box html.
  1178.     -- @data - a table of data generated by p.makeStartBoxData.
  1179.     local sbox = mw.html.create('div')
  1180.     sbox
  1181.         -- 'documentation-startbox'
  1182.         :addClass(message('start-box-class'))
  1183.         :newline()
  1184.         :tag('span')
  1185.             :addClass(data.headingClass)
  1186.             :attr('id', 'documentation-heading')
  1187.             :cssText(data.headingStyleText)
  1188.             :wikitext(data.heading)
  1189.     local links = data.links
  1190.     if links then
  1191.         sbox:tag('span')
  1192.             :addClass(data.linksClass)
  1193.             :attr('id', data.linksId)
  1194.             :wikitext(links)
  1195.     end
  1196.     return tostring(sbox)
  1197. end
  1198.  
  1199. ----------------------------------------------------------------------------
  1200. -- Documentation content
  1201. ----------------------------------------------------------------------------
  1202.  
  1203. p.content = makeInvokeFunc('_content')
  1204.  
  1205. function p._content(args, env)
  1206.     -- Displays the documentation contents
  1207.     -- @args - a table of arguments passed by the user
  1208.     -- @env - environment table containing title objects, etc., generated with p.getEnvironment
  1209.     env = env or p.getEnvironment(args)
  1210.     local docTitle = env.docTitle
  1211.     local content = args.content
  1212.     if not content and docTitle and docTitle.exists then
  1213.         content = args._content or mw.getCurrentFrame():expandTemplate{title = docTitle.prefixedText}
  1214.     end
  1215.     -- The line breaks below are necessary so that "=== Headings ===" at the start and end
  1216.     -- of docs are interpreted correctly.
  1217.     return '\n' .. (content or '') .. '\n'
  1218. end
  1219.  
  1220. p.contentTitle = makeInvokeFunc('_contentTitle')
  1221.  
  1222. function p._contentTitle(args, env)
  1223.     env = env or p.getEnvironment(args)
  1224.     local docTitle = env.docTitle
  1225.     if not args.content and docTitle and docTitle.exists then
  1226.         return docTitle.prefixedText
  1227.     else
  1228.         return ''
  1229.     end
  1230. end
  1231.  
  1232. ----------------------------------------------------------------------------
  1233. -- End box
  1234. ----------------------------------------------------------------------------
  1235.  
  1236. p.endBox = makeInvokeFunc('_endBox')
  1237.  
  1238. function p._endBox(args, env)
  1239.     --[=[
  1240.     -- This function generates the end box (also known as the link box).
  1241.     -- @args - a table of arguments passed by the user
  1242.     -- @env - environment table containing title objects, etc., generated with p.getEnvironment
  1243.     --
  1244.     --]=]
  1245.    
  1246.     -- Get environment data.
  1247.     env = env or p.getEnvironment(args)
  1248.     local subjectSpace = env.subjectSpace
  1249.     local docTitle = env.docTitle
  1250.     if not subjectSpace or not docTitle then
  1251.         return nil
  1252.     end
  1253.        
  1254.     -- Check whether we should output the end box at all. Add the end
  1255.     -- box by default if the documentation exists or if we are in the
  1256.     -- user, module or template namespaces.
  1257.     local linkBox = args['link box']
  1258.     if linkBox == 'off'
  1259.         or not (
  1260.             docTitle.exists
  1261.             or subjectSpace == 2
  1262.             or subjectSpace == 828
  1263.             or subjectSpace == 10
  1264.         )
  1265.     then
  1266.         return nil
  1267.     end
  1268.  
  1269.     -- Assemble the link box.
  1270.     local text = ''
  1271.     if linkBox then
  1272.         text = text .. linkBox
  1273.     else
  1274.         text = text .. (p.makeDocPageBlurb(args, env) or '') -- "This documentation is transcluded from [[Foo]]."
  1275.         if subjectSpace == 2 or subjectSpace == 10 or subjectSpace == 828 then
  1276.             -- We are in the user, template or module namespaces.
  1277.             -- Add sandbox and testcases links.
  1278.             -- "Editors can experiment in this template's sandbox and testcases pages."
  1279.             text = text .. (p.makeExperimentBlurb(args, env) or '') .. '&lt;br /&gt;'
  1280.             if not args.content and not args[1] then
  1281.                 -- "Please add categories to the /doc subpage."
  1282.                 -- Don't show this message with inline docs or with an explicitly specified doc page,
  1283.                 -- as then it is unclear where to add the categories.
  1284.                 text = text .. (p.makeCategoriesBlurb(args, env) or '')
  1285.             end
  1286.             text = text .. ' ' .. (p.makeSubpagesBlurb(args, env) or '') --"Subpages of this template"
  1287.         end
  1288.     end
  1289.    
  1290.     local box = mw.html.create('div')
  1291.     -- 'documentation-metadata'
  1292.     box:attr('role', 'note')
  1293.         :addClass(message('end-box-class'))
  1294.         -- 'plainlinks'
  1295.         :addClass(message('end-box-plainlinks'))
  1296.         :wikitext(text)
  1297.         :done()
  1298.  
  1299.     return '\n' .. tostring(box)
  1300. end
  1301.  
  1302. function p.makeDocPageBlurb(args, env)
  1303.     --[=[
  1304.     -- Makes the blurb "This documentation is transcluded from [[Template:Foo]] (edit, history)".
  1305.     -- @args - a table of arguments passed by the user
  1306.     -- @env - environment table containing title objects, etc., generated with p.getEnvironment
  1307.     --
  1308.     -- Messages:
  1309.     -- 'edit-link-display' --&gt; 'edit'
  1310.     -- 'history-link-display' --&gt; 'history'
  1311.     -- 'transcluded-from-blurb' --&gt;
  1312.     -- 'The above [[Wikipedia:Template documentation|documentation]]
  1313.     -- is [[Help:Transclusion|transcluded]] from $1.'
  1314.     -- 'module-preload' --&gt; 'Template:Documentation/preload-module-doc'
  1315.     -- 'create-link-display' --&gt; 'create'
  1316.     -- 'create-module-doc-blurb' --&gt;
  1317.     -- 'You might want to $1 a documentation page for this [[Wikipedia:Lua|Scribunto module]].'
  1318.     --]=]
  1319.     local docTitle = env.docTitle
  1320.     if not docTitle then
  1321.         return nil
  1322.     end
  1323.     local ret
  1324.     if docTitle.exists then
  1325.         -- /doc exists; link to it.
  1326.         local docLink = makeWikilink(docTitle.prefixedText)
  1327.         local editUrl = docTitle:fullUrl{action = 'edit'}
  1328.         local editDisplay = message('edit-link-display')
  1329.         local editLink = makeUrlLink(editUrl, editDisplay)
  1330.         local historyUrl = docTitle:fullUrl{action = 'history'}
  1331.         local historyDisplay = message('history-link-display')
  1332.         local historyLink = makeUrlLink(historyUrl, historyDisplay)
  1333.         ret = message('transcluded-from-blurb', {docLink})
  1334.             .. ' '
  1335.             .. makeToolbar(editLink, historyLink)
  1336.             .. '&lt;br /&gt;'
  1337.     elseif env.subjectSpace == 828 then
  1338.         -- /doc does not exist; ask to create it.
  1339.         local createUrl = docTitle:fullUrl{action = 'edit', preload = message('module-preload')}
  1340.         local createDisplay = message('create-link-display')
  1341.         local createLink = makeUrlLink(createUrl, createDisplay)
  1342.         ret = message('create-module-doc-blurb', {createLink})
  1343.             .. '&lt;br /&gt;'
  1344.     end
  1345.     return ret
  1346. end
  1347.  
  1348. function p.makeExperimentBlurb(args, env)
  1349.     --[[
  1350.     -- Renders the text "Editors can experiment in this template's sandbox (edit | diff) and testcases (edit) pages."
  1351.     -- @args - a table of arguments passed by the user
  1352.     -- @env - environment table containing title objects, etc., generated with p.getEnvironment
  1353.     --
  1354.     -- Messages:
  1355.     -- 'sandbox-link-display' --&gt; 'sandbox'
  1356.     -- 'sandbox-edit-link-display' --&gt; 'edit'
  1357.     -- 'compare-link-display' --&gt; 'diff'
  1358.     -- 'module-sandbox-preload' --&gt; 'Template:Documentation/preload-module-sandbox'
  1359.     -- 'template-sandbox-preload' --&gt; 'Template:Documentation/preload-sandbox'
  1360.     -- 'sandbox-create-link-display' --&gt; 'create'
  1361.     -- 'mirror-edit-summary' --&gt; 'Create sandbox version of $1'
  1362.     -- 'mirror-link-display' --&gt; 'mirror'
  1363.     -- 'mirror-link-preload' --&gt; 'Template:Documentation/mirror'
  1364.     -- 'sandbox-link-display' --&gt; 'sandbox'
  1365.     -- 'testcases-link-display' --&gt; 'testcases'
  1366.     -- 'testcases-edit-link-display'--&gt; 'edit'
  1367.     -- 'template-sandbox-preload' --&gt; 'Template:Documentation/preload-sandbox'
  1368.     -- 'testcases-create-link-display' --&gt; 'create'
  1369.     -- 'testcases-link-display' --&gt; 'testcases'
  1370.     -- 'testcases-edit-link-display' --&gt; 'edit'
  1371.     -- 'module-testcases-preload' --&gt; 'Template:Documentation/preload-module-testcases'
  1372.     -- 'template-testcases-preload' --&gt; 'Template:Documentation/preload-testcases'
  1373.     -- 'experiment-blurb-module' --&gt; 'Editors can experiment in this module's $1 and $2 pages.'
  1374.     -- 'experiment-blurb-template' --&gt; 'Editors can experiment in this template's $1 and $2 pages.'
  1375.     --]]
  1376.     local subjectSpace = env.subjectSpace
  1377.     local templateTitle = env.templateTitle
  1378.     local sandboxTitle = env.sandboxTitle
  1379.     local testcasesTitle = env.testcasesTitle
  1380.     local templatePage = templateTitle.prefixedText
  1381.     if not subjectSpace or not templateTitle or not sandboxTitle or not testcasesTitle then
  1382.         return nil
  1383.     end
  1384.     -- Make links.
  1385.     local sandboxLinks, testcasesLinks
  1386.     if sandboxTitle.exists then
  1387.         local sandboxPage = sandboxTitle.prefixedText
  1388.         local sandboxDisplay = message('sandbox-link-display')
  1389.         local sandboxLink = makeWikilink(sandboxPage, sandboxDisplay)
  1390.         local sandboxEditUrl = sandboxTitle:fullUrl{action = 'edit'}
  1391.         local sandboxEditDisplay = message('sandbox-edit-link-display')
  1392.         local sandboxEditLink = makeUrlLink(sandboxEditUrl, sandboxEditDisplay)
  1393.         local compareUrl = env.compareUrl
  1394.         local compareLink
  1395.         if compareUrl then
  1396.             local compareDisplay = message('compare-link-display')
  1397.             compareLink = makeUrlLink(compareUrl, compareDisplay)
  1398.         end
  1399.         sandboxLinks = sandboxLink .. ' ' .. makeToolbar(sandboxEditLink, compareLink)
  1400.     else
  1401.         local sandboxPreload
  1402.         if subjectSpace == 828 then
  1403.             sandboxPreload = message('module-sandbox-preload')
  1404.         else
  1405.             sandboxPreload = message('template-sandbox-preload')
  1406.         end
  1407.         local sandboxCreateUrl = sandboxTitle:fullUrl{action = 'edit', preload = sandboxPreload}
  1408.         local sandboxCreateDisplay = message('sandbox-create-link-display')
  1409.         local sandboxCreateLink = makeUrlLink(sandboxCreateUrl, sandboxCreateDisplay)
  1410.         local mirrorSummary = message('mirror-edit-summary', {makeWikilink(templatePage)})
  1411.         local mirrorPreload = message('mirror-link-preload')
  1412.         local mirrorUrl = sandboxTitle:fullUrl{action = 'edit', preload = mirrorPreload, summary = mirrorSummary}
  1413.         if subjectSpace == 828 then
  1414.             mirrorUrl = sandboxTitle:fullUrl{action = 'edit', preload = templateTitle.prefixedText, summary = mirrorSummary}
  1415.         end
  1416.         local mirrorDisplay = message('mirror-link-display')
  1417.         local mirrorLink = makeUrlLink(mirrorUrl, mirrorDisplay)
  1418.         sandboxLinks = message('sandbox-link-display') .. ' ' .. makeToolbar(sandboxCreateLink, mirrorLink)
  1419.     end
  1420.     if testcasesTitle.exists then
  1421.         local testcasesPage = testcasesTitle.prefixedText
  1422.         local testcasesDisplay = message('testcases-link-display')
  1423.         local testcasesLink = makeWikilink(testcasesPage, testcasesDisplay)
  1424.         local testcasesEditUrl = testcasesTitle:fullUrl{action = 'edit'}
  1425.         local testcasesEditDisplay = message('testcases-edit-link-display')
  1426.         local testcasesEditLink = makeUrlLink(testcasesEditUrl, testcasesEditDisplay)
  1427.         -- for Modules, add testcases run link if exists
  1428.         if testcasesTitle.contentModel == "Scribunto"  and testcasesTitle.talkPageTitle and testcasesTitle.talkPageTitle.exists then
  1429.             local testcasesRunLinkDisplay = message('testcases-run-link-display')
  1430.             local testcasesRunLink = makeWikilink(testcasesTitle.talkPageTitle.prefixedText, testcasesRunLinkDisplay)
  1431.             testcasesLinks = testcasesLink .. ' ' .. makeToolbar(testcasesEditLink, testcasesRunLink)
  1432.         else
  1433.             testcasesLinks = testcasesLink .. ' ' .. makeToolbar(testcasesEditLink)
  1434.         end
  1435.     else
  1436.         local testcasesPreload
  1437.         if subjectSpace == 828 then
  1438.             testcasesPreload = message('module-testcases-preload')
  1439.         else
  1440.             testcasesPreload = message('template-testcases-preload')
  1441.         end
  1442.         local testcasesCreateUrl = testcasesTitle:fullUrl{action = 'edit', preload = testcasesPreload}
  1443.         local testcasesCreateDisplay = message('testcases-create-link-display')
  1444.         local testcasesCreateLink = makeUrlLink(testcasesCreateUrl, testcasesCreateDisplay)
  1445.         testcasesLinks = message('testcases-link-display') .. ' ' .. makeToolbar(testcasesCreateLink)
  1446.     end
  1447.     local messageName
  1448.     if subjectSpace == 828 then
  1449.         messageName = 'experiment-blurb-module'
  1450.     else
  1451.         messageName = 'experiment-blurb-template'
  1452.     end
  1453.     return message(messageName, {sandboxLinks, testcasesLinks})
  1454. end
  1455.  
  1456. function p.makeCategoriesBlurb(args, env)
  1457.     --[[
  1458.     -- Generates the text "Please add categories to the /doc subpage."
  1459.     -- @args - a table of arguments passed by the user
  1460.     -- @env - environment table containing title objects, etc., generated with p.getEnvironment
  1461.     -- Messages:
  1462.     -- 'doc-link-display' --&gt; '/doc'
  1463.     -- 'add-categories-blurb' --&gt; 'Please add categories to the $1 subpage.'
  1464.     --]]
  1465.     local docTitle = env.docTitle
  1466.     if not docTitle then
  1467.         return nil
  1468.     end
  1469.     local docPathLink = makeWikilink(docTitle.prefixedText, message('doc-link-display'))
  1470.     return message('add-categories-blurb', {docPathLink})
  1471. end
  1472.  
  1473. function p.makeSubpagesBlurb(args, env)
  1474.     --[[
  1475.     -- Generates the "Subpages of this template" link.
  1476.     -- @args - a table of arguments passed by the user
  1477.     -- @env - environment table containing title objects, etc., generated with p.getEnvironment
  1478.    
  1479.     -- Messages:
  1480.     -- 'template-pagetype' --&gt; 'template'
  1481.     -- 'module-pagetype' --&gt; 'module'
  1482.     -- 'default-pagetype' --&gt; 'page'
  1483.     -- 'subpages-link-display' --&gt; 'Subpages of this $1'
  1484.     --]]
  1485.     local subjectSpace = env.subjectSpace
  1486.     local templateTitle = env.templateTitle
  1487.     if not subjectSpace or not templateTitle then
  1488.         return nil
  1489.     end
  1490.     local pagetype
  1491.     if subjectSpace == 10 then
  1492.         pagetype = message('template-pagetype')
  1493.     elseif subjectSpace == 828 then
  1494.         pagetype = message('module-pagetype')
  1495.     else
  1496.         pagetype = message('default-pagetype')
  1497.     end
  1498.     local subpagesLink = makeWikilink(
  1499.         'Special:PrefixIndex/' .. templateTitle.prefixedText .. '/',
  1500.         message('subpages-link-display', {pagetype})
  1501.     )
  1502.     return message('subpages-blurb', {subpagesLink})
  1503. end
  1504.  
  1505. ----------------------------------------------------------------------------
  1506. -- Tracking categories
  1507. ----------------------------------------------------------------------------
  1508.  
  1509. function p.addTrackingCategories(env)
  1510.     --[[
  1511.     -- Check if {{documentation}} is transcluded on a /doc or /testcases page.
  1512.     -- @env - environment table containing title objects, etc., generated with p.getEnvironment
  1513.    
  1514.     -- Messages:
  1515.     -- 'display-strange-usage-category' --&gt; true
  1516.     -- 'doc-subpage' --&gt; 'doc'
  1517.     -- 'testcases-subpage' --&gt; 'testcases'
  1518.     -- 'strange-usage-category' --&gt; 'Wikipedia pages with strange ((documentation)) usage'
  1519.     --
  1520.     -- /testcases pages in the module namespace are not categorised, as they may have
  1521.     -- {{documentation}} transcluded automatically.
  1522.     --]]
  1523.     local title = env.title
  1524.     local subjectSpace = env.subjectSpace
  1525.     if not title or not subjectSpace then
  1526.         return nil
  1527.     end
  1528.     local subpage = title.subpageText
  1529.     local ret = ''
  1530.     if message('display-strange-usage-category', nil, 'boolean')
  1531.         and (
  1532.             subpage == message('doc-subpage')
  1533.             or subjectSpace ~= 828 and subpage == message('testcases-subpage')
  1534.         )
  1535.     then
  1536.         ret = ret .. makeCategoryLink(message('strange-usage-category'))
  1537.     end
  1538.     return ret
  1539. end
  1540.  
  1541. return p</text>
  1542.       <sha1>hhe6rl9gdvqywjk6r1tbi4ymwotbf24</sha1>
  1543.     </revision>
  1544.   </page>
  1545.   <page>
  1546.     <title>Module:Documentation/config</title>
  1547.     <ns>828</ns>
  1548.     <id>41520829</id>
  1549.     <revision>
  1550.       <id>1067946425</id>
  1551.       <parentid>1061447213</parentid>
  1552.       <timestamp>2022-01-25T23:46:11Z</timestamp>
  1553.       <contributor>
  1554.         <username>Ianblair23</username>
  1555.         <id>145927</id>
  1556.       </contributor>
  1557.       <comment>link</comment>
  1558.       <model>Scribunto</model>
  1559.       <format>text/plain</format>
  1560.       <text bytes="17433" xml:space="preserve">----------------------------------------------------------------------------------------------------
  1561. --
  1562. --                               Configuration for Module:Documentation
  1563. --
  1564. -- Here you can set the values of the parameters and messages used in Module:Documentation to
  1565. -- localise it to your wiki and your language. Unless specified otherwise, values given here
  1566. -- should be string values.
  1567. ----------------------------------------------------------------------------------------------------
  1568.  
  1569. local cfg = {} -- Do not edit this line.
  1570.  
  1571. ----------------------------------------------------------------------------------------------------
  1572. -- Protection template configuration
  1573. ----------------------------------------------------------------------------------------------------
  1574.  
  1575. -- cfg['protection-reason-edit']
  1576. -- The protection reason for edit-protected templates to pass to
  1577. -- [[Module:Protection banner]].
  1578. cfg['protection-reason-edit'] = 'template'
  1579.  
  1580. --[[
  1581. ----------------------------------------------------------------------------------------------------
  1582. -- Sandbox notice configuration
  1583. --
  1584. -- On sandbox pages the module can display a template notifying users that the current page is a
  1585. -- sandbox, and the location of test cases pages, etc. The module decides whether the page is a
  1586. -- sandbox or not based on the value of cfg['sandbox-subpage']. The following settings configure the
  1587. -- messages that the notices contains.
  1588. ----------------------------------------------------------------------------------------------------
  1589. --]]
  1590.  
  1591. -- cfg['sandbox-notice-image']
  1592. -- The image displayed in the sandbox notice.
  1593. cfg['sandbox-notice-image'] = '[[File:Sandbox.svg|50px|alt=|link=]]'
  1594.  
  1595. --[[
  1596. -- cfg['sandbox-notice-pagetype-template']
  1597. -- cfg['sandbox-notice-pagetype-module']
  1598. -- cfg['sandbox-notice-pagetype-other']
  1599. -- The page type of the sandbox page. The message that is displayed depends on the current subject
  1600. -- namespace. This message is used in either cfg['sandbox-notice-blurb'] or
  1601. -- cfg['sandbox-notice-diff-blurb'].
  1602. --]]
  1603. cfg['sandbox-notice-pagetype-template'] = '[[Wikipedia:Template test cases|template sandbox]] page'
  1604. cfg['sandbox-notice-pagetype-module'] = '[[Wikipedia:Template test cases|module sandbox]] page'
  1605. cfg['sandbox-notice-pagetype-other'] = 'sandbox page'
  1606.  
  1607. --[[
  1608. -- cfg['sandbox-notice-blurb']
  1609. -- cfg['sandbox-notice-diff-blurb']
  1610. -- cfg['sandbox-notice-diff-display']
  1611. -- Either cfg['sandbox-notice-blurb'] or cfg['sandbox-notice-diff-blurb'] is the opening sentence
  1612. -- of the sandbox notice. The latter has a diff link, but the former does not. $1 is the page
  1613. -- type, which is either cfg['sandbox-notice-pagetype-template'],
  1614. -- cfg['sandbox-notice-pagetype-module'] or cfg['sandbox-notice-pagetype-other'] depending what
  1615. -- namespace we are in. $2 is a link to the main template page, and $3 is a diff link between
  1616. -- the sandbox and the main template. The display value of the diff link is set by
  1617. -- cfg['sandbox-notice-compare-link-display'].
  1618. --]]
  1619. cfg['sandbox-notice-blurb'] = 'This is the $1 for $2.'
  1620. cfg['sandbox-notice-diff-blurb'] = 'This is the $1 for $2 ($3).'
  1621. cfg['sandbox-notice-compare-link-display'] = 'diff'
  1622.  
  1623. --[[
  1624. -- cfg['sandbox-notice-testcases-blurb']
  1625. -- cfg['sandbox-notice-testcases-link-display']
  1626. -- cfg['sandbox-notice-testcases-run-blurb']
  1627. -- cfg['sandbox-notice-testcases-run-link-display']
  1628. -- cfg['sandbox-notice-testcases-blurb'] is a sentence notifying the user that there is a test cases page
  1629. -- corresponding to this sandbox that they can edit. $1 is a link to the test cases page.
  1630. -- cfg['sandbox-notice-testcases-link-display'] is the display value for that link.
  1631. -- cfg['sandbox-notice-testcases-run-blurb'] is a sentence notifying the user that there is a test cases page
  1632. -- corresponding to this sandbox that they can edit, along with a link to run it. $1 is a link to the test
  1633. -- cases page, and $2 is a link to the page to run it.
  1634. -- cfg['sandbox-notice-testcases-run-link-display'] is the display value for the link to run the test
  1635. -- cases.
  1636. --]]
  1637. cfg['sandbox-notice-testcases-blurb'] = 'See also the companion subpage for $1.'
  1638. cfg['sandbox-notice-testcases-link-display'] = 'test cases'
  1639. cfg['sandbox-notice-testcases-run-blurb'] = 'See also the companion subpage for $1 ($2).'
  1640. cfg['sandbox-notice-testcases-run-link-display'] = 'run'
  1641.  
  1642. -- cfg['sandbox-category']
  1643. -- A category to add to all template sandboxes.
  1644. cfg['sandbox-category'] = 'Template sandboxes'
  1645.  
  1646. ----------------------------------------------------------------------------------------------------
  1647. -- Start box configuration
  1648. ----------------------------------------------------------------------------------------------------
  1649.  
  1650. -- cfg['documentation-icon-wikitext']
  1651. -- The wikitext for the icon shown at the top of the template.
  1652. cfg['documentation-icon-wikitext'] = '[[File:Test Template Info-Icon - Version (2).svg|50px|link=|alt=]]'
  1653.  
  1654. -- cfg['template-namespace-heading']
  1655. -- The heading shown in the template namespace.
  1656. cfg['template-namespace-heading'] = 'Template documentation'
  1657.  
  1658. -- cfg['module-namespace-heading']
  1659. -- The heading shown in the module namespace.
  1660. cfg['module-namespace-heading'] = 'Module documentation'
  1661.  
  1662. -- cfg['file-namespace-heading']
  1663. -- The heading shown in the file namespace.
  1664. cfg['file-namespace-heading'] = 'Summary'
  1665.  
  1666. -- cfg['other-namespaces-heading']
  1667. -- The heading shown in other namespaces.
  1668. cfg['other-namespaces-heading'] = 'Documentation'
  1669.  
  1670. -- cfg['view-link-display']
  1671. -- The text to display for "view" links.
  1672. cfg['view-link-display'] = 'view'
  1673.  
  1674. -- cfg['edit-link-display']
  1675. -- The text to display for "edit" links.
  1676. cfg['edit-link-display'] = 'edit'
  1677.  
  1678. -- cfg['history-link-display']
  1679. -- The text to display for "history" links.
  1680. cfg['history-link-display'] = 'history'
  1681.  
  1682. -- cfg['purge-link-display']
  1683. -- The text to display for "purge" links.
  1684. cfg['purge-link-display'] = 'purge'
  1685.  
  1686. -- cfg['create-link-display']
  1687. -- The text to display for "create" links.
  1688. cfg['create-link-display'] = 'create'
  1689.  
  1690. ----------------------------------------------------------------------------------------------------
  1691. -- Link box (end box) configuration
  1692. ----------------------------------------------------------------------------------------------------
  1693.  
  1694. -- cfg['transcluded-from-blurb']
  1695. -- Notice displayed when the docs are transcluded from another page. $1 is a wikilink to that page.
  1696. cfg['transcluded-from-blurb'] = 'The above [[Wikipedia:Template documentation|documentation]] is [[Help:Transclusion|transcluded]] from $1.'
  1697.  
  1698. --[[
  1699. -- cfg['create-module-doc-blurb']
  1700. -- Notice displayed in the module namespace when the documentation subpage does not exist.
  1701. -- $1 is a link to create the documentation page with the preload cfg['module-preload'] and the
  1702. -- display cfg['create-link-display'].
  1703. --]]
  1704. cfg['create-module-doc-blurb'] = 'You might want to $1 a documentation page for this [[Wikipedia:Lua|Scribunto module]].'
  1705.  
  1706. ----------------------------------------------------------------------------------------------------
  1707. -- Experiment blurb configuration
  1708. ----------------------------------------------------------------------------------------------------
  1709.  
  1710. --[[
  1711. -- cfg['experiment-blurb-template']
  1712. -- cfg['experiment-blurb-module']
  1713. -- The experiment blurb is the text inviting editors to experiment in sandbox and test cases pages.
  1714. -- It is only shown in the template and module namespaces. With the default English settings, it
  1715. -- might look like this:
  1716. --
  1717. -- Editors can experiment in this template's sandbox (edit | diff) and testcases (edit) pages.
  1718. --
  1719. -- In this example, "sandbox", "edit", "diff", "testcases", and "edit" would all be links.
  1720. --
  1721. -- There are two versions, cfg['experiment-blurb-template'] and cfg['experiment-blurb-module'], depending
  1722. -- on what namespace we are in.
  1723. --
  1724. -- Parameters:
  1725. --
  1726. -- $1 is a link to the sandbox page. If the sandbox exists, it is in the following format:
  1727. --
  1728. --     cfg['sandbox-link-display'] (cfg['sandbox-edit-link-display'] | cfg['compare-link-display'])
  1729. --
  1730. -- If the sandbox doesn't exist, it is in the format:
  1731. --
  1732. --     cfg['sandbox-link-display'] (cfg['sandbox-create-link-display'] | cfg['mirror-link-display'])
  1733. --
  1734. -- The link for cfg['sandbox-create-link-display'] link preloads the page with cfg['template-sandbox-preload']
  1735. -- or cfg['module-sandbox-preload'], depending on the current namespace. The link for cfg['mirror-link-display']
  1736. -- loads a default edit summary of cfg['mirror-edit-summary'].
  1737. --
  1738. -- $2 is a link to the test cases page. If the test cases page exists, it is in the following format:
  1739. --
  1740. --     cfg['testcases-link-display'] (cfg['testcases-edit-link-display'] | cfg['testcases-run-link-display'])
  1741. --
  1742. -- If the test cases page doesn't exist, it is in the format:
  1743. --
  1744. --     cfg['testcases-link-display'] (cfg['testcases-create-link-display'])
  1745. --
  1746. -- If the test cases page doesn't exist, the link for cfg['testcases-create-link-display'] preloads the
  1747. -- page with cfg['template-testcases-preload'] or cfg['module-testcases-preload'], depending on the current
  1748. -- namespace.
  1749. --]]
  1750. cfg['experiment-blurb-template'] = "Editors can experiment in this template's $1 and $2 pages."
  1751. cfg['experiment-blurb-module'] = "Editors can experiment in this module's $1 and $2 pages."
  1752.  
  1753. ----------------------------------------------------------------------------------------------------
  1754. -- Sandbox link configuration
  1755. ----------------------------------------------------------------------------------------------------
  1756.  
  1757. -- cfg['sandbox-subpage']
  1758. -- The name of the template subpage typically used for sandboxes.
  1759. cfg['sandbox-subpage'] = 'sandbox'
  1760.  
  1761. -- cfg['template-sandbox-preload']
  1762. -- Preload file for template sandbox pages.
  1763. cfg['template-sandbox-preload'] = 'Template:Documentation/preload-sandbox'
  1764.  
  1765. -- cfg['module-sandbox-preload']
  1766. -- Preload file for Lua module sandbox pages.
  1767. cfg['module-sandbox-preload'] = 'Template:Documentation/preload-module-sandbox'
  1768.  
  1769. -- cfg['sandbox-link-display']
  1770. -- The text to display for "sandbox" links.
  1771. cfg['sandbox-link-display'] = 'sandbox'
  1772.  
  1773. -- cfg['sandbox-edit-link-display']
  1774. -- The text to display for sandbox "edit" links.
  1775. cfg['sandbox-edit-link-display'] = 'edit'
  1776.  
  1777. -- cfg['sandbox-create-link-display']
  1778. -- The text to display for sandbox "create" links.
  1779. cfg['sandbox-create-link-display'] = 'create'
  1780.  
  1781. -- cfg['compare-link-display']
  1782. -- The text to display for "compare" links.
  1783. cfg['compare-link-display'] = 'diff'
  1784.  
  1785. -- cfg['mirror-edit-summary']
  1786. -- The default edit summary to use when a user clicks the "mirror" link. $1 is a wikilink to the
  1787. -- template page.
  1788. cfg['mirror-edit-summary'] = 'Create sandbox version of $1'
  1789.  
  1790. -- cfg['mirror-link-display']
  1791. -- The text to display for "mirror" links.
  1792. cfg['mirror-link-display'] = 'mirror'
  1793.  
  1794. -- cfg['mirror-link-preload']
  1795. -- The page to preload when a user clicks the "mirror" link.
  1796. cfg['mirror-link-preload'] = 'Template:Documentation/mirror'
  1797.  
  1798. ----------------------------------------------------------------------------------------------------
  1799. -- Test cases link configuration
  1800. ----------------------------------------------------------------------------------------------------
  1801.  
  1802. -- cfg['testcases-subpage']
  1803. -- The name of the template subpage typically used for test cases.
  1804. cfg['testcases-subpage'] = 'testcases'
  1805.  
  1806. -- cfg['template-testcases-preload']
  1807. -- Preload file for template test cases pages.
  1808. cfg['template-testcases-preload'] = 'Template:Documentation/preload-testcases'
  1809.  
  1810. -- cfg['module-testcases-preload']
  1811. -- Preload file for Lua module test cases pages.
  1812. cfg['module-testcases-preload'] = 'Template:Documentation/preload-module-testcases'
  1813.  
  1814. -- cfg['testcases-link-display']
  1815. -- The text to display for "testcases" links.
  1816. cfg['testcases-link-display'] = 'testcases'
  1817.  
  1818. -- cfg['testcases-edit-link-display']
  1819. -- The text to display for test cases "edit" links.
  1820. cfg['testcases-edit-link-display'] = 'edit'
  1821.  
  1822. -- cfg['testcases-run-link-display']
  1823. -- The text to display for test cases "run" links.
  1824. cfg['testcases-run-link-display'] = 'run'
  1825.  
  1826. -- cfg['testcases-create-link-display']
  1827. -- The text to display for test cases "create" links.
  1828. cfg['testcases-create-link-display'] = 'create'
  1829.  
  1830. ----------------------------------------------------------------------------------------------------
  1831. -- Add categories blurb configuration
  1832. ----------------------------------------------------------------------------------------------------
  1833.  
  1834. --[[
  1835. -- cfg['add-categories-blurb']
  1836. -- Text to direct users to add categories to the /doc subpage. Not used if the "content" or
  1837. -- "docname fed" arguments are set, as then it is not clear where to add the categories. $1 is a
  1838. -- link to the /doc subpage with a display value of cfg['doc-link-display'].
  1839. --]]
  1840. cfg['add-categories-blurb'] = 'Add categories to the $1 subpage.'
  1841.  
  1842. -- cfg['doc-link-display']
  1843. -- The text to display when linking to the /doc subpage.
  1844. cfg['doc-link-display'] = '/doc'
  1845.  
  1846. ----------------------------------------------------------------------------------------------------
  1847. -- Subpages link configuration
  1848. ----------------------------------------------------------------------------------------------------
  1849.  
  1850. --[[
  1851. -- cfg['subpages-blurb']
  1852. -- The "Subpages of this template" blurb. $1 is a link to the main template's subpages with a
  1853. -- display value of cfg['subpages-link-display']. In the English version this blurb is simply
  1854. -- the link followed by a period, and the link display provides the actual text.
  1855. --]]
  1856. cfg['subpages-blurb'] = '$1.'
  1857.  
  1858. --[[
  1859. -- cfg['subpages-link-display']
  1860. -- The text to display for the "subpages of this page" link. $1 is cfg['template-pagetype'],
  1861. -- cfg['module-pagetype'] or cfg['default-pagetype'], depending on whether the current page is in
  1862. -- the template namespace, the module namespace, or another namespace.
  1863. --]]
  1864. cfg['subpages-link-display'] = 'Subpages of this $1'
  1865.  
  1866. -- cfg['template-pagetype']
  1867. -- The pagetype to display for template pages.
  1868. cfg['template-pagetype'] = 'template'
  1869.  
  1870. -- cfg['module-pagetype']
  1871. -- The pagetype to display for Lua module pages.
  1872. cfg['module-pagetype'] = 'module'
  1873.  
  1874. -- cfg['default-pagetype']
  1875. -- The pagetype to display for pages other than templates or Lua modules.
  1876. cfg['default-pagetype'] = 'page'
  1877.  
  1878. ----------------------------------------------------------------------------------------------------
  1879. -- Doc link configuration
  1880. ----------------------------------------------------------------------------------------------------
  1881.  
  1882. -- cfg['doc-subpage']
  1883. -- The name of the subpage typically used for documentation pages.
  1884. cfg['doc-subpage'] = 'doc'
  1885.  
  1886. -- cfg['docpage-preload']
  1887. -- Preload file for template documentation pages in all namespaces.
  1888. cfg['docpage-preload'] = 'Template:Documentation/preload'
  1889.  
  1890. -- cfg['module-preload']
  1891. -- Preload file for Lua module documentation pages.
  1892. cfg['module-preload'] = 'Template:Documentation/preload-module-doc'
  1893.  
  1894. ----------------------------------------------------------------------------------------------------
  1895. -- HTML and CSS configuration
  1896. ----------------------------------------------------------------------------------------------------
  1897.  
  1898. -- cfg['templatestyles']
  1899. -- The name of the TemplateStyles page where CSS is kept.
  1900. -- Sandbox CSS will be at Module:Documentation/sandbox/styles.css when needed.
  1901. cfg['templatestyles'] = 'Module:Documentation/styles.css'
  1902.  
  1903. -- cfg['container']
  1904. -- Class which can be used to set flex or grid CSS on the
  1905. -- two child divs documentation and documentation-metadata
  1906. cfg['container'] = 'documentation-container'
  1907.  
  1908. -- cfg['main-div-classes']
  1909. -- Classes added to the main HTML "div" tag.
  1910. cfg['main-div-classes'] = 'documentation'
  1911.  
  1912. -- cfg['main-div-heading-class']
  1913. -- Class for the main heading for templates and modules and assoc. talk spaces
  1914. cfg['main-div-heading-class'] = 'documentation-heading'
  1915.  
  1916. -- cfg['start-box-class']
  1917. -- Class for the start box
  1918. cfg['start-box-class'] = 'documentation-startbox'
  1919.  
  1920. -- cfg['start-box-link-classes']
  1921. -- Classes used for the [view][edit][history] or [create] links in the start box.
  1922. -- mw-editsection-like is per [[Wikipedia:Village pump (technical)/Archive 117]]
  1923. cfg['start-box-link-classes'] = 'mw-editsection-like plainlinks'
  1924.  
  1925. -- cfg['end-box-class']
  1926. -- Class for the end box.
  1927. cfg['end-box-class'] = 'documentation-metadata'
  1928.  
  1929. -- cfg['end-box-plainlinks']
  1930. -- Plainlinks
  1931. cfg['end-box-plainlinks'] = 'plainlinks'
  1932.  
  1933. -- cfg['toolbar-class']
  1934. -- Class added for toolbar links.
  1935. cfg['toolbar-class'] = 'documentation-toolbar'
  1936.  
  1937. -- cfg['clear']
  1938. -- Just used to clear things.
  1939. cfg['clear'] = 'documentation-clear'
  1940.  
  1941.  
  1942. ----------------------------------------------------------------------------------------------------
  1943. -- Tracking category configuration
  1944. ----------------------------------------------------------------------------------------------------
  1945.  
  1946. -- cfg['display-strange-usage-category']
  1947. -- Set to true to enable output of cfg['strange-usage-category'] if the module is used on a /doc subpage
  1948. -- or a /testcases subpage. This should be a boolean value (either true or false).
  1949. cfg['display-strange-usage-category'] = true
  1950.  
  1951. -- cfg['strange-usage-category']
  1952. -- Category to output if cfg['display-strange-usage-category'] is set to true and the module is used on a
  1953. -- /doc subpage or a /testcases subpage.
  1954. cfg['strange-usage-category'] = 'Wikipedia pages with strange ((documentation)) usage'
  1955.  
  1956. --[[
  1957. ----------------------------------------------------------------------------------------------------
  1958. -- End configuration
  1959. --
  1960. -- Don't edit anything below this line.
  1961. ----------------------------------------------------------------------------------------------------
  1962. --]]
  1963.  
  1964. return cfg</text>
  1965.       <sha1>da6pt1lzpy4z0skaztwblaqnfhizyf7</sha1>
  1966.     </revision>
  1967.   </page>
  1968.   <page>
  1969.     <title>Module:Documentation/styles.css</title>
  1970.     <ns>828</ns>
  1971.     <id>61440006</id>
  1972.     <revision>
  1973.       <id>989579069</id>
  1974.       <parentid>989578481</parentid>
  1975.       <timestamp>2020-11-19T20:21:58Z</timestamp>
  1976.       <contributor>
  1977.         <username>Izno</username>
  1978.         <id>2927383</id>
  1979.       </contributor>
  1980.       <minor/>
  1981.       <comment>Changed protection level for "[[Module:Documentation/styles.css]]": actually match module ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite))</comment>
  1982.       <model>sanitized-css</model>
  1983.       <format>text/css</format>
  1984.       <text bytes="716" xml:space="preserve">/* {{pp|small=yes}} */
  1985. .documentation,
  1986. .documentation-metadata {
  1987.     border: 1px solid #a2a9b1;
  1988.     background-color: #ecfcf4;
  1989.     clear: both;
  1990. }
  1991.  
  1992. .documentation {
  1993.     margin: 1em 0 0 0;
  1994.     padding: 1em;
  1995. }
  1996.  
  1997. .documentation-metadata {
  1998.     margin: 0.2em 0; /* same margin left-right as .documentation */
  1999.     font-style: italic;
  2000.     padding: 0.4em 1em; /* same padding left-right as .documentation */
  2001. }
  2002.  
  2003. .documentation-startbox {
  2004.     padding-bottom: 3px;
  2005.     border-bottom: 1px solid #aaa;
  2006.     margin-bottom: 1ex;
  2007. }
  2008.  
  2009. .documentation-heading {
  2010.     font-weight: bold;
  2011.     font-size: 125%;
  2012. }
  2013.  
  2014. .documentation-clear { /* Don't want things to stick out where they shouldn't. */
  2015.     clear: both;
  2016. }
  2017.  
  2018. .documentation-toolbar {
  2019.     font-style: normal;
  2020.     font-size: 85%;
  2021. }</text>
  2022.       <sha1>o2ia525en7wbq32mq4fpdevtl5d6pcb</sha1>
  2023.     </revision>
  2024.   </page>
  2025.   <page>
  2026.     <title>Module:Effective protection expiry</title>
  2027.     <ns>828</ns>
  2028.     <id>48785459</id>
  2029.     <revision>
  2030.       <id>948472505</id>
  2031.       <parentid>948472504</parentid>
  2032.       <timestamp>2020-04-01T06:12:42Z</timestamp>
  2033.       <contributor>
  2034.         <username>MusikAnimal</username>
  2035.         <id>14882394</id>
  2036.       </contributor>
  2037.       <minor/>
  2038.       <comment>1 revision imported</comment>
  2039.       <model>Scribunto</model>
  2040.       <format>text/plain</format>
  2041.       <text bytes="1494" xml:space="preserve">local p = {}
  2042.  
  2043. -- Returns the expiry of a restriction of an action on a given title, or unknown if it cannot be known.
  2044. -- If no title is specified, the title of the page being displayed is used.
  2045. function p._main(action, pagename)
  2046.     local title
  2047.     if type(pagename) == 'table' and pagename.prefixedText then
  2048.         title = pagename
  2049.     elseif pagename then
  2050.         title = mw.title.new(pagename)
  2051.     else
  2052.         title = mw.title.getCurrentTitle()
  2053.     end
  2054.     pagename = title.prefixedText
  2055.     if action == 'autoreview' then
  2056.         local stabilitySettings = mw.ext.FlaggedRevs.getStabilitySettings(title)
  2057.         return stabilitySettings and stabilitySettings.expiry or 'unknown'
  2058.     elseif action ~= 'edit' and action ~= 'move' and action ~= 'create' and action ~= 'upload' then
  2059.         error( 'First parameter must be one of edit, move, create, upload, autoreview', 2 )
  2060.     end
  2061.     local rawExpiry = mw.getCurrentFrame():callParserFunction('PROTECTIONEXPIRY', action, pagename)
  2062.     if rawExpiry == 'infinity' then
  2063.         return 'infinity'
  2064.     elseif rawExpiry == '' then
  2065.         return 'unknown'
  2066.     else
  2067.         local year, month, day, hour, minute, second = rawExpiry:match(
  2068.             '^(%d%d%d%d)(%d%d)(%d%d)(%d%d)(%d%d)(%d%d)$'
  2069.         )
  2070.         if year then
  2071.             return string.format(
  2072.                 '%s-%s-%sT%s:%s:%s',
  2073.                 year, month, day, hour, minute, second
  2074.             )
  2075.         else
  2076.             error('internal error in Module:Effective protection expiry; malformed expiry timestamp')
  2077.         end
  2078.     end
  2079. end
  2080.  
  2081. setmetatable(p, { __index = function(t, k)
  2082.     return function(frame)
  2083.         return t._main(k, frame.args[1])
  2084.     end
  2085. end })
  2086.  
  2087. return p</text>
  2088.       <sha1>i1wms02y1w048cq6uttka18a5pjzp63</sha1>
  2089.     </revision>
  2090.   </page>
  2091.   <page>
  2092.     <title>Module:Effective protection level</title>
  2093.     <ns>828</ns>
  2094.     <id>41617327</id>
  2095.     <revision>
  2096.       <id>980896975</id>
  2097.       <parentid>948472507</parentid>
  2098.       <timestamp>2020-09-29T03:38:47Z</timestamp>
  2099.       <contributor>
  2100.         <username>Jackmcbarn</username>
  2101.         <id>19285809</id>
  2102.       </contributor>
  2103.       <comment>bring in changes from sandbox</comment>
  2104.       <model>Scribunto</model>
  2105.       <format>text/plain</format>
  2106.       <text bytes="3438" xml:space="preserve">local p = {}
  2107.  
  2108. -- Returns the permission required to perform a given action on a given title.
  2109. -- If no title is specified, the title of the page being displayed is used.
  2110. function p._main(action, pagename)
  2111.     local title
  2112.     if type(pagename) == 'table' and pagename.prefixedText then
  2113.         title = pagename
  2114.     elseif pagename then
  2115.         title = mw.title.new(pagename)
  2116.     else
  2117.         title = mw.title.getCurrentTitle()
  2118.     end
  2119.     pagename = title.prefixedText
  2120.     if action == 'autoreview' then
  2121.         local level = mw.ext.FlaggedRevs.getStabilitySettings(title)
  2122.         level = level and level.autoreview
  2123.         if level == 'review' then
  2124.             return 'reviewer'
  2125.         elseif level ~= '' then
  2126.             return level
  2127.         else
  2128.             return nil -- not '*'. a page not being PC-protected is distinct from it being PC-protected with anyone able to review. also not '', as that would mean PC-protected but nobody can review
  2129.         end
  2130.     elseif action ~= 'edit' and action ~= 'move' and action ~= 'create' and action ~= 'upload' and action ~= 'undelete' then
  2131.         error( 'First parameter must be one of edit, move, create, upload, undelete, autoreview', 2 )
  2132.     end
  2133.     if title.namespace == 8 then -- MediaWiki namespace
  2134.         if title.text:sub(-3) == '.js' or title.text:sub(-4) == '.css' or title.contentModel == 'javascript' or title.contentModel == 'css' then -- site JS or CSS page
  2135.             return 'interfaceadmin'
  2136.         else -- any non-JS/CSS MediaWiki page
  2137.             return 'sysop'
  2138.         end
  2139.     elseif title.namespace == 2 and title.isSubpage then
  2140.         if title.contentModel == 'javascript' or title.contentModel == 'css' then -- user JS or CSS page
  2141.             return 'interfaceadmin'
  2142.         elseif title.contentModel == 'json' then -- user JSON page
  2143.             return 'sysop'
  2144.         end
  2145.     end
  2146.     if action == 'undelete' then
  2147.         return 'sysop'
  2148.     end
  2149.     local level = title.protectionLevels[action] and title.protectionLevels[action][1]
  2150.     if level == 'sysop' or level == 'editprotected' then
  2151.         return 'sysop'
  2152.     elseif title.cascadingProtection.restrictions[action] and title.cascadingProtection.restrictions[action][1] then -- used by a cascading-protected page
  2153.         return 'sysop'
  2154.     elseif level == 'templateeditor' then
  2155.         return 'templateeditor'
  2156.     elseif action == 'move' then
  2157.         local blacklistentry = mw.ext.TitleBlacklist.test('edit', pagename) -- Testing action edit is correct, since this is for the source page. The target page name gets tested with action move.
  2158.         if blacklistentry and not blacklistentry.params.autoconfirmed then
  2159.             return 'templateeditor'
  2160.         elseif title.namespace == 6 then
  2161.             return 'filemover'
  2162.         elseif level == 'extendedconfirmed' then
  2163.             return 'extendedconfirmed'
  2164.         else
  2165.             return 'autoconfirmed'
  2166.         end
  2167.     end
  2168.     local blacklistentry = mw.ext.TitleBlacklist.test(action, pagename)
  2169.     if blacklistentry then
  2170.         if not blacklistentry.params.autoconfirmed then
  2171.             return 'templateeditor'
  2172.         elseif level == 'extendedconfirmed' then
  2173.             return 'extendedconfirmed'
  2174.         else
  2175.             return 'autoconfirmed'
  2176.         end
  2177.     elseif level == 'editsemiprotected' then -- create-semiprotected pages return this for some reason
  2178.         return 'autoconfirmed'
  2179.     elseif level then
  2180.         return level
  2181.     elseif action == 'upload' then
  2182.         return 'autoconfirmed'
  2183.     elseif action == 'create' and title.namespace % 2 == 0 and title.namespace ~= 118 then -- You need to be registered, but not autoconfirmed, to create non-talk pages other than drafts
  2184.         return 'user'
  2185.     else
  2186.         return '*'
  2187.     end
  2188. end
  2189.  
  2190. setmetatable(p, { __index = function(t, k)
  2191.     return function(frame)
  2192.         return t._main(k, frame.args[1])
  2193.     end
  2194. end })
  2195.  
  2196. return p</text>
  2197.       <sha1>d3lhy0comfcfe9lnnj8klmrfqm1oxw7</sha1>
  2198.     </revision>
  2199.   </page>
  2200.   <page>
  2201.     <title>Module:File link</title>
  2202.     <ns>828</ns>
  2203.     <id>42903140</id>
  2204.     <revision>
  2205.       <id>948474149</id>
  2206.       <parentid>948472509</parentid>
  2207.       <timestamp>2020-04-01T06:31:54Z</timestamp>
  2208.       <contributor>
  2209.         <username>MusikAnimal</username>
  2210.         <id>14882394</id>
  2211.       </contributor>
  2212.       <comment>Undid revision 948472508 by [[Special:Contributions/w&gt;IPad365|w&gt;IPad365]] ([[User talk:w&gt;IPad365|talk]])</comment>
  2213.       <model>Scribunto</model>
  2214.       <format>text/plain</format>
  2215.       <text bytes="2544" xml:space="preserve">-- This module provides a library for formatting file wikilinks.
  2216.  
  2217. local yesno = require('Module:Yesno')
  2218. local checkType = require('libraryUtil').checkType
  2219.  
  2220. local p = {}
  2221.  
  2222. function p._main(args)
  2223.     checkType('_main', 1, args, 'table')
  2224.  
  2225.     -- This is basically libraryUtil.checkTypeForNamedArg, but we are rolling our
  2226.     -- own function to get the right error level.
  2227.     local function checkArg(key, val, level)
  2228.         if type(val) ~= 'string' then
  2229.             error(string.format(
  2230.                 "type error in '%s' parameter of '_main' (expected string, got %s)",
  2231.                 key, type(val)
  2232.             ), level)
  2233.         end
  2234.     end
  2235.  
  2236.     local ret = {}
  2237.  
  2238.     -- Adds a positional parameter to the buffer.
  2239.     local function addPositional(key)
  2240.         local val = args[key]
  2241.         if not val then
  2242.             return nil
  2243.         end
  2244.         checkArg(key, val, 4)
  2245.         ret[#ret + 1] = val
  2246.     end
  2247.  
  2248.     -- Adds a named parameter to the buffer. We assume that the parameter name
  2249.     -- is the same as the argument key.
  2250.     local function addNamed(key)
  2251.         local val = args[key]
  2252.         if not val then
  2253.             return nil
  2254.         end
  2255.         checkArg(key, val, 4)
  2256.         ret[#ret + 1] = key .. '=' .. val
  2257.     end
  2258.  
  2259.     -- Filename
  2260.     checkArg('file', args.file, 3)
  2261.     ret[#ret + 1] = 'File:' .. args.file
  2262.  
  2263.     -- Format
  2264.     if args.format then
  2265.         checkArg('format', args.format)
  2266.         if args.formatfile then
  2267.             checkArg('formatfile', args.formatfile)
  2268.             ret[#ret + 1] = args.format .. '=' .. args.formatfile
  2269.         else
  2270.             ret[#ret + 1] = args.format
  2271.         end
  2272.     end
  2273.  
  2274.     -- Border
  2275.     if yesno(args.border) then
  2276.         ret[#ret + 1] = 'border'
  2277.     end
  2278.  
  2279.     addPositional('location')
  2280.     addPositional('alignment')
  2281.     addPositional('size')
  2282.     addNamed('upright')
  2283.     addNamed('link')
  2284.     addNamed('alt')
  2285.     addNamed('page')
  2286.     addNamed('class')
  2287.     addNamed('lang')
  2288.     addNamed('start')
  2289.     addNamed('end')
  2290.     addNamed('thumbtime')
  2291.     addPositional('caption')
  2292.  
  2293.     return string.format('[[%s]]', table.concat(ret, '|'))
  2294. end
  2295.  
  2296. function p.main(frame)
  2297.     local origArgs = require('Module:Arguments').getArgs(frame, {
  2298.         wrappers = 'Template:File link'
  2299.     })
  2300.     if not origArgs.file then
  2301.         error("'file' parameter missing from [[Template:File link]]", 0)
  2302.     end
  2303.  
  2304.     -- Copy the arguments that were passed to a new table to avoid looking up
  2305.     -- every possible parameter in the frame object.
  2306.     local args = {}
  2307.     for k, v in pairs(origArgs) do
  2308.         -- Make _BLANK a special argument to add a blank parameter. For use in
  2309.         -- conditional templates etc. it is useful for blank arguments to be
  2310.         -- ignored, but we still need a way to specify them so that we can do
  2311.         -- things like [[File:Example.png|link=]].
  2312.         if v == '_BLANK' then
  2313.             v = ''
  2314.         end
  2315.         args[k] = v
  2316.     end
  2317.     return p._main(args)
  2318. end
  2319.  
  2320. return p</text>
  2321.       <sha1>bzc22v133v9z5yc4aisazripn6l94p8</sha1>
  2322.     </revision>
  2323.   </page>
  2324.   <page>
  2325.     <title>Module:High-use</title>
  2326.     <ns>828</ns>
  2327.     <id>62062952</id>
  2328.     <revision>
  2329.       <id>1053091615</id>
  2330.       <parentid>1050945202</parentid>
  2331.       <timestamp>2021-11-01T21:11:55Z</timestamp>
  2332.       <contributor>
  2333.         <username>GKFX</username>
  2334.         <id>14918984</id>
  2335.       </contributor>
  2336.       <comment>Link to https://linkcount.toolforge.org/ which is more detailed and mobile-friendly</comment>
  2337.       <model>Scribunto</model>
  2338.       <format>text/plain</format>
  2339.       <text bytes="6690" xml:space="preserve">local p = {}
  2340.  
  2341. -- _fetch looks at the "demo" argument.
  2342. local _fetch = require('Module:Transclusion_count').fetch
  2343. local yesno = require('Module:Yesno')
  2344.  
  2345. function p.num(frame, count)
  2346.     if count == nil then
  2347.         if yesno(frame.args['fetch']) == false then
  2348.             if (frame.args[1] or '') ~= '' then count = tonumber(frame.args[1]) end
  2349.         else
  2350.             count = _fetch(frame)
  2351.         end
  2352.     end
  2353.    
  2354.     -- Build output string
  2355.     local return_value = ""
  2356.     if count == nil then
  2357.         if frame.args[1] == "risk" then
  2358.             return_value = "a very large number of"
  2359.         else
  2360.             return_value = "many"
  2361.         end
  2362.     else
  2363.         -- Use 2 significant figures for smaller numbers and 3 for larger ones
  2364.         local sigfig = 2
  2365.         if count &gt;= 100000 then
  2366.             sigfig = 3
  2367.         end
  2368.        
  2369.         -- Prepare to round to appropriate number of sigfigs
  2370.         local f = math.floor(math.log10(count)) - sigfig + 1
  2371.        
  2372.         -- Round and insert "approximately" or "+" when appropriate
  2373.         if (frame.args[2] == "yes") or (mw.ustring.sub(frame.args[1],-1) == "+") then
  2374.             -- Round down
  2375.             return_value = string.format("%s+", mw.getContentLanguage():formatNum(math.floor( (count / 10^(f)) ) * (10^(f))) )
  2376.         else
  2377.             -- Round to nearest
  2378.             return_value = string.format("approximately&amp;#x20;%s", mw.getContentLanguage():formatNum(math.floor( (count / 10^(f)) + 0.5) * (10^(f))) )
  2379.         end
  2380.  
  2381.         -- Insert percentage of pages if that is likely to be &gt;= 1% and when |no-percent= not set to yes
  2382.         if count and count &gt; 250000 and not yesno (frame:getParent().args['no-percent']) then
  2383.             local percent = math.floor( ( (count/frame:callParserFunction('NUMBEROFPAGES', 'R') ) * 100) + 0.5)
  2384.             if percent &gt;= 1 then
  2385.                 return_value = string.format("%s&amp;#x20;pages, or roughly %s%% of all", return_value, percent)
  2386.             end
  2387.         end
  2388.     end
  2389.    
  2390.     return return_value
  2391. end
  2392. -- Actions if there is a large (greater than or equal to 100,000) transclusion count
  2393. function p.risk(frame)
  2394.     local return_value = ""
  2395.     if frame.args[1] == "risk" then
  2396.         return_value = "risk"
  2397.     else
  2398.         local count = _fetch(frame)
  2399.         if count and count &gt;= 100000 then return_value = "risk" end
  2400.     end
  2401.     return return_value
  2402. end
  2403.  
  2404. function p.text(frame, count)
  2405.     -- Only show the information about how this template gets updated if someone
  2406.     -- is actually editing the page and maybe trying to update the count.
  2407.     local bot_text = (frame:preprocess("{{REVISIONID}}") == "") and "\n\n----\n'''Preview message''': Transclusion count updated automatically ([[Template:High-use/doc#Technical details|see documentation]])." or ''
  2408.    
  2409.     if count == nil then
  2410.         if yesno(frame.args['fetch']) == false then
  2411.             if (frame.args[1] or '') ~= '' then count = tonumber(frame.args[1]) end
  2412.         else
  2413.             count = _fetch(frame)
  2414.         end
  2415.     end
  2416.     local title = mw.title.getCurrentTitle()
  2417.     if title.subpageText == "doc" or title.subpageText == "sandbox" then
  2418.         title = title.basePageTitle
  2419.     end
  2420.    
  2421.     local systemMessages = frame.args['system']
  2422.     if frame.args['system'] == '' then
  2423.         systemMessages = nil
  2424.     end
  2425.    
  2426.     -- This retrieves the project URL automatically to simplify localiation.
  2427.     local templateCount = ('on [https://linkcount.toolforge.org/index.php?project=%s&amp;page=%s %s pages]'):format(
  2428.         mw.title.getCurrentTitle():fullUrl():gsub('//(.-)/.*', '%1'),
  2429.         mw.uri.encode(title.fullText), p.num(frame, count))
  2430.     local used_on_text = "'''This " .. (mw.title.getCurrentTitle().namespace == 828 and "Lua module" or "template") .. ' is used ';
  2431.     if systemMessages then
  2432.         used_on_text = used_on_text .. systemMessages ..
  2433.             ((count and count &gt; 2000) and (",''' and " .. templateCount) or ("'''"))
  2434.     else
  2435.         used_on_text = used_on_text .. templateCount .. "'''"
  2436.     end
  2437.    
  2438.    
  2439.     local sandbox_text =  ("%s's [[%s/sandbox|/sandbox]] or [[%s/testcases|/testcases]] subpages, or in your own [[%s]]. "):format(
  2440.         (mw.title.getCurrentTitle().namespace == 828 and "module" or "template"),
  2441.         title.fullText, title.fullText,
  2442.         mw.title.getCurrentTitle().namespace == 828 and "Module:Sandbox|module sandbox" or "Wikipedia:User pages#SUB|user subpage"
  2443.     )
  2444.    
  2445.     local infoArg = frame.args["info"] ~= "" and frame.args["info"]
  2446.     if (systemMessages or frame.args[1] == "risk" or (count and count &gt;= 100000) ) then
  2447.         local info = systemMessages and '.&lt;br/&gt;Changes to it can cause immediate changes to the Wikipedia user interface.' or '.'
  2448.         if infoArg then
  2449.             info = info .. "&lt;br /&gt;" .. infoArg
  2450.         end
  2451.         sandbox_text = info .. '&lt;br /&gt; To avoid major disruption' ..
  2452.             (count and count &gt;= 100000 and ' and server load' or '') ..
  2453.             ', any changes should be tested in the ' .. sandbox_text ..
  2454.             'The tested changes can be added to this page in a single edit. '
  2455.     else
  2456.         sandbox_text = (infoArg and ('.&lt;br /&gt;' .. infoArg .. ' C') or ' and c') ..
  2457.             'hanges may be widely noticed. Test changes in the ' .. sandbox_text
  2458.     end
  2459.  
  2460.    
  2461.     local discussion_text = systemMessages and 'Please discuss changes ' or 'Consider discussing changes '
  2462.     if frame.args["2"] and frame.args["2"] ~= "" and frame.args["2"] ~= "yes" then
  2463.         discussion_text = string.format("%sat [[%s]]", discussion_text, frame.args["2"])
  2464.     else
  2465.         discussion_text = string.format("%son the [[%s|talk page]]", discussion_text, title.talkPageTitle.fullText )
  2466.     end
  2467.    
  2468.     return used_on_text .. sandbox_text .. discussion_text .. " before implementing them." .. bot_text
  2469. end
  2470.  
  2471. function p.main(frame)
  2472.     local count = nil
  2473.     if yesno(frame.args['fetch']) == false then
  2474.         if (frame.args[1] or '') ~= '' then count = tonumber(frame.args[1]) end
  2475.     else
  2476.         count = _fetch(frame)
  2477.     end
  2478.     local image = "[[File:Ambox warning yellow.svg|40px|alt=Warning|link=]]"
  2479.     local type_param = "style"
  2480.     local epilogue = ''
  2481.     if frame.args['system'] and frame.args['system'] ~= '' then
  2482.         image = "[[File:Ambox important.svg|40px|alt=Warning|link=]]"
  2483.         type_param = "content"
  2484.         local nocat = frame:getParent().args['nocat'] or frame.args['nocat']
  2485.         local categorise = (nocat == '' or not yesno(nocat))
  2486.         if categorise then
  2487.             epilogue = frame:preprocess('{{Sandbox other||{{#switch:{{#invoke:Effective protection level|{{#switch:{{NAMESPACE}}|File=upload|#default=edit}}|{{FULLPAGENAME}}}}|sysop|templateeditor|interfaceadmin=|#default=[[Category:Pages used in system messages needing protection]]}}}}')
  2488.         end
  2489.     elseif (frame.args[1] == "risk" or (count and count &gt;= 100000)) then
  2490.         image = "[[File:Ambox warning orange.svg|40px|alt=Warning|link=]]"
  2491.         type_param = "content"
  2492.     end
  2493.    
  2494.     if frame.args["form"] == "editnotice" then
  2495.         return frame:expandTemplate{
  2496.                 title = 'editnotice',
  2497.                 args = {
  2498.                         ["image"] = image,
  2499.                         ["text"] = p.text(frame, count),
  2500.                         ["expiry"] = (frame.args["expiry"] or "")
  2501.                 }
  2502.         } .. epilogue
  2503.     else
  2504.         return require('Module:Message box').main('ombox', {
  2505.             type = type_param,
  2506.             image = image,
  2507.             text = p.text(frame, count),
  2508.             expiry = (frame.args["expiry"] or "")
  2509.         }) .. epilogue
  2510.     end
  2511. end
  2512.  
  2513. return p</text>
  2514.       <sha1>sy3y3omwgho4wnukvqxwejjpb2mjh3c</sha1>
  2515.     </revision>
  2516.   </page>
  2517.   <page>
  2518.     <title>Module:Message box</title>
  2519.     <ns>828</ns>
  2520.     <id>40574910</id>
  2521.     <revision>
  2522.       <id>1027212344</id>
  2523.       <parentid>970994177</parentid>
  2524.       <timestamp>2021-06-06T18:51:34Z</timestamp>
  2525.       <contributor>
  2526.         <username>Izno</username>
  2527.         <id>2927383</id>
  2528.       </contributor>
  2529.       <comment>per tper</comment>
  2530.       <model>Scribunto</model>
  2531.       <format>text/plain</format>
  2532.       <text bytes="18348" xml:space="preserve">-- This is a meta-module for producing message box templates, including
  2533. -- {{mbox}}, {{ambox}}, {{imbox}}, {{tmbox}}, {{ombox}}, {{cmbox}} and {{fmbox}}.
  2534.  
  2535. -- Load necessary modules.
  2536. require('Module:No globals')
  2537. local getArgs
  2538. local yesno = require('Module:Yesno')
  2539.  
  2540. -- Get a language object for formatDate and ucfirst.
  2541. local lang = mw.language.getContentLanguage()
  2542.  
  2543. -- Define constants
  2544. local CONFIG_MODULE = 'Module:Message box/configuration'
  2545. local DEMOSPACES = {talk = 'tmbox', image = 'imbox', file = 'imbox', category = 'cmbox', article = 'ambox', main = 'ambox'}
  2546.  
  2547. --------------------------------------------------------------------------------
  2548. -- Helper functions
  2549. --------------------------------------------------------------------------------
  2550.  
  2551. local function getTitleObject(...)
  2552.     -- Get the title object, passing the function through pcall
  2553.     -- in case we are over the expensive function count limit.
  2554.     local success, title = pcall(mw.title.new, ...)
  2555.     if success then
  2556.         return title
  2557.     end
  2558. end
  2559.  
  2560. local function union(t1, t2)
  2561.     -- Returns the union of two arrays.
  2562.     local vals = {}
  2563.     for i, v in ipairs(t1) do
  2564.         vals[v] = true
  2565.     end
  2566.     for i, v in ipairs(t2) do
  2567.         vals[v] = true
  2568.     end
  2569.     local ret = {}
  2570.     for k in pairs(vals) do
  2571.         table.insert(ret, k)
  2572.     end
  2573.     table.sort(ret)
  2574.     return ret
  2575. end
  2576.  
  2577. local function getArgNums(args, prefix)
  2578.     local nums = {}
  2579.     for k, v in pairs(args) do
  2580.         local num = mw.ustring.match(tostring(k), '^' .. prefix .. '([1-9]%d*)$')
  2581.         if num then
  2582.             table.insert(nums, tonumber(num))
  2583.         end
  2584.     end
  2585.     table.sort(nums)
  2586.     return nums
  2587. end
  2588.  
  2589. --------------------------------------------------------------------------------
  2590. -- Box class definition
  2591. --------------------------------------------------------------------------------
  2592.  
  2593. local MessageBox = {}
  2594. MessageBox.__index = MessageBox
  2595.  
  2596. function MessageBox.new(boxType, args, cfg)
  2597.     args = args or {}
  2598.     local obj = {}
  2599.  
  2600.     -- Set the title object and the namespace.
  2601.     obj.title = getTitleObject(args.page) or mw.title.getCurrentTitle()
  2602.  
  2603.     -- Set the config for our box type.
  2604.     obj.cfg = cfg[boxType]
  2605.     if not obj.cfg then
  2606.         local ns = obj.title.namespace
  2607.         -- boxType is "mbox" or invalid input
  2608.         if args.demospace and args.demospace ~= '' then
  2609.             -- implement demospace parameter of mbox
  2610.             local demospace = string.lower(args.demospace)
  2611.             if DEMOSPACES[demospace] then
  2612.                 -- use template from DEMOSPACES
  2613.                 obj.cfg = cfg[DEMOSPACES[demospace]]
  2614.             elseif string.find( demospace, 'talk' ) then
  2615.                 -- demo as a talk page
  2616.                 obj.cfg = cfg.tmbox
  2617.             else
  2618.                 -- default to ombox
  2619.                 obj.cfg = cfg.ombox
  2620.             end
  2621.         elseif ns == 0 then
  2622.             obj.cfg = cfg.ambox -- main namespace
  2623.         elseif ns == 6 then
  2624.             obj.cfg = cfg.imbox -- file namespace
  2625.         elseif ns == 14 then
  2626.             obj.cfg = cfg.cmbox -- category namespace
  2627.         else
  2628.             local nsTable = mw.site.namespaces[ns]
  2629.             if nsTable and nsTable.isTalk then
  2630.                 obj.cfg = cfg.tmbox -- any talk namespace
  2631.             else
  2632.                 obj.cfg = cfg.ombox -- other namespaces or invalid input
  2633.             end
  2634.         end
  2635.     end
  2636.  
  2637.     -- Set the arguments, and remove all blank arguments except for the ones
  2638.     -- listed in cfg.allowBlankParams.
  2639.     do
  2640.         local newArgs = {}
  2641.         for k, v in pairs(args) do
  2642.             if v ~= '' then
  2643.                 newArgs[k] = v
  2644.             end
  2645.         end
  2646.         for i, param in ipairs(obj.cfg.allowBlankParams or {}) do
  2647.             newArgs[param] = args[param]
  2648.         end
  2649.         obj.args = newArgs
  2650.     end
  2651.  
  2652.     -- Define internal data structure.
  2653.     obj.categories = {}
  2654.     obj.classes = {}
  2655.     -- For lazy loading of [[Module:Category handler]].
  2656.     obj.hasCategories = false
  2657.  
  2658.     return setmetatable(obj, MessageBox)
  2659. end
  2660.  
  2661. function MessageBox:addCat(ns, cat, sort)
  2662.     if not cat then
  2663.         return nil
  2664.     end
  2665.     if sort then
  2666.         cat = string.format('[[Category:%s|%s]]', cat, sort)
  2667.     else
  2668.         cat = string.format('[[Category:%s]]', cat)
  2669.     end
  2670.     self.hasCategories = true
  2671.     self.categories[ns] = self.categories[ns] or {}
  2672.     table.insert(self.categories[ns], cat)
  2673. end
  2674.  
  2675. function MessageBox:addClass(class)
  2676.     if not class then
  2677.         return nil
  2678.     end
  2679.     table.insert(self.classes, class)
  2680. end
  2681.  
  2682. function MessageBox:setParameters()
  2683.     local args = self.args
  2684.     local cfg = self.cfg
  2685.  
  2686.     -- Get type data.
  2687.     self.type = args.type
  2688.     local typeData = cfg.types[self.type]
  2689.     self.invalidTypeError = cfg.showInvalidTypeError
  2690.         and self.type
  2691.         and not typeData
  2692.     typeData = typeData or cfg.types[cfg.default]
  2693.     self.typeClass = typeData.class
  2694.     self.typeImage = typeData.image
  2695.  
  2696.     -- Find if the box has been wrongly substituted.
  2697.     self.isSubstituted = cfg.substCheck and args.subst == 'SUBST'
  2698.  
  2699.     -- Find whether we are using a small message box.
  2700.     self.isSmall = cfg.allowSmall and (
  2701.         cfg.smallParam and args.small == cfg.smallParam
  2702.         or not cfg.smallParam and yesno(args.small)
  2703.     )
  2704.  
  2705.     -- Add attributes, classes and styles.
  2706.     self.id = args.id
  2707.     self.name = args.name
  2708.     if self.name then
  2709.         self:addClass('box-' .. string.gsub(self.name,' ','_'))
  2710.     end
  2711.     if yesno(args.plainlinks) ~= false then
  2712.         self:addClass('plainlinks')
  2713.     end
  2714.     for _, class in ipairs(cfg.classes or {}) do
  2715.         self:addClass(class)
  2716.     end
  2717.     if self.isSmall then
  2718.         self:addClass(cfg.smallClass or 'mbox-small')
  2719.     end
  2720.     self:addClass(self.typeClass)
  2721.     self:addClass(args.class)
  2722.     self.style = args.style
  2723.     self.attrs = args.attrs
  2724.  
  2725.     -- Set text style.
  2726.     self.textstyle = args.textstyle
  2727.  
  2728.     -- Find if we are on the template page or not. This functionality is only
  2729.     -- used if useCollapsibleTextFields is set, or if both cfg.templateCategory
  2730.     -- and cfg.templateCategoryRequireName are set.
  2731.     self.useCollapsibleTextFields = cfg.useCollapsibleTextFields
  2732.     if self.useCollapsibleTextFields
  2733.         or cfg.templateCategory
  2734.         and cfg.templateCategoryRequireName
  2735.     then
  2736.         if self.name then
  2737.             local templateName = mw.ustring.match(
  2738.                 self.name,
  2739.                 '^[tT][eE][mM][pP][lL][aA][tT][eE][%s_]*:[%s_]*(.*)$'
  2740.             ) or self.name
  2741.             templateName = 'Template:' .. templateName
  2742.             self.templateTitle = getTitleObject(templateName)
  2743.         end
  2744.         self.isTemplatePage = self.templateTitle
  2745.             and mw.title.equals(self.title, self.templateTitle)
  2746.     end
  2747.    
  2748.     -- Process data for collapsible text fields. At the moment these are only
  2749.     -- used in {{ambox}}.
  2750.     if self.useCollapsibleTextFields then
  2751.         -- Get the self.issue value.
  2752.         if self.isSmall and args.smalltext then
  2753.             self.issue = args.smalltext
  2754.         else
  2755.             local sect
  2756.             if args.sect == '' then
  2757.                 sect = 'This ' .. (cfg.sectionDefault or 'page')
  2758.             elseif type(args.sect) == 'string' then
  2759.                 sect = 'This ' .. args.sect
  2760.             end
  2761.             local issue = args.issue
  2762.             issue = type(issue) == 'string' and issue ~= '' and issue or nil
  2763.             local text = args.text
  2764.             text = type(text) == 'string' and text or nil
  2765.             local issues = {}
  2766.             table.insert(issues, sect)
  2767.             table.insert(issues, issue)
  2768.             table.insert(issues, text)
  2769.             self.issue = table.concat(issues, ' ')
  2770.         end
  2771.  
  2772.         -- Get the self.talk value.
  2773.         local talk = args.talk
  2774.         -- Show talk links on the template page or template subpages if the talk
  2775.         -- parameter is blank.
  2776.         if talk == ''
  2777.             and self.templateTitle
  2778.             and (
  2779.                 mw.title.equals(self.templateTitle, self.title)
  2780.                 or self.title:isSubpageOf(self.templateTitle)
  2781.             )
  2782.         then
  2783.             talk = '#'
  2784.         elseif talk == '' then
  2785.             talk = nil
  2786.         end
  2787.         if talk then
  2788.             -- If the talk value is a talk page, make a link to that page. Else
  2789.             -- assume that it's a section heading, and make a link to the talk
  2790.             -- page of the current page with that section heading.
  2791.             local talkTitle = getTitleObject(talk)
  2792.             local talkArgIsTalkPage = true
  2793.             if not talkTitle or not talkTitle.isTalkPage then
  2794.                 talkArgIsTalkPage = false
  2795.                 talkTitle = getTitleObject(
  2796.                     self.title.text,
  2797.                     mw.site.namespaces[self.title.namespace].talk.id
  2798.                 )
  2799.             end
  2800.             if talkTitle and talkTitle.exists then
  2801.                 local talkText
  2802.                 if self.isSmall then
  2803.                     local talkLink = talkArgIsTalkPage and talk or (talkTitle.prefixedText .. '#' .. talk)
  2804.                     talkText = string.format('([[%s|talk]])', talkLink)
  2805.                 else
  2806.                     talkText = 'Relevant discussion may be found on'
  2807.                     if talkArgIsTalkPage then
  2808.                         talkText = string.format(
  2809.                             '%s [[%s|%s]].',
  2810.                             talkText,
  2811.                             talk,
  2812.                             talkTitle.prefixedText
  2813.                         )
  2814.                     else
  2815.                         talkText = string.format(
  2816.                             '%s the [[%s#%s|talk page]].',
  2817.                             talkText,
  2818.                             talkTitle.prefixedText,
  2819.                             talk
  2820.                         )
  2821.                     end
  2822.                 end
  2823.                 self.talk = talkText
  2824.             end
  2825.         end
  2826.  
  2827.         -- Get other values.
  2828.         self.fix = args.fix ~= '' and args.fix or nil
  2829.         local date
  2830.         if args.date and args.date ~= '' then
  2831.             date = args.date
  2832.         elseif args.date == '' and self.isTemplatePage then
  2833.             date = lang:formatDate('F Y')
  2834.         end
  2835.         if date then
  2836.             self.date = string.format(" &lt;span class='date-container'&gt;''(&lt;span class='date'&gt;%s&lt;/span&gt;)''&lt;/span&gt;", date)
  2837.         end
  2838.         self.info = args.info
  2839.         if yesno(args.removalnotice) then
  2840.             self.removalNotice = cfg.removalNotice
  2841.         end
  2842.     end
  2843.  
  2844.     -- Set the non-collapsible text field. At the moment this is used by all box
  2845.     -- types other than ambox, and also by ambox when small=yes.
  2846.     if self.isSmall then
  2847.         self.text = args.smalltext or args.text
  2848.     else
  2849.         self.text = args.text
  2850.     end
  2851.  
  2852.     -- Set the below row.
  2853.     self.below = cfg.below and args.below
  2854.  
  2855.     -- General image settings.
  2856.     self.imageCellDiv = not self.isSmall and cfg.imageCellDiv
  2857.     self.imageEmptyCell = cfg.imageEmptyCell
  2858.     if cfg.imageEmptyCellStyle then
  2859.         self.imageEmptyCellStyle = 'border:none;padding:0;width:1px'
  2860.     end
  2861.  
  2862.     -- Left image settings.
  2863.     local imageLeft = self.isSmall and args.smallimage or args.image
  2864.     if cfg.imageCheckBlank and imageLeft ~= 'blank' and imageLeft ~= 'none'
  2865.         or not cfg.imageCheckBlank and imageLeft ~= 'none'
  2866.     then
  2867.         self.imageLeft = imageLeft
  2868.         if not imageLeft then
  2869.             local imageSize = self.isSmall
  2870.                 and (cfg.imageSmallSize or '30x30px')
  2871.                 or '40x40px'
  2872.             self.imageLeft = string.format('[[File:%s|%s|link=|alt=]]', self.typeImage
  2873.                 or 'Imbox notice.png', imageSize)
  2874.         end
  2875.     end
  2876.  
  2877.     -- Right image settings.
  2878.     local imageRight = self.isSmall and args.smallimageright or args.imageright
  2879.     if not (cfg.imageRightNone and imageRight == 'none') then
  2880.         self.imageRight = imageRight
  2881.     end
  2882. end
  2883.  
  2884. function MessageBox:setMainspaceCategories()
  2885.     local args = self.args
  2886.     local cfg = self.cfg
  2887.  
  2888.     if not cfg.allowMainspaceCategories then
  2889.         return nil
  2890.     end
  2891.  
  2892.     local nums = {}
  2893.     for _, prefix in ipairs{'cat', 'category', 'all'} do
  2894.         args[prefix .. '1'] = args[prefix]
  2895.         nums = union(nums, getArgNums(args, prefix))
  2896.     end
  2897.  
  2898.     -- The following is roughly equivalent to the old {{Ambox/category}}.
  2899.     local date = args.date
  2900.     date = type(date) == 'string' and date
  2901.     local preposition = 'from'
  2902.     for _, num in ipairs(nums) do
  2903.         local mainCat = args['cat' .. tostring(num)]
  2904.             or args['category' .. tostring(num)]
  2905.         local allCat = args['all' .. tostring(num)]
  2906.         mainCat = type(mainCat) == 'string' and mainCat
  2907.         allCat = type(allCat) == 'string' and allCat
  2908.         if mainCat and date and date ~= '' then
  2909.             local catTitle = string.format('%s %s %s', mainCat, preposition, date)
  2910.             self:addCat(0, catTitle)
  2911.             catTitle = getTitleObject('Category:' .. catTitle)
  2912.             if not catTitle or not catTitle.exists then
  2913.                 self:addCat(0, 'Articles with invalid date parameter in template')
  2914.             end
  2915.         elseif mainCat and (not date or date == '') then
  2916.             self:addCat(0, mainCat)
  2917.         end
  2918.         if allCat then
  2919.             self:addCat(0, allCat)
  2920.         end
  2921.     end
  2922. end
  2923.  
  2924. function MessageBox:setTemplateCategories()
  2925.     local args = self.args
  2926.     local cfg = self.cfg
  2927.  
  2928.     -- Add template categories.
  2929.     if cfg.templateCategory then
  2930.         if cfg.templateCategoryRequireName then
  2931.             if self.isTemplatePage then
  2932.                 self:addCat(10, cfg.templateCategory)
  2933.             end
  2934.         elseif not self.title.isSubpage then
  2935.             self:addCat(10, cfg.templateCategory)
  2936.         end
  2937.     end
  2938.  
  2939.     -- Add template error categories.
  2940.     if cfg.templateErrorCategory then
  2941.         local templateErrorCategory = cfg.templateErrorCategory
  2942.         local templateCat, templateSort
  2943.         if not self.name and not self.title.isSubpage then
  2944.             templateCat = templateErrorCategory
  2945.         elseif self.isTemplatePage then
  2946.             local paramsToCheck = cfg.templateErrorParamsToCheck or {}
  2947.             local count = 0
  2948.             for i, param in ipairs(paramsToCheck) do
  2949.                 if not args[param] then
  2950.                     count = count + 1
  2951.                 end
  2952.             end
  2953.             if count &gt; 0 then
  2954.                 templateCat = templateErrorCategory
  2955.                 templateSort = tostring(count)
  2956.             end
  2957.             if self.categoryNums and #self.categoryNums &gt; 0 then
  2958.                 templateCat = templateErrorCategory
  2959.                 templateSort = 'C'
  2960.             end
  2961.         end
  2962.         self:addCat(10, templateCat, templateSort)
  2963.     end
  2964. end
  2965.  
  2966. function MessageBox:setAllNamespaceCategories()
  2967.     -- Set categories for all namespaces.
  2968.     if self.invalidTypeError then
  2969.         local allSort = (self.title.namespace == 0 and 'Main:' or '') .. self.title.prefixedText
  2970.         self:addCat('all', 'Wikipedia message box parameter needs fixing', allSort)
  2971.     end
  2972.     if self.isSubstituted then
  2973.         self:addCat('all', 'Pages with incorrectly substituted templates')
  2974.     end
  2975. end
  2976.  
  2977. function MessageBox:setCategories()
  2978.     if self.title.namespace == 0 then
  2979.         self:setMainspaceCategories()
  2980.     elseif self.title.namespace == 10 then
  2981.         self:setTemplateCategories()
  2982.     end
  2983.     self:setAllNamespaceCategories()
  2984. end
  2985.  
  2986. function MessageBox:renderCategories()
  2987.     if not self.hasCategories then
  2988.         -- No categories added, no need to pass them to Category handler so,
  2989.         -- if it was invoked, it would return the empty string.
  2990.         -- So we shortcut and return the empty string.
  2991.         return ""
  2992.     end
  2993.     -- Convert category tables to strings and pass them through
  2994.     -- [[Module:Category handler]].
  2995.     return require('Module:Category handler')._main{
  2996.         main = table.concat(self.categories[0] or {}),
  2997.         template = table.concat(self.categories[10] or {}),
  2998.         all = table.concat(self.categories.all or {}),
  2999.         nocat = self.args.nocat,
  3000.         page = self.args.page
  3001.     }
  3002. end
  3003.  
  3004. function MessageBox:export()
  3005.     local root = mw.html.create()
  3006.  
  3007.     -- Add the subst check error.
  3008.     if self.isSubstituted and self.name then
  3009.         root:tag('b')
  3010.             :addClass('error')
  3011.             :wikitext(string.format(
  3012.                 'Template &lt;code&gt;%s[[Template:%s|%s]]%s&lt;/code&gt; has been incorrectly substituted.',
  3013.                 mw.text.nowiki('{{'), self.name, self.name, mw.text.nowiki('}}')
  3014.             ))
  3015.     end
  3016.  
  3017.     -- Create the box table.
  3018.     local boxTable = root:tag('table')
  3019.     boxTable:attr('id', self.id or nil)
  3020.     for i, class in ipairs(self.classes or {}) do
  3021.         boxTable:addClass(class or nil)
  3022.     end
  3023.     boxTable
  3024.         :cssText(self.style or nil)
  3025.         :attr('role', 'presentation')
  3026.  
  3027.     if self.attrs then
  3028.         boxTable:attr(self.attrs)
  3029.     end
  3030.  
  3031.     -- Add the left-hand image.
  3032.     local row = boxTable:tag('tr')
  3033.     if self.imageLeft then
  3034.         local imageLeftCell = row:tag('td'):addClass('mbox-image')
  3035.         if self.imageCellDiv then
  3036.             -- If we are using a div, redefine imageLeftCell so that the image
  3037.             -- is inside it. Divs use style="width: 52px;", which limits the
  3038.             -- image width to 52px. If any images in a div are wider than that,
  3039.             -- they may overlap with the text or cause other display problems.
  3040.             imageLeftCell = imageLeftCell:tag('div'):css('width', '52px')
  3041.         end
  3042.         imageLeftCell:wikitext(self.imageLeft or nil)
  3043.     elseif self.imageEmptyCell then
  3044.         -- Some message boxes define an empty cell if no image is specified, and
  3045.         -- some don't. The old template code in templates where empty cells are
  3046.         -- specified gives the following hint: "No image. Cell with some width
  3047.         -- or padding necessary for text cell to have 100% width."
  3048.         row:tag('td')
  3049.             :addClass('mbox-empty-cell')
  3050.             :cssText(self.imageEmptyCellStyle or nil)
  3051.     end
  3052.  
  3053.     -- Add the text.
  3054.     local textCell = row:tag('td'):addClass('mbox-text')
  3055.     if self.useCollapsibleTextFields then
  3056.         -- The message box uses advanced text parameters that allow things to be
  3057.         -- collapsible. At the moment, only ambox uses this.
  3058.         textCell:cssText(self.textstyle or nil)
  3059.         local textCellDiv = textCell:tag('div')
  3060.         textCellDiv
  3061.             :addClass('mbox-text-span')
  3062.             :wikitext(self.issue or nil)
  3063.         if (self.talk or self.fix) then
  3064.             textCellDiv:tag('span')
  3065.                 :addClass('hide-when-compact')
  3066.                 :wikitext(self.talk and (' ' .. self.talk) or nil)
  3067.                 :wikitext(self.fix and (' ' .. self.fix) or nil)
  3068.         end
  3069.         textCellDiv:wikitext(self.date and (' ' .. self.date) or nil)
  3070.         if self.info and not self.isSmall then
  3071.             textCellDiv
  3072.                 :tag('span')
  3073.                 :addClass('hide-when-compact')
  3074.                 :wikitext(self.info and (' ' .. self.info) or nil)
  3075.         end
  3076.         if self.removalNotice then
  3077.             textCellDiv:tag('span')
  3078.                 :addClass('hide-when-compact')
  3079.                 :tag('i')
  3080.                     :wikitext(string.format(" (%s)", self.removalNotice))
  3081.         end
  3082.     else
  3083.         -- Default text formatting - anything goes.
  3084.         textCell
  3085.             :cssText(self.textstyle or nil)
  3086.             :wikitext(self.text or nil)
  3087.     end
  3088.  
  3089.     -- Add the right-hand image.
  3090.     if self.imageRight then
  3091.         local imageRightCell = row:tag('td'):addClass('mbox-imageright')
  3092.         if self.imageCellDiv then
  3093.             -- If we are using a div, redefine imageRightCell so that the image
  3094.             -- is inside it.
  3095.             imageRightCell = imageRightCell:tag('div'):css('width', '52px')
  3096.         end
  3097.         imageRightCell
  3098.             :wikitext(self.imageRight or nil)
  3099.     end
  3100.  
  3101.     -- Add the below row.
  3102.     if self.below then
  3103.         boxTable:tag('tr')
  3104.             :tag('td')
  3105.                 :attr('colspan', self.imageRight and '3' or '2')
  3106.                 :addClass('mbox-text')
  3107.                 :cssText(self.textstyle or nil)
  3108.                 :wikitext(self.below or nil)
  3109.     end
  3110.  
  3111.     -- Add error message for invalid type parameters.
  3112.     if self.invalidTypeError then
  3113.         root:tag('div')
  3114.             :css('text-align', 'center')
  3115.             :wikitext(string.format(
  3116.                 'This message box is using an invalid "type=%s" parameter and needs fixing.',
  3117.                 self.type or ''
  3118.             ))
  3119.     end
  3120.  
  3121.     -- Add categories.
  3122.     root:wikitext(self:renderCategories() or nil)
  3123.  
  3124.     return tostring(root)
  3125. end
  3126.  
  3127. --------------------------------------------------------------------------------
  3128. -- Exports
  3129. --------------------------------------------------------------------------------
  3130.  
  3131. local p, mt = {}, {}
  3132.  
  3133. function p._exportClasses()
  3134.     -- For testing.
  3135.     return {
  3136.         MessageBox = MessageBox
  3137.     }
  3138. end
  3139.  
  3140. function p.main(boxType, args, cfgTables)
  3141.     local box = MessageBox.new(boxType, args, cfgTables or mw.loadData(CONFIG_MODULE))
  3142.     box:setParameters()
  3143.     box:setCategories()
  3144.     return box:export()
  3145. end
  3146.  
  3147. function mt.__index(t, k)
  3148.     return function (frame)
  3149.         if not getArgs then
  3150.             getArgs = require('Module:Arguments').getArgs
  3151.         end
  3152.         return t.main(k, getArgs(frame, {trim = false, removeBlanks = false}))
  3153.     end
  3154. end
  3155.  
  3156. return setmetatable(p, mt)</text>
  3157.       <sha1>kshc0gmfnt1xkzycgdb25urt4m770p1</sha1>
  3158.     </revision>
  3159.   </page>
  3160.   <page>
  3161.     <title>Module:Message box/configuration</title>
  3162.     <ns>828</ns>
  3163.     <id>40627038</id>
  3164.     <revision>
  3165.       <id>948472514</id>
  3166.       <parentid>948472513</parentid>
  3167.       <timestamp>2020-04-01T06:12:44Z</timestamp>
  3168.       <contributor>
  3169.         <username>MusikAnimal</username>
  3170.         <id>14882394</id>
  3171.       </contributor>
  3172.       <minor/>
  3173.       <comment>1 revision imported</comment>
  3174.       <model>Scribunto</model>
  3175.       <format>text/plain</format>
  3176.       <text bytes="6141" xml:space="preserve">--------------------------------------------------------------------------------
  3177. --                          Message box configuration                         --
  3178. --                                                                            --
  3179. -- This module contains configuration data for [[Module:Message box]].        --
  3180. --------------------------------------------------------------------------------
  3181.  
  3182. return {
  3183.     ambox = {
  3184.         types = {
  3185.             speedy = {
  3186.                 class = 'ambox-speedy',
  3187.                 image = 'Ambox warning pn.svg'
  3188.             },
  3189.             delete = {
  3190.                 class = 'ambox-delete',
  3191.                 image = 'Ambox warning pn.svg'
  3192.             },
  3193.             content = {
  3194.                 class = 'ambox-content',
  3195.                 image = 'Ambox important.svg'
  3196.             },
  3197.             style = {
  3198.                 class = 'ambox-style',
  3199.                 image = 'Edit-clear.svg'
  3200.             },
  3201.             move = {
  3202.                 class = 'ambox-move',
  3203.                 image = 'Merge-split-transwiki default.svg'
  3204.             },
  3205.             protection = {
  3206.                 class = 'ambox-protection',
  3207.                 image = 'Semi-protection-shackle-keyhole.svg'
  3208.             },
  3209.             notice = {
  3210.                 class = 'ambox-notice',
  3211.                 image = 'Information icon4.svg'
  3212.             }
  3213.         },
  3214.         default                     = 'notice',
  3215.         allowBlankParams            = {'talk', 'sect', 'date', 'issue', 'fix', 'subst', 'hidden'},
  3216.         allowSmall                  = true,
  3217.         smallParam                  = 'left',
  3218.         smallClass                  = 'mbox-small-left',
  3219.         substCheck                  = true,
  3220.         classes                     = {'metadata', 'ambox'},
  3221.         imageEmptyCell              = true,
  3222.         imageCheckBlank             = true,
  3223.         imageSmallSize              = '20x20px',
  3224.         imageCellDiv                = true,
  3225.         useCollapsibleTextFields    = true,
  3226.         imageRightNone              = true,
  3227.         sectionDefault              = 'article',
  3228.         allowMainspaceCategories    = true,
  3229.         templateCategory            = 'Article message templates',
  3230.             templateCategoryRequireName = true,
  3231.         templateErrorCategory       = 'Article message templates with missing parameters',
  3232.         templateErrorParamsToCheck  = {'issue', 'fix', 'subst'},
  3233.         removalNotice               = '[[Help:Maintenance template removal|Learn how and when to remove this template message]]'
  3234.     },
  3235.    
  3236.     cmbox = {
  3237.         types = {
  3238.             speedy = {
  3239.                 class = 'cmbox-speedy',
  3240.                 image = 'Ambox warning pn.svg'
  3241.             },
  3242.             delete = {
  3243.                 class = 'cmbox-delete',
  3244.                 image = 'Ambox warning pn.svg'
  3245.             },
  3246.             content = {
  3247.                 class = 'cmbox-content',
  3248.                 image = 'Ambox important.svg'
  3249.             },
  3250.             style = {
  3251.                 class = 'cmbox-style',
  3252.                 image = 'Edit-clear.svg'
  3253.             },
  3254.             move = {
  3255.                 class = 'cmbox-move',
  3256.                 image = 'Merge-split-transwiki default.svg'
  3257.             },
  3258.             protection = {
  3259.                 class = 'cmbox-protection',
  3260.                 image = 'Semi-protection-shackle-keyhole.svg'
  3261.             },
  3262.             notice = {
  3263.                 class = 'cmbox-notice',
  3264.                 image = 'Information icon4.svg'
  3265.             }
  3266.         },
  3267.         default              = 'notice',
  3268.         showInvalidTypeError = true,
  3269.         classes              = {'cmbox'},
  3270.         imageEmptyCell       = true
  3271.     },
  3272.    
  3273.     fmbox = {
  3274.         types = {
  3275.             warning = {
  3276.                 class = 'fmbox-warning',
  3277.                 image = 'Ambox warning pn.svg'
  3278.             },
  3279.             editnotice = {
  3280.                 class = 'fmbox-editnotice',
  3281.                 image = 'Information icon4.svg'
  3282.             },
  3283.             system = {
  3284.                 class = 'fmbox-system',
  3285.                 image = 'Information icon4.svg'
  3286.             }
  3287.         },
  3288.         default              = 'system',
  3289.         showInvalidTypeError = true,
  3290.         classes              = {'fmbox'},
  3291.         imageEmptyCell       = false,
  3292.         imageRightNone       = false
  3293.     },
  3294.    
  3295.     imbox = {
  3296.         types = {
  3297.             speedy = {
  3298.                 class = 'imbox-speedy',
  3299.                 image = 'Ambox warning pn.svg'
  3300.             },
  3301.             delete = {
  3302.                 class = 'imbox-delete',
  3303.                 image = 'Ambox warning pn.svg'
  3304.             },
  3305.             content = {
  3306.                 class = 'imbox-content',
  3307.                 image = 'Ambox important.svg'
  3308.             },
  3309.             style = {
  3310.                 class = 'imbox-style',
  3311.                 image = 'Edit-clear.svg'
  3312.             },
  3313.             move = {
  3314.                 class = 'imbox-move',
  3315.                 image = 'Merge-split-transwiki default.svg'
  3316.             },
  3317.             protection = {
  3318.                 class = 'imbox-protection',
  3319.                 image = 'Semi-protection-shackle-keyhole.svg'
  3320.             },
  3321.             license = {
  3322.                 class = 'imbox-license licensetpl',
  3323.                 image = 'Imbox license.png' -- @todo We need an SVG version of this
  3324.             },
  3325.             featured = {
  3326.                 class = 'imbox-featured',
  3327.                 image = 'Cscr-featured.svg'
  3328.             },
  3329.             notice = {
  3330.                 class = 'imbox-notice',
  3331.                 image = 'Information icon4.svg'
  3332.             }
  3333.         },
  3334.         default              = 'notice',
  3335.         showInvalidTypeError = true,
  3336.         classes              = {'imbox'},
  3337.         imageEmptyCell       = true,
  3338.         below                = true,
  3339.         templateCategory     = 'File message boxes'
  3340.     },
  3341.    
  3342.     ombox = {
  3343.         types = {
  3344.             speedy = {
  3345.                 class = 'ombox-speedy',
  3346.                 image = 'Ambox warning pn.svg'
  3347.             },
  3348.             delete = {
  3349.                 class = 'ombox-delete',
  3350.                 image = 'Ambox warning pn.svg'
  3351.             },
  3352.             content = {
  3353.                 class = 'ombox-content',
  3354.                 image = 'Ambox important.svg'
  3355.             },
  3356.             style = {
  3357.                 class = 'ombox-style',
  3358.                 image = 'Edit-clear.svg'
  3359.             },
  3360.             move = {
  3361.                 class = 'ombox-move',
  3362.                 image = 'Merge-split-transwiki default.svg'
  3363.             },
  3364.             protection = {
  3365.                 class = 'ombox-protection',
  3366.                 image = 'Semi-protection-shackle-keyhole.svg'
  3367.             },
  3368.             notice = {
  3369.                 class = 'ombox-notice',
  3370.                 image = 'Information icon4.svg'
  3371.             }
  3372.         },
  3373.         default              = 'notice',
  3374.         showInvalidTypeError = true,
  3375.         classes              = {'ombox'},
  3376.         allowSmall           = true,
  3377.         imageEmptyCell       = true,
  3378.         imageRightNone       = true
  3379.     },
  3380.    
  3381.     tmbox = {
  3382.         types = {
  3383.             speedy = {
  3384.                 class = 'tmbox-speedy',
  3385.                 image = 'Ambox warning pn.svg'
  3386.             },
  3387.             delete = {
  3388.                 class = 'tmbox-delete',
  3389.                 image = 'Ambox warning pn.svg'
  3390.             },
  3391.             content = {
  3392.                 class = 'tmbox-content',
  3393.                 image = 'Ambox important.svg'
  3394.             },
  3395.             style = {
  3396.                 class = 'tmbox-style',
  3397.                 image = 'Edit-clear.svg'
  3398.             },
  3399.             move = {
  3400.                 class = 'tmbox-move',
  3401.                 image = 'Merge-split-transwiki default.svg'
  3402.             },
  3403.             protection = {
  3404.                 class = 'tmbox-protection',
  3405.                 image = 'Semi-protection-shackle-keyhole.svg'
  3406.             },
  3407.             notice = {
  3408.                 class = 'tmbox-notice',
  3409.                 image = 'Information icon4.svg'
  3410.             }
  3411.         },
  3412.         default              = 'notice',
  3413.         showInvalidTypeError = true,
  3414.         classes              = {'tmbox'},
  3415.         allowSmall           = true,
  3416.         imageRightNone       = true,
  3417.         imageEmptyCell       = true,
  3418.         imageEmptyCellStyle  = true,
  3419.         templateCategory     = 'Talk message boxes'
  3420.     }
  3421. }</text>
  3422.       <sha1>rz63o9789otb966y0k5pcntqf0bl6te</sha1>
  3423.     </revision>
  3424.   </page>
  3425.   <page>
  3426.     <title>Module:Navbar</title>
  3427.     <ns>828</ns>
  3428.     <id>38827227</id>
  3429.     <revision>
  3430.       <id>1054994468</id>
  3431.       <parentid>1054938041</parentid>
  3432.       <timestamp>2021-11-13T05:33:43Z</timestamp>
  3433.       <contributor>
  3434.         <username>Izno</username>
  3435.         <id>2927383</id>
  3436.       </contributor>
  3437.       <comment>remove unnecessary line</comment>
  3438.       <model>Scribunto</model>
  3439.       <format>text/plain</format>
  3440.       <text bytes="5088" xml:space="preserve">local p = {}
  3441. local cfg = mw.loadData('Module:Navbar/configuration')
  3442.  
  3443. local function get_title_arg(is_collapsible, template)
  3444.     local title_arg = 1
  3445.     if is_collapsible then title_arg = 2 end
  3446.     if template then title_arg = 'template' end
  3447.     return title_arg
  3448. end
  3449.  
  3450. local function choose_links(template, args)
  3451.     -- The show table indicates the default displayed items.
  3452.     -- view, talk, edit, hist, move, watch
  3453.     -- TODO: Move to configuration.
  3454.     local show = {true, true, true, false, false, false}
  3455.     if template then
  3456.         show[2] = false
  3457.         show[3] = false
  3458.         local index = {t = 2, d = 2, e = 3, h = 4, m = 5, w = 6,
  3459.             talk = 2, edit = 3, hist = 4, move = 5, watch = 6}
  3460.         -- TODO: Consider removing TableTools dependency.
  3461.         for _, v in ipairs(require ('Module:TableTools').compressSparseArray(args)) do
  3462.             local num = index[v]
  3463.             if num then show[num] = true end
  3464.         end
  3465.     end
  3466.  
  3467.     local remove_edit_link = args.noedit
  3468.     if remove_edit_link then show[3] = false end
  3469.    
  3470.     return show
  3471.    
  3472. end
  3473.  
  3474. local function add_link(link_description, ul, is_mini, font_style)
  3475.     local l
  3476.     if link_description.url then
  3477.         l = {'[', '', ']'}
  3478.     else
  3479.         l = {'[[', '|', ']]'}
  3480.     end
  3481.     ul:tag('li')
  3482.         :addClass('nv-' .. link_description.full)
  3483.         :wikitext(l[1] .. link_description.link .. l[2])
  3484.         :tag(is_mini and 'abbr' or 'span')
  3485.             :attr('title', link_description.html_title)
  3486.             :cssText(font_style)
  3487.             :wikitext(is_mini and link_description.mini or link_description.full)
  3488.             :done()
  3489.         :wikitext(l[3])
  3490.         :done()
  3491. end
  3492.  
  3493. local function make_list(title_text, has_brackets, displayed_links, is_mini, font_style)
  3494.    
  3495.     local title = mw.title.new(mw.text.trim(title_text), cfg.title_namespace)
  3496.     if not title then
  3497.         error(cfg.invalid_title .. title_text)
  3498.     end
  3499.     local talkpage = title.talkPageTitle and title.talkPageTitle.fullText or ''
  3500.    
  3501.     -- TODO: Get link_descriptions and show into the configuration module.
  3502.     -- link_descriptions should be easier...
  3503.     local link_descriptions = {
  3504.         { ['mini'] = 'v', ['full'] = 'view', ['html_title'] = 'View this template',
  3505.             ['link'] = title.fullText, ['url'] = false },
  3506.         { ['mini'] = 't', ['full'] = 'talk', ['html_title'] = 'Discuss this template',
  3507.             ['link'] = talkpage, ['url'] = false },
  3508.         { ['mini'] = 'e', ['full'] = 'edit', ['html_title'] = 'Edit this template',
  3509.             ['link'] = title:fullUrl('action=edit'), ['url'] = true },
  3510.         { ['mini'] = 'h', ['full'] = 'hist', ['html_title'] = 'History of this template',
  3511.             ['link'] = title:fullUrl('action=history'), ['url'] = true },
  3512.         { ['mini'] = 'm', ['full'] = 'move', ['html_title'] = 'Move this template',
  3513.             ['link'] = mw.title.new('Special:Movepage'):fullUrl('target='..title.fullText), ['url'] = true },
  3514.         { ['mini'] = 'w', ['full'] = 'watch', ['html_title'] = 'Watch this template',
  3515.             ['link'] = title:fullUrl('action=watch'), ['url'] = true }
  3516.     }
  3517.  
  3518.     local ul = mw.html.create('ul')
  3519.     if has_brackets then
  3520.         ul:addClass(cfg.classes.brackets)
  3521.             :cssText(font_style)
  3522.     end
  3523.    
  3524.     for i, _ in ipairs(displayed_links) do
  3525.         if displayed_links[i] then add_link(link_descriptions[i], ul, is_mini, font_style) end
  3526.     end
  3527.     return ul:done()
  3528.    
  3529. end
  3530.  
  3531. function p._navbar(args)
  3532.    
  3533.     -- TODO: We probably don't need both fontstyle and fontcolor...
  3534.     local font_style = args.fontstyle
  3535.     local font_color = args.fontcolor
  3536.     local is_collapsible = args.collapsible
  3537.     local is_mini = args.mini
  3538.     local is_plain = args.plain
  3539.    
  3540.     local collapsible_class = nil
  3541.     if is_collapsible then
  3542.         collapsible_class = cfg.classes.collapsible
  3543.         if not is_plain then is_mini = 1 end
  3544.         if font_color then
  3545.             font_style = (font_style or '') .. '; color: ' .. font_color .. ';'
  3546.         end
  3547.     end
  3548.    
  3549.     local navbar_style = args.style
  3550.     local div = mw.html.create():tag('div')
  3551.     div
  3552.         :addClass(cfg.classes.navbar)
  3553.         :addClass(cfg.classes.plainlinks)
  3554.         :addClass(cfg.classes.horizontal_list)
  3555.         :addClass(collapsible_class) -- we made the determination earlier
  3556.         :cssText(navbar_style)
  3557.  
  3558.     if is_mini then div:addClass(cfg.classes.mini) end
  3559.  
  3560.     local box_text = (args.text or cfg.box_text) .. ' '
  3561.      -- the concatenated space guarantees the box text is separated
  3562.     if not (is_mini or is_plain) then
  3563.         div
  3564.             :tag('span')
  3565.                 :addClass(cfg.classes.box_text)
  3566.                 :cssText(font_style)
  3567.                 :wikitext(box_text)
  3568.     end
  3569.    
  3570.     local template = args.template
  3571.     local displayed_links = choose_links(template, args)
  3572.     local has_brackets = args.brackets
  3573.     local title_arg = get_title_arg(is_collapsible, template)
  3574.     local title_text = args[title_arg] or (':' .. mw.getCurrentFrame():getParent():getTitle())
  3575.     local list = make_list(title_text, has_brackets, displayed_links, is_mini, font_style)
  3576.     div:node(list)
  3577.  
  3578.     if is_collapsible then
  3579.         local title_text_class
  3580.         if is_mini then
  3581.             title_text_class = cfg.classes.collapsible_title_mini
  3582.         else
  3583.             title_text_class = cfg.classes.collapsible_title_full
  3584.         end
  3585.         div:done()
  3586.             :tag('div')
  3587.             :addClass(title_text_class)
  3588.             :cssText(font_style)
  3589.             :wikitext(args[1])
  3590.     end
  3591.    
  3592.     return mw.getCurrentFrame():extensionTag{
  3593.         name = 'templatestyles', args = { src = cfg.templatestyles }
  3594.     } .. tostring(div:done())
  3595. end
  3596.  
  3597. function p.navbar(frame)
  3598.     return p._navbar(require('Module:Arguments').getArgs(frame))
  3599. end
  3600.  
  3601. return p</text>
  3602.       <sha1>jd5no8v1zi4m1e6lvbwo0a9t0kogdvi</sha1>
  3603.     </revision>
  3604.   </page>
  3605.   <page>
  3606.     <title>Module:Navbar/configuration</title>
  3607.     <ns>828</ns>
  3608.     <id>66010105</id>
  3609.     <revision>
  3610.       <id>1054995103</id>
  3611.       <parentid>992950717</parentid>
  3612.       <timestamp>2021-11-13T05:39:10Z</timestamp>
  3613.       <contributor>
  3614.         <username>Izno</username>
  3615.         <id>2927383</id>
  3616.       </contributor>
  3617.       <comment>well, since I edited the other one...</comment>
  3618.       <model>Scribunto</model>
  3619.       <format>text/plain</format>
  3620.       <text bytes="867" xml:space="preserve">return {
  3621.     ['templatestyles'] = 'Module:Navbar/styles.css',
  3622.     ['box_text'] = 'This box: ',            -- default text box when not plain or mini
  3623.     ['title_namespace'] = 'Template',       -- namespace to default to for title
  3624.     ['invalid_title'] = 'Invalid title ',
  3625.     ['classes'] = { -- set a line to nil if you don't want it
  3626.         ['navbar'] = 'navbar',
  3627.         ['plainlinks'] = 'plainlinks', -- plainlinks
  3628.         ['horizontal_list'] = 'hlist', -- horizontal list class
  3629.         ['mini'] = 'navbar-mini', -- class indicating small links in the navbar
  3630.         ['this_box'] = 'navbar-boxtext',
  3631.         ['brackets'] = 'navbar-brackets',
  3632.         -- 'collapsible' is the key for a class to indicate the navbar is
  3633.         -- setting up the collapsible element in addition to the normal
  3634.         -- navbar.
  3635.         ['collapsible'] = 'navbar-collapse',
  3636.         ['collapsible_title_mini'] = 'navbar-ct-mini',
  3637.         ['collapsible_title_full'] = 'navbar-ct-full'
  3638.     }
  3639. }</text>
  3640.       <sha1>lydm72m86hfvyzj0u2lqfps9lz9gquk</sha1>
  3641.     </revision>
  3642.   </page>
  3643.   <page>
  3644.     <title>Module:Navbox</title>
  3645.     <ns>828</ns>
  3646.     <id>38634746</id>
  3647.     <revision>
  3648.       <id>1064341473</id>
  3649.       <parentid>1057332100</parentid>
  3650.       <timestamp>2022-01-07T21:39:23Z</timestamp>
  3651.       <contributor>
  3652.         <username>Izno</username>
  3653.         <id>2927383</id>
  3654.       </contributor>
  3655.       <comment>remove titlegroup per templatestyles section on talk page</comment>
  3656.       <model>Scribunto</model>
  3657.       <format>text/plain</format>
  3658.       <text bytes="16064" xml:space="preserve">local p = {}
  3659. local navbar = require('Module:Navbar')._navbar
  3660. local cfg = mw.loadData('Module:Navbox/configuration')
  3661. local getArgs -- lazily initialized
  3662. local args
  3663. local format = string.format
  3664.  
  3665. local function striped(wikitext, border)
  3666.     -- Return wikitext with markers replaced for odd/even striping.
  3667.     -- Child (subgroup) navboxes are flagged with a category that is removed
  3668.     -- by parent navboxes. The result is that the category shows all pages
  3669.     -- where a child navbox is not contained in a parent navbox.
  3670.     local orphanCat = cfg.category.orphan
  3671.     if border == cfg.keyword.border_subgroup and args[cfg.arg.orphan] ~= cfg.keyword.orphan_yes then
  3672.         -- No change; striping occurs in outermost navbox.
  3673.         return wikitext .. orphanCat
  3674.     end
  3675.     local first, second = cfg.class.navbox_odd_part, cfg.class.navbox_even_part
  3676.     if args[cfg.arg.evenodd] then
  3677.         if args[cfg.arg.evenodd] == cfg.keyword.evenodd_swap then
  3678.             first, second = second, first
  3679.         else
  3680.             first = args[cfg.arg.evenodd]
  3681.             second = first
  3682.         end
  3683.     end
  3684.     local changer
  3685.     if first == second then
  3686.         changer = first
  3687.     else
  3688.         local index = 0
  3689.         changer = function (code)
  3690.             if code == '0' then
  3691.                 -- Current occurrence is for a group before a nested table.
  3692.                 -- Set it to first as a valid although pointless class.
  3693.                 -- The next occurrence will be the first row after a title
  3694.                 -- in a subgroup and will also be first.
  3695.                 index = 0
  3696.                 return first
  3697.             end
  3698.             index = index + 1
  3699.             return index % 2 == 1 and first or second
  3700.         end
  3701.     end
  3702.     local regex = orphanCat:gsub('([%[%]])', '%%%1')
  3703.     return (wikitext:gsub(regex, ''):gsub(cfg.marker.regex, changer)) -- () omits gsub count
  3704. end
  3705.  
  3706. local function processItem(item, nowrapitems)
  3707.     if item:sub(1, 2) == '{|' then
  3708.         -- Applying nowrap to lines in a table does not make sense.
  3709.         -- Add newlines to compensate for trim of x in |parm=x in a template.
  3710.         return '\n' .. item ..'\n'
  3711.     end
  3712.     if nowrapitems == cfg.keyword.nowrapitems_yes then
  3713.         local lines = {}
  3714.         for line in (item .. '\n'):gmatch('([^\n]*)\n') do
  3715.             local prefix, content = line:match('^([*:;#]+)%s*(.*)')
  3716.             if prefix and not content:match(cfg.pattern.nowrap) then
  3717.                 line = format(cfg.nowrap_item, prefix, content)
  3718.             end
  3719.             table.insert(lines, line)
  3720.         end
  3721.         item = table.concat(lines, '\n')
  3722.     end
  3723.     if item:match('^[*:;#]') then
  3724.         return '\n' .. item ..'\n'
  3725.     end
  3726.     return item
  3727. end
  3728.  
  3729. -- we will want this later when we want to add tstyles for hlist/plainlist
  3730. local function has_navbar()
  3731.     return args[cfg.arg.navbar] ~= cfg.keyword.navbar_off
  3732.         and args[cfg.arg.navbar] ~= cfg.keyword.navbar_plain
  3733.         and (
  3734.             args[cfg.arg.name]
  3735.             or mw.getCurrentFrame():getParent():getTitle():gsub(cfg.pattern.sandbox, '')
  3736.                 ~= cfg.pattern.navbox
  3737.         )
  3738. end
  3739.  
  3740. local function renderNavBar(titleCell)
  3741.     if has_navbar() then
  3742.         titleCell:wikitext(navbar{
  3743.             [cfg.navbar.name] = args[cfg.arg.name],
  3744.             [cfg.navbar.mini] = 1,
  3745.             [cfg.navbar.fontstyle] = (args[cfg.arg.basestyle] or '') .. ';' ..
  3746.                 (args[cfg.arg.titlestyle] or '') ..
  3747.                 ';background:none transparent;border:none;box-shadow:none;padding:0;'
  3748.         })
  3749.     end
  3750.  
  3751. end
  3752.  
  3753. local function renderTitleRow(tbl)
  3754.     if not args[cfg.arg.title] then return end
  3755.  
  3756.     local titleRow = tbl:tag('tr')
  3757.  
  3758.     local titleCell = titleRow:tag('th'):attr('scope', 'col')
  3759.  
  3760.     local titleColspan = 2
  3761.     if args[cfg.arg.imageleft] then titleColspan = titleColspan + 1 end
  3762.     if args[cfg.arg.image] then titleColspan = titleColspan + 1 end
  3763.  
  3764.     titleCell
  3765.         :cssText(args[cfg.arg.basestyle])
  3766.         :cssText(args[cfg.arg.titlestyle])
  3767.         :addClass(cfg.class.navbox_title)
  3768.         :attr('colspan', titleColspan)
  3769.  
  3770.     renderNavBar(titleCell)
  3771.  
  3772.     titleCell
  3773.         :tag('div')
  3774.             -- id for aria-labelledby attribute
  3775.             :attr('id', mw.uri.anchorEncode(args[cfg.arg.title]))
  3776.             :addClass(args[cfg.arg.titleclass])
  3777.             :css('font-size', '114%')
  3778.             :css('margin', '0 4em')
  3779.             :wikitext(processItem(args[cfg.arg.title]))
  3780. end
  3781.  
  3782. local function getAboveBelowColspan()
  3783.     local ret = 2
  3784.     if args[cfg.arg.imageleft] then ret = ret + 1 end
  3785.     if args[cfg.arg.image] then ret = ret + 1 end
  3786.     return ret
  3787. end
  3788.  
  3789. local function renderAboveRow(tbl)
  3790.     if not args[cfg.arg.above] then return end
  3791.  
  3792.     tbl:tag('tr')
  3793.         :tag('td')
  3794.             :addClass(cfg.class.navbox_abovebelow)
  3795.             :addClass(args[cfg.arg.aboveclass])
  3796.             :cssText(args[cfg.arg.basestyle])
  3797.             :cssText(args[cfg.arg.abovestyle])
  3798.             :attr('colspan', getAboveBelowColspan())
  3799.             :tag('div')
  3800.                 -- id for aria-labelledby attribute, if no title
  3801.                 :attr('id', args[cfg.arg.title] and nil or mw.uri.anchorEncode(args[cfg.arg.above]))
  3802.                 :wikitext(processItem(args[cfg.arg.above], args[cfg.arg.nowrapitems]))
  3803. end
  3804.  
  3805. local function renderBelowRow(tbl)
  3806.     if not args[cfg.arg.below] then return end
  3807.  
  3808.     tbl:tag('tr')
  3809.         :tag('td')
  3810.             :addClass(cfg.class.navbox_abovebelow)
  3811.             :addClass(args[cfg.arg.belowclass])
  3812.             :cssText(args[cfg.arg.basestyle])
  3813.             :cssText(args[cfg.arg.belowstyle])
  3814.             :attr('colspan', getAboveBelowColspan())
  3815.             :tag('div')
  3816.                 :wikitext(processItem(args[cfg.arg.below], args[cfg.arg.nowrapitems]))
  3817. end
  3818.  
  3819. local function renderListRow(tbl, index, listnum, listnums_size)
  3820.     local row = tbl:tag('tr')
  3821.  
  3822.     if index == 1 and args[cfg.arg.imageleft] then
  3823.         row
  3824.             :tag('td')
  3825.                 :addClass(cfg.class.noviewer)
  3826.                 :addClass(cfg.class.navbox_image)
  3827.                 :addClass(args[cfg.arg.imageclass])
  3828.                 :css('width', '1px')               -- Minimize width
  3829.                 :css('padding', '0 2px 0 0')
  3830.                 :cssText(args[cfg.arg.imageleftstyle])
  3831.                 :attr('rowspan', listnums_size)
  3832.                 :tag('div')
  3833.                     :wikitext(processItem(args[cfg.arg.imageleft]))
  3834.     end
  3835.  
  3836.     local group_and_num = format(cfg.arg.group_and_num, listnum)
  3837.     local groupstyle_and_num = format(cfg.arg.groupstyle_and_num, listnum)
  3838.     if args[group_and_num] then
  3839.         local groupCell = row:tag('th')
  3840.  
  3841.         -- id for aria-labelledby attribute, if lone group with no title or above
  3842.         if listnum == 1 and not (args[cfg.arg.title] or args[cfg.arg.above] or args[cfg.arg.group2]) then
  3843.             groupCell
  3844.                 :attr('id', mw.uri.anchorEncode(args[cfg.arg.group1]))
  3845.         end
  3846.  
  3847.         groupCell
  3848.             :attr('scope', 'row')
  3849.             :addClass(cfg.class.navbox_group)
  3850.             :addClass(args[cfg.arg.groupclass])
  3851.             :cssText(args[cfg.arg.basestyle])
  3852.             -- If groupwidth not specified, minimize width
  3853.             :css('width', args[cfg.arg.groupwidth] or '1%')
  3854.  
  3855.         groupCell
  3856.             :cssText(args[cfg.arg.groupstyle])
  3857.             :cssText(args[groupstyle_and_num])
  3858.             :wikitext(args[group_and_num])
  3859.     end
  3860.  
  3861.     local listCell = row:tag('td')
  3862.  
  3863.     if args[group_and_num] then
  3864.         listCell
  3865.             :addClass(cfg.class.navbox_list_with_group)
  3866.     else
  3867.         listCell:attr('colspan', 2)
  3868.     end
  3869.  
  3870.     if not args[cfg.arg.groupwidth] then
  3871.         listCell:css('width', '100%')
  3872.     end
  3873.  
  3874.     local rowstyle  -- usually nil so cssText(rowstyle) usually adds nothing
  3875.     if index % 2 == 1 then
  3876.         rowstyle = args[cfg.arg.oddstyle]
  3877.     else
  3878.         rowstyle = args[cfg.arg.evenstyle]
  3879.     end
  3880.  
  3881.     local list_and_num = format(cfg.arg.list_and_num, listnum)
  3882.     local listText = args[list_and_num]
  3883.     local oddEven = cfg.marker.oddeven
  3884.     if listText:sub(1, 12) == '&lt;/div&gt;&lt;table' then
  3885.         -- Assume list text is for a subgroup navbox so no automatic striping for this row.
  3886.         oddEven = listText:find(cfg.pattern.navbox_title) and cfg.marker.restart or cfg.class.navbox_odd_part
  3887.     end
  3888.  
  3889.     local liststyle_and_num = format(cfg.arg.liststyle_and_num, listnum)
  3890.     local listclass_and_num = format(cfg.arg.listclass_and_num, listnum)
  3891.     listCell
  3892.         :css('padding', '0')
  3893.         :cssText(args[cfg.arg.liststyle])
  3894.         :cssText(rowstyle)
  3895.         :cssText(args[liststyle_and_num])
  3896.         :addClass(cfg.class.navbox_list)
  3897.         :addClass(cfg.class.navbox_part .. oddEven)
  3898.         :addClass(args[cfg.arg.listclass])
  3899.         :addClass(args[listclass_and_num])
  3900.         :tag('div')
  3901.             :css('padding',
  3902.                 (index == 1 and args[cfg.arg.list1padding]) or args[cfg.arg.listpadding] or '0 0.25em'
  3903.             )
  3904.             :wikitext(processItem(listText, args[cfg.arg.nowrapitems]))
  3905.  
  3906.     if index == 1 and args[cfg.arg.image] then
  3907.         row
  3908.             :tag('td')
  3909.                 :addClass(cfg.class.noviewer)
  3910.                 :addClass(cfg.class.navbox_image)
  3911.                 :addClass(args[cfg.arg.imageclass])
  3912.                 :css('width', '1px')               -- Minimize width
  3913.                 :css('padding', '0 0 0 2px')
  3914.                 :cssText(args[cfg.arg.imagestyle])
  3915.                 :attr('rowspan', listnums_size)
  3916.                 :tag('div')
  3917.                     :wikitext(processItem(args[cfg.arg.image]))
  3918.     end
  3919. end
  3920.  
  3921. -- uses this now to make the needHlistCategory correct
  3922. -- to use later for when we add list styles via navbox
  3923. local function has_list_class(htmlclass)
  3924.     local class_args = { -- rough order of probability of use
  3925.         cfg.arg.bodyclass, cfg.arg.listclass, cfg.arg.aboveclass,
  3926.         cfg.arg.belowclass, cfg.arg.titleclass, cfg.arg.navboxclass,
  3927.         cfg.arg.groupclass, cfg.arg.imageclass
  3928.     }
  3929.     local patterns = {
  3930.         '^' .. htmlclass .. '$',
  3931.         '%s' .. htmlclass .. '$',
  3932.         '^' .. htmlclass .. '%s',
  3933.         '%s' .. htmlclass .. '%s'
  3934.     }
  3935.    
  3936.     for _, arg in ipairs(class_args) do
  3937.         for _, pattern in ipairs(patterns) do
  3938.             if mw.ustring.find(args[arg] or '', pattern) then
  3939.                 return true
  3940.             end
  3941.         end
  3942.     end
  3943.     return false
  3944. end
  3945.  
  3946. local function needsHorizontalLists(border)
  3947.     if border == cfg.keyword.border_subgroup or args[cfg.arg.tracking] == cfg.keyword.tracking_no then
  3948.         return false
  3949.     end
  3950.     return not has_list_class(cfg.pattern.hlist) and not has_list_class(cfg.pattern.plainlist)
  3951. end
  3952.  
  3953. local function hasBackgroundColors()
  3954.     for _, key in ipairs({cfg.arg.titlestyle, cfg.arg.groupstyle,
  3955.         cfg.arg.basestyle, cfg.arg.abovestyle, cfg.arg.belowstyle}) do
  3956.         if tostring(args[key]):find('background', 1, true) then
  3957.             return true
  3958.         end
  3959.     end
  3960.     return false
  3961. end
  3962.  
  3963. local function hasBorders()
  3964.     for _, key in ipairs({cfg.arg.groupstyle, cfg.arg.basestyle,
  3965.         cfg.arg.abovestyle, cfg.arg.belowstyle}) do
  3966.         if tostring(args[key]):find('border', 1, true) then
  3967.             return true
  3968.         end
  3969.     end
  3970.     return false
  3971. end
  3972.  
  3973. local function isIllegible()
  3974.     local styleratio = require('Module:Color contrast')._styleratio
  3975.     for key, style in pairs(args) do
  3976.         if tostring(key):match(cfg.pattern.style) then
  3977.             if styleratio{mw.text.unstripNoWiki(style)} &lt; 4.5 then
  3978.                 return true
  3979.             end
  3980.         end
  3981.     end
  3982.     return false
  3983. end
  3984.  
  3985. local function getTrackingCategories(border)
  3986.     local cats = {}
  3987.     if needsHorizontalLists(border) then table.insert(cats, cfg.category.horizontal_lists) end
  3988.     if hasBackgroundColors() then table.insert(cats, cfg.category.background_colors) end
  3989.     if isIllegible() then table.insert(cats, cfg.category.illegible) end
  3990.     if hasBorders() then table.insert(cats, cfg.category.borders) end
  3991.     return cats
  3992. end
  3993.  
  3994. local function renderTrackingCategories(builder, border)
  3995.     local title = mw.title.getCurrentTitle()
  3996.     if title.namespace ~= 10 then return end -- not in template space
  3997.     local subpage = title.subpageText
  3998.     if subpage == cfg.keyword.subpage_doc or subpage == cfg.keyword.subpage_sandbox
  3999.         or subpage == cfg.keyword.subpage_testcases then return end
  4000.  
  4001.     for _, cat in ipairs(getTrackingCategories(border)) do
  4002.         builder:wikitext('[[Category:' .. cat .. ']]')
  4003.     end
  4004. end
  4005.  
  4006. local function renderMainTable(border, listnums)
  4007.     local tbl = mw.html.create('table')
  4008.         :addClass(cfg.class.nowraplinks)
  4009.         :addClass(args[cfg.arg.bodyclass])
  4010.  
  4011.     local state = args[cfg.arg.state]
  4012.     if args[cfg.arg.title] and state ~= cfg.keyword.state_plain and state ~= cfg.keyword.state_off then
  4013.         if state == cfg.keyword.state_collapsed then
  4014.             state = cfg.class.collapsed
  4015.         end
  4016.         tbl
  4017.             :addClass(cfg.class.collapsible)
  4018.             :addClass(state or cfg.class.autocollapse)
  4019.     end
  4020.  
  4021.     tbl:css('border-spacing', 0)
  4022.     if border == cfg.keyword.border_subgroup or border == cfg.keyword.border_none then
  4023.         tbl
  4024.             :addClass(cfg.class.navbox_subgroup)
  4025.             :cssText(args[cfg.arg.bodystyle])
  4026.             :cssText(args[cfg.arg.style])
  4027.     else  -- regular navbox - bodystyle and style will be applied to the wrapper table
  4028.         tbl
  4029.             :addClass(cfg.class.navbox_inner)
  4030.             :css('background', 'transparent')
  4031.             :css('color', 'inherit')
  4032.     end
  4033.     tbl:cssText(args[cfg.arg.innerstyle])
  4034.  
  4035.     renderTitleRow(tbl)
  4036.     renderAboveRow(tbl)
  4037.     local listnums_size = #listnums
  4038.     for i, listnum in ipairs(listnums) do
  4039.         renderListRow(tbl, i, listnum, listnums_size)
  4040.     end
  4041.     renderBelowRow(tbl)
  4042.  
  4043.     return tbl
  4044. end
  4045.  
  4046. local function add_navbox_styles()
  4047.     local frame = mw.getCurrentFrame()
  4048.     -- This is a lambda so that it doesn't need the frame as a parameter
  4049.     local function add_user_styles(templatestyles)
  4050.         if templatestyles and templatestyles ~= '' then
  4051.             return frame:extensionTag{
  4052.                 name = 'templatestyles', args = { src = templatestyles }
  4053.             }
  4054.         end
  4055.         return ''
  4056.     end
  4057.  
  4058.     -- get templatestyles. load base from config so that Lua only needs to do
  4059.     -- the work once of parser tag expansion
  4060.     local base_templatestyles = cfg.templatestyles
  4061.     local templatestyles = add_user_styles(args[cfg.arg.templatestyles])
  4062.     local child_templatestyles = add_user_styles(args[cfg.arg.child_templatestyles])
  4063.  
  4064.     -- The 'navbox-styles' div exists for two reasons:
  4065.     --  1. To wrap the styles to work around T200206 more elegantly. Instead
  4066.     --     of combinatorial rules, this ends up being linear number of CSS rules.
  4067.     --  2. To allow MobileFrontend to rip the styles out with 'nomobile' such that
  4068.     --     they are not dumped into the mobile view.
  4069.     return mw.html.create('div')
  4070.         :addClass(cfg.class.navbox_styles)
  4071.         :addClass(cfg.class.nomobile)
  4072.         :wikitext(base_templatestyles .. templatestyles .. child_templatestyles)
  4073.         :done()
  4074. end
  4075.  
  4076. function p._navbox(navboxArgs)
  4077.     args = navboxArgs
  4078.     local listnums = {}
  4079.  
  4080.     for k, _ in pairs(args) do
  4081.         if type(k) == 'string' then
  4082.             local listnum = k:match(cfg.pattern.listnum)
  4083.             if listnum then table.insert(listnums, tonumber(listnum)) end
  4084.         end
  4085.     end
  4086.     table.sort(listnums)
  4087.  
  4088.     local border = mw.text.trim(args[cfg.arg.border] or args[1] or '')
  4089.     if border == cfg.keyword.border_child then
  4090.         border = cfg.keyword.border_subgroup
  4091.     end
  4092.  
  4093.     -- render the main body of the navbox
  4094.     local tbl = renderMainTable(border, listnums)
  4095.  
  4096.     local res = mw.html.create()
  4097.     -- render the appropriate wrapper for the navbox, based on the border param
  4098.  
  4099.     if border == cfg.keyword.border_none then
  4100.         res:node(add_navbox_styles())
  4101.         local nav = res:tag('div')
  4102.             :attr('role', 'navigation')
  4103.             :node(tbl)
  4104.         -- aria-labelledby title, otherwise above, otherwise lone group
  4105.         if args[cfg.arg.title] or args[cfg.arg.above] or (args[cfg.arg.group1]
  4106.             and not args[cfg.arg.group2]) then
  4107.             nav:attr(
  4108.                 'aria-labelledby',
  4109.                 mw.uri.anchorEncode(
  4110.                     args[cfg.arg.title] or args[cfg.arg.above] or args[cfg.arg.group1]
  4111.                 )
  4112.             )
  4113.         else
  4114.             nav:attr('aria-label', cfg.aria_label)
  4115.         end
  4116.     elseif border == cfg.keyword.border_subgroup then
  4117.         -- We assume that this navbox is being rendered in a list cell of a
  4118.         -- parent navbox, and is therefore inside a div with padding:0em 0.25em.
  4119.         -- We start with a &lt;/div&gt; to avoid the padding being applied, and at the
  4120.         -- end add a &lt;div&gt; to balance out the parent's &lt;/div&gt;
  4121.         res
  4122.             :wikitext('&lt;/div&gt;')
  4123.             :node(tbl)
  4124.             :wikitext('&lt;div&gt;')
  4125.     else
  4126.         res:node(add_navbox_styles())
  4127.         local nav = res:tag('div')
  4128.             :attr('role', 'navigation')
  4129.             :addClass(cfg.class.navbox)
  4130.             :addClass(args[cfg.arg.navboxclass])
  4131.             :cssText(args[cfg.arg.bodystyle])
  4132.             :cssText(args[cfg.arg.style])
  4133.             :css('padding', '3px')
  4134.             :node(tbl)
  4135.         -- aria-labelledby title, otherwise above, otherwise lone group
  4136.         if args[cfg.arg.title] or args[cfg.arg.above]
  4137.             or (args[cfg.arg.group1] and not args[cfg.arg.group2]) then
  4138.             nav:attr(
  4139.                 'aria-labelledby',
  4140.                 mw.uri.anchorEncode(args[cfg.arg.title] or args[cfg.arg.above] or args[cfg.arg.group1])
  4141.             )
  4142.         else
  4143.             nav:attr('aria-label', cfg.aria_label)
  4144.         end
  4145.     end
  4146.  
  4147.     if (args[cfg.arg.nocat] or cfg.keyword.nocat_false):lower() == cfg.keyword.nocat_false then
  4148.         renderTrackingCategories(res, border)
  4149.     end
  4150.     return striped(tostring(res), border)
  4151. end
  4152.  
  4153. function p.navbox(frame)
  4154.     if not getArgs then
  4155.         getArgs = require('Module:Arguments').getArgs
  4156.     end
  4157.     args = getArgs(frame, {wrappers = {cfg.pattern.navbox}})
  4158.  
  4159.     -- Read the arguments in the order they'll be output in, to make references
  4160.     -- number in the right order.
  4161.     local _
  4162.     _ = args[cfg.arg.title]
  4163.     _ = args[cfg.arg.above]
  4164.     -- Limit this to 20 as covering 'most' cases (that's a SWAG) and because
  4165.     -- iterator approach won't work here
  4166.     for i = 1, 20 do
  4167.         _ = args[format(cfg.arg.group_and_num, i)]
  4168.         _ = args[format(cfg.arg.list_and_num, i)]
  4169.     end
  4170.     _ = args[cfg.arg.below]
  4171.  
  4172.     return p._navbox(args)
  4173. end
  4174.  
  4175. return p</text>
  4176.       <sha1>aps8opqyleqagctjqmi5zb3x6sq8mux</sha1>
  4177.     </revision>
  4178.   </page>
  4179.   <page>
  4180.     <title>Module:Navbox/configuration</title>
  4181.     <ns>828</ns>
  4182.     <id>67685668</id>
  4183.     <revision>
  4184.       <id>1064341544</id>
  4185.       <parentid>1057330154</parentid>
  4186.       <timestamp>2022-01-07T21:39:52Z</timestamp>
  4187.       <contributor>
  4188.         <username>Izno</username>
  4189.         <id>2927383</id>
  4190.       </contributor>
  4191.       <comment>remove titlegroup</comment>
  4192.       <model>Scribunto</model>
  4193.       <format>text/plain</format>
  4194.       <text bytes="3597" xml:space="preserve">return {
  4195.     aria_label = 'Navbox',
  4196.     nowrap_item = '%s&lt;span class="nowrap"&gt;%s&lt;/span&gt;',
  4197.     templatestyles = mw.getCurrentFrame():extensionTag{
  4198.         name = 'templatestyles', args = { src = 'Module:Navbox/styles.css' }
  4199.     },
  4200.     -- do not localize marker table
  4201.     marker = {
  4202.         oddeven = '\127_ODDEVEN_\127',
  4203.         restart = '\127_ODDEVEN0_\127',
  4204.         regex = '\127_ODDEVEN(%d?)_\127'
  4205.     },
  4206.     category = {
  4207.         orphan = '[[Category:Navbox orphans]]',
  4208.         horizontal_lists = 'Navigational boxes without horizontal lists',
  4209.         background_colors = 'Navboxes using background colours',
  4210.         illegible = 'Potentially illegible navboxes',
  4211.         borders = 'Navboxes using borders',
  4212.     },
  4213.     keyword = {
  4214.         border_subgroup = 'subgroup',
  4215.         border_child = 'child',
  4216.         border_none = 'none',
  4217.         evenodd_swap = 'swap',
  4218.         navbar_off = 'off',
  4219.         navbar_plain = 'plain',
  4220.         nocat_false = 'false',
  4221.         nowrapitems_yes = 'yes',
  4222.         orphan_yes = 'yes',
  4223.         state_collapsed = 'collapsed',
  4224.         state_off = 'off',
  4225.         state_plain = 'plain',
  4226.         subpage_doc = 'doc',
  4227.         subpage_sandbox = 'sandbox',
  4228.         subpage_testcases = 'testcases',
  4229.         tracking_no = 'no'
  4230.     },
  4231.     class = {
  4232.         autocollapse = 'autocollapse',
  4233.         collapsible = 'mw-collapsible',
  4234.         collapsed = 'mw-collapsed',
  4235.         -- Warning
  4236.         navbox = 'navbox', -- WMF currently hides 'navbox' from mobile,
  4237.         -- so you probably shouldn't change the navbox class.
  4238.         navbox_abovebelow = 'navbox-abovebelow',
  4239.         navbox_group = 'navbox-group',
  4240.         navbox_image = 'navbox-image',
  4241.         navbox_inner = 'navbox-inner',
  4242.         navbox_list = 'navbox-list',
  4243.         navbox_list_with_group = 'navbox-list-with-group',
  4244.         navbox_part = 'navbox-', -- do not l10n
  4245.         navbox_styles = 'navbox-styles',
  4246.         navbox_subgroup = 'navbox-subgroup',
  4247.         navbox_title = 'navbox-title', -- l10n only if you change pattern.navbox_title below
  4248.         navbox_odd_part = 'odd', -- do not l10n
  4249.         navbox_even_part = 'even', -- do not l10n
  4250.         nomobile = 'nomobile',
  4251.         nowraplinks = 'nowraplinks',
  4252.         noviewer = 'noviewer' -- used to remove images from MediaViewer
  4253.     },
  4254.     pattern = {
  4255.         listnum = '^list(%d+)$',
  4256.         sandbox = '/sandbox$',
  4257.         navbox = 'Template:Navbox',
  4258.         nowrap = '^&lt;span class="nowrap"&gt;',
  4259.         style = 'style$',
  4260.         navbox_title = '&lt;th[^&gt;]*"navbox%-title"',
  4261.         hlist = 'hlist',
  4262.         plainlist = 'plainlist'
  4263.     },
  4264.     arg = {
  4265.         above = 'above',
  4266.         aboveclass = 'aboveclass',
  4267.         abovestyle = 'abovestyle',
  4268.         basestyle = 'basestyle',
  4269.         bodyclass = 'bodyclass',
  4270.         bodystyle = 'bodystyle',
  4271.         border = 'border',
  4272.         below = 'below',
  4273.         belowclass = 'belowclass',
  4274.         belowstyle = 'belowstyle',
  4275.         evenodd = 'evenodd',
  4276.         evenstyle = 'evenstyle',
  4277.         group1 = 'group1',
  4278.         group2 = 'group2',
  4279.         group_and_num = 'group%d',
  4280.         groupstyle_and_num = 'group%dstyle',
  4281.         groupclass = 'groupclass',
  4282.         groupstyle = 'groupstyle',
  4283.         groupwidth = 'groupwidth',
  4284.         innerstyle = 'innerstyle',
  4285.         image = 'image',
  4286.         imageclass = 'imageclass',
  4287.         imageleft = 'imageleft',
  4288.         imageleftstyle = 'imageleftstyle',
  4289.         imagesetyle = 'imagestyle',
  4290.         list_and_num = 'list%d',
  4291.         listclass_and_num = 'list%dclass',
  4292.         liststyle_and_num = 'list%dstyle',
  4293.         list1padding = 'list1padding',
  4294.         listclass = 'listclass',
  4295.         listpadding = 'listpadding',
  4296.         liststyle = 'liststyle',
  4297.         name = 'name',
  4298.         navbar = 'navbar',
  4299.         navboxclass = 'navboxclass',
  4300.         nocat = 'nocat',
  4301.         nowrapitems = 'nowrapitems',
  4302.         oddstyle = 'oddstyle',
  4303.         orphan = 'orphan',
  4304.         state = 'state',
  4305.         style = 'style',
  4306.         templatestyles = 'templatestyles',
  4307.         child_templatestyles = 'child templatestyles',
  4308.         title = 'title',
  4309.         titleclass = 'titleclass',
  4310.         titlestyle = 'titlestyle',
  4311.         tracking = 'tracking'
  4312.     },
  4313.     -- names of navbar arguments
  4314.     navbar = {
  4315.         name = 1,
  4316.         fontstyle = 'fontstyle',
  4317.         mini = 'mini'
  4318.     }
  4319. }</text>
  4320.       <sha1>8npf15277sgmgep5j2bnmes7i7ktuvo</sha1>
  4321.     </revision>
  4322.   </page>
  4323.   <page>
  4324.     <title>Module:Navbox/styles.css</title>
  4325.     <ns>828</ns>
  4326.     <id>69272159</id>
  4327.     <revision>
  4328.       <id>1061467846</id>
  4329.       <parentid>1057682214</parentid>
  4330.       <timestamp>2021-12-21T22:10:10Z</timestamp>
  4331.       <contributor>
  4332.         <username>Izno</username>
  4333.         <id>2927383</id>
  4334.       </contributor>
  4335.       <comment>remove qualifications on th - this will remove styling from "hand-crafted" navboxes, but there's no other elegant way to deal with it. see talk page</comment>
  4336.       <model>sanitized-css</model>
  4337.       <format>text/css</format>
  4338.       <text bytes="2183" xml:space="preserve">/* {{pp|small=y}} */
  4339. .navbox {
  4340.     box-sizing: border-box;
  4341.     border: 1px solid #a2a9b1;
  4342.     width: 100%;
  4343.     clear: both;
  4344.     font-size: 88%;
  4345.     text-align: center;
  4346.     padding: 1px;
  4347.     margin: 1em auto 0; /* Prevent preceding content from clinging to navboxes */
  4348. }
  4349.  
  4350. .navbox .navbox {
  4351.     margin-top: 0; /* No top margin for nested navboxes */
  4352. }
  4353.  
  4354. .navbox + .navbox, /* TODO: remove first line after transclusions have updated */
  4355. .navbox + .navbox-styles + .navbox {
  4356.     margin-top: -1px; /* Single pixel border between adjacent navboxes */
  4357. }
  4358.  
  4359. .navbox-inner,
  4360. .navbox-subgroup {
  4361.     width: 100%;
  4362. }
  4363.  
  4364. .navbox-group,
  4365. .navbox-title,
  4366. .navbox-abovebelow {
  4367.     padding: 0.25em 1em;
  4368.     line-height: 1.5em;
  4369.     text-align: center;
  4370. }
  4371.  
  4372. .navbox-group {
  4373.     white-space: nowrap;
  4374.     /* @noflip */
  4375.     text-align: right;
  4376. }
  4377.  
  4378. .navbox,
  4379. .navbox-subgroup {
  4380.     background-color: #fdfdfd;
  4381. }
  4382.  
  4383. .navbox-list {
  4384.     line-height: 1.5em;
  4385.     border-color: #fdfdfd; /* Must match background color */
  4386. }
  4387.  
  4388. .navbox-list-with-group {
  4389.     text-align: left;
  4390.     border-left-width: 2px;
  4391.     border-left-style: solid;
  4392. }
  4393.  
  4394. /* cell spacing for navbox cells */
  4395. /* Borders above 2nd, 3rd, etc. rows */
  4396. /* TODO: figure out how to replace tr as structure;
  4397.  * with div structure it should be just a matter of first-child */
  4398. tr + tr &gt; .navbox-abovebelow,
  4399. tr + tr &gt; .navbox-group,
  4400. tr + tr &gt; .navbox-image,
  4401. tr + tr &gt; .navbox-list {
  4402.     border-top: 2px solid #fdfdfd; /* Must match background color */
  4403. }
  4404.  
  4405. .navbox-title {
  4406.     background-color: #ccf; /* Level 1 color */
  4407. }
  4408.  
  4409. .navbox-abovebelow,
  4410. .navbox-group,
  4411. .navbox-subgroup .navbox-title {
  4412.     background-color: #ddf; /* Level 2 color */
  4413. }
  4414.  
  4415. .navbox-subgroup .navbox-group,
  4416. .navbox-subgroup .navbox-abovebelow {
  4417.     background-color: #e6e6ff; /* Level 3 color */
  4418. }
  4419.  
  4420. .navbox-even {
  4421.     background-color: #f7f7f7;
  4422. }
  4423.  
  4424. .navbox-odd {
  4425.     background-color: transparent;
  4426. }
  4427.  
  4428. /* TODO: figure out how to remove reliance on td as structure */
  4429. .navbox .hlist td dl,
  4430. .navbox .hlist td ol,
  4431. .navbox .hlist td ul,
  4432. .navbox td.hlist dl,
  4433. .navbox td.hlist ol,
  4434. .navbox td.hlist ul {
  4435.     padding: 0.125em 0;
  4436. }
  4437.  
  4438. .navbox .navbar {
  4439.     display: block;
  4440.     font-size: 100%;
  4441. }
  4442.  
  4443. .navbox-title .navbar {
  4444.     /* @noflip */
  4445.     float: left;
  4446.     /* @noflip */
  4447.     text-align: left;
  4448.     /* @noflip */
  4449.     margin-right: 0.5em;
  4450. }</text>
  4451.       <sha1>r3sdo030s2y37osfqh0rur7i26x9epy</sha1>
  4452.     </revision>
  4453.   </page>
  4454.   <page>
  4455.     <title>Module:Navboxes/doc</title>
  4456.     <ns>828</ns>
  4457.     <id>63365392</id>
  4458.     <revision>
  4459.       <id>1061627648</id>
  4460.       <parentid>945403484</parentid>
  4461.       <timestamp>2021-12-22T21:38:23Z</timestamp>
  4462.       <contributor>
  4463.         <username>GKFX</username>
  4464.         <id>14918984</id>
  4465.       </contributor>
  4466.       <comment>Mark high-use</comment>
  4467.       <model>wikitext</model>
  4468.       <format>text/x-wiki</format>
  4469.       <text bytes="48" xml:space="preserve">{{high-use}}
  4470. Implements most of {{tl|Navboxes}}.</text>
  4471.       <sha1>lzmsnzuow65l1zfmr9bkiuvidrg9z4s</sha1>
  4472.     </revision>
  4473.   </page>
  4474.   <page>
  4475.     <title>Module:No globals</title>
  4476.     <ns>828</ns>
  4477.     <id>42567026</id>
  4478.     <revision>
  4479.       <id>948473889</id>
  4480.       <parentid>948472526</parentid>
  4481.       <timestamp>2020-04-01T06:28:55Z</timestamp>
  4482.       <contributor>
  4483.         <username>MusikAnimal</username>
  4484.         <id>14882394</id>
  4485.       </contributor>
  4486.       <comment>Undid revision 948472525 by [[Special:Contributions/w&gt;DiBabelYurikBot|w&gt;DiBabelYurikBot]] ([[User talk:w&gt;DiBabelYurikBot|talk]])</comment>
  4487.       <model>Scribunto</model>
  4488.       <format>text/plain</format>
  4489.       <text bytes="307" xml:space="preserve">local mt = getmetatable(_G) or {}
  4490. function mt.__index (t, k)
  4491.     if k ~= 'arg' then
  4492.         error('Tried to read nil global ' .. tostring(k), 2)
  4493.     end
  4494.     return nil
  4495. end
  4496. function mt.__newindex(t, k, v)
  4497.     if k ~= 'arg' then
  4498.         error('Tried to write global ' .. tostring(k), 2)
  4499.     end
  4500.     rawset(t, k, v)
  4501. end
  4502. setmetatable(_G, mt)</text>
  4503.       <sha1>gggsv54pq7f94l3up48hr91qtxnskdm</sha1>
  4504.     </revision>
  4505.   </page>
  4506.   <page>
  4507.     <title>Module:Protection banner</title>
  4508.     <ns>828</ns>
  4509.     <id>42040984</id>
  4510.     <revision>
  4511.       <id>1033879006</id>
  4512.       <parentid>1013845490</parentid>
  4513.       <timestamp>2021-07-16T11:59:29Z</timestamp>
  4514.       <contributor>
  4515.         <username>ProcrastinatingReader</username>
  4516.         <id>32939139</id>
  4517.       </contributor>
  4518.       <comment>add catonly param which hides both the banner and padlock if set to yes. all testcases pass. tested in sandbox.</comment>
  4519.       <model>Scribunto</model>
  4520.       <format>text/plain</format>
  4521.       <text bytes="26029" xml:space="preserve">-- This module implements {{pp-meta}} and its daughter templates such as
  4522. -- {{pp-dispute}}, {{pp-vandalism}} and {{pp-sock}}.
  4523.  
  4524. -- Initialise necessary modules.
  4525. require('Module:No globals')
  4526. local makeFileLink = require('Module:File link')._main
  4527. local effectiveProtectionLevel = require('Module:Effective protection level')._main
  4528. local effectiveProtectionExpiry = require('Module:Effective protection expiry')._main
  4529. local yesno = require('Module:Yesno')
  4530.  
  4531. -- Lazily initialise modules and objects we don't always need.
  4532. local getArgs, makeMessageBox, lang
  4533.  
  4534. -- Set constants.
  4535. local CONFIG_MODULE = 'Module:Protection banner/config'
  4536.  
  4537. --------------------------------------------------------------------------------
  4538. -- Helper functions
  4539. --------------------------------------------------------------------------------
  4540.  
  4541. local function makeCategoryLink(cat, sort)
  4542.     if cat then
  4543.         return string.format(
  4544.             '[[%s:%s|%s]]',
  4545.             mw.site.namespaces[14].name,
  4546.             cat,
  4547.             sort
  4548.         )
  4549.     end
  4550. end
  4551.  
  4552. -- Validation function for the expiry and the protection date
  4553. local function validateDate(dateString, dateType)
  4554.     if not lang then
  4555.         lang = mw.language.getContentLanguage()
  4556.     end
  4557.     local success, result = pcall(lang.formatDate, lang, 'U', dateString)
  4558.     if success then
  4559.         result = tonumber(result)
  4560.         if result then
  4561.             return result
  4562.         end
  4563.     end
  4564.     error(string.format(
  4565.         'invalid %s: %s',
  4566.         dateType,
  4567.         tostring(dateString)
  4568.     ), 4)
  4569. end
  4570.  
  4571. local function makeFullUrl(page, query, display)
  4572.     return string.format(
  4573.         '[%s %s]',
  4574.         tostring(mw.uri.fullUrl(page, query)),
  4575.         display
  4576.     )
  4577. end
  4578.  
  4579. -- Given a directed graph formatted as node -&gt; table of direct successors,
  4580. -- get a table of all nodes reachable from a given node (though always
  4581. -- including the given node).
  4582. local function getReachableNodes(graph, start)
  4583.     local toWalk, retval = {[start] = true}, {}
  4584.     while true do
  4585.         -- Can't use pairs() since we're adding and removing things as we're iterating
  4586.         local k = next(toWalk) -- This always gets the "first" key
  4587.         if k == nil then
  4588.             return retval
  4589.         end
  4590.         toWalk[k] = nil
  4591.         retval[k] = true
  4592.         for _,v in ipairs(graph[k]) do
  4593.             if not retval[v] then
  4594.                 toWalk[v] = true
  4595.             end
  4596.         end
  4597.     end
  4598. end
  4599.  
  4600. --------------------------------------------------------------------------------
  4601. -- Protection class
  4602. --------------------------------------------------------------------------------
  4603.  
  4604. local Protection = {}
  4605. Protection.__index = Protection
  4606.  
  4607. Protection.supportedActions = {
  4608.     edit = true,
  4609.     move = true,
  4610.     autoreview = true,
  4611.     upload = true
  4612. }
  4613.  
  4614. Protection.bannerConfigFields = {
  4615.     'text',
  4616.     'explanation',
  4617.     'tooltip',
  4618.     'alt',
  4619.     'link',
  4620.     'image'
  4621. }
  4622.  
  4623. function Protection.new(args, cfg, title)
  4624.     local obj = {}
  4625.     obj._cfg = cfg
  4626.     obj.title = title or mw.title.getCurrentTitle()
  4627.  
  4628.     -- Set action
  4629.     if not args.action then
  4630.         obj.action = 'edit'
  4631.     elseif Protection.supportedActions[args.action] then
  4632.         obj.action = args.action
  4633.     else
  4634.         error(string.format(
  4635.             'invalid action: %s',
  4636.             tostring(args.action)
  4637.         ), 3)
  4638.     end
  4639.  
  4640.     -- Set level
  4641.     obj.level = args.demolevel or effectiveProtectionLevel(obj.action, obj.title)
  4642.     if not obj.level or (obj.action == 'move' and obj.level == 'autoconfirmed') then
  4643.         -- Users need to be autoconfirmed to move pages anyway, so treat
  4644.         -- semi-move-protected pages as unprotected.
  4645.         obj.level = '*'
  4646.     end
  4647.  
  4648.     -- Set expiry
  4649.     local effectiveExpiry = effectiveProtectionExpiry(obj.action, obj.title)
  4650.     if effectiveExpiry == 'infinity' then
  4651.         obj.expiry = 'indef'
  4652.     elseif effectiveExpiry ~= 'unknown' then
  4653.         obj.expiry = validateDate(effectiveExpiry, 'expiry date')
  4654.     end
  4655.  
  4656.     -- Set reason
  4657.     if args[1] then
  4658.         obj.reason = mw.ustring.lower(args[1])
  4659.         if obj.reason:find('|') then
  4660.             error('reasons cannot contain the pipe character ("|")', 3)
  4661.         end
  4662.     end
  4663.  
  4664.     -- Set protection date
  4665.     if args.date then
  4666.         obj.protectionDate = validateDate(args.date, 'protection date')
  4667.     end
  4668.    
  4669.     -- Set banner config
  4670.     do
  4671.         obj.bannerConfig = {}
  4672.         local configTables = {}
  4673.         if cfg.banners[obj.action] then
  4674.             configTables[#configTables + 1] = cfg.banners[obj.action][obj.reason]
  4675.         end
  4676.         if cfg.defaultBanners[obj.action] then
  4677.             configTables[#configTables + 1] = cfg.defaultBanners[obj.action][obj.level]
  4678.             configTables[#configTables + 1] = cfg.defaultBanners[obj.action].default
  4679.         end
  4680.         configTables[#configTables + 1] = cfg.masterBanner
  4681.         for i, field in ipairs(Protection.bannerConfigFields) do
  4682.             for j, t in ipairs(configTables) do
  4683.                 if t[field] then
  4684.                     obj.bannerConfig[field] = t[field]
  4685.                     break
  4686.                 end
  4687.             end
  4688.         end
  4689.     end
  4690.     return setmetatable(obj, Protection)
  4691. end
  4692.  
  4693. function Protection:isUserScript()
  4694.     -- Whether the page is a user JavaScript or CSS page.
  4695.     local title = self.title
  4696.     return title.namespace == 2 and (
  4697.         title.contentModel == 'javascript' or title.contentModel == 'css'
  4698.     )
  4699. end
  4700.  
  4701. function Protection:isProtected()
  4702.     return self.level ~= '*'
  4703. end
  4704.  
  4705. function Protection:shouldShowLock()
  4706.     -- Whether we should output a banner/padlock
  4707.     return self:isProtected() and not self:isUserScript()
  4708. end
  4709.  
  4710. -- Whether this page needs a protection category.
  4711. Protection.shouldHaveProtectionCategory = Protection.shouldShowLock
  4712.  
  4713. function Protection:isTemporary()
  4714.     return type(self.expiry) == 'number'
  4715. end
  4716.  
  4717. function Protection:makeProtectionCategory()
  4718.     if not self:shouldHaveProtectionCategory() then
  4719.         return ''
  4720.     end
  4721.  
  4722.     local cfg = self._cfg
  4723.     local title = self.title
  4724.    
  4725.     -- Get the expiry key fragment.
  4726.     local expiryFragment
  4727.     if self.expiry == 'indef' then
  4728.         expiryFragment = self.expiry
  4729.     elseif type(self.expiry) == 'number' then
  4730.         expiryFragment = 'temp'
  4731.     end
  4732.  
  4733.     -- Get the namespace key fragment.
  4734.     local namespaceFragment = cfg.categoryNamespaceKeys[title.namespace]
  4735.     if not namespaceFragment and title.namespace % 2 == 1 then
  4736.             namespaceFragment = 'talk'
  4737.     end
  4738.  
  4739.     -- Define the order that key fragments are tested in. This is done with an
  4740.     -- array of tables containing the value to be tested, along with its
  4741.     -- position in the cfg.protectionCategories table.
  4742.     local order = {
  4743.         {val = expiryFragment,    keypos = 1},
  4744.         {val = namespaceFragment, keypos = 2},
  4745.         {val = self.reason,       keypos = 3},
  4746.         {val = self.level,        keypos = 4},
  4747.         {val = self.action,       keypos = 5}
  4748.     }
  4749.  
  4750.     --[[
  4751.     -- The old protection templates used an ad-hoc protection category system,
  4752.     -- with some templates prioritising namespaces in their categories, and
  4753.     -- others prioritising the protection reason. To emulate this in this module
  4754.     -- we use the config table cfg.reasonsWithNamespacePriority to set the
  4755.     -- reasons for which namespaces have priority over protection reason.
  4756.     -- If we are dealing with one of those reasons, move the namespace table to
  4757.     -- the end of the order table, i.e. give it highest priority. If not, the
  4758.     -- reason should have highest priority, so move that to the end of the table
  4759.     -- instead.
  4760.     --]]
  4761.     table.insert(order, table.remove(order, self.reason and cfg.reasonsWithNamespacePriority[self.reason] and 2 or 3))
  4762.  
  4763.     --[[
  4764.     -- Define the attempt order. Inactive subtables (subtables with nil "value"
  4765.     -- fields) are moved to the end, where they will later be given the key
  4766.     -- "all". This is to cut down on the number of table lookups in
  4767.     -- cfg.protectionCategories, which grows exponentially with the number of
  4768.     -- non-nil keys. We keep track of the number of active subtables with the
  4769.     -- noActive parameter.
  4770.     --]]
  4771.     local noActive, attemptOrder
  4772.     do
  4773.         local active, inactive = {}, {}
  4774.         for i, t in ipairs(order) do
  4775.             if t.val then
  4776.                 active[#active + 1] = t
  4777.             else
  4778.                 inactive[#inactive + 1] = t
  4779.             end
  4780.         end
  4781.         noActive = #active
  4782.         attemptOrder = active
  4783.         for i, t in ipairs(inactive) do
  4784.             attemptOrder[#attemptOrder + 1] = t
  4785.         end
  4786.     end
  4787.  
  4788.     --[[
  4789.     -- Check increasingly generic key combinations until we find a match. If a
  4790.     -- specific category exists for the combination of key fragments we are
  4791.     -- given, that match will be found first. If not, we keep trying different
  4792.     -- key fragment combinations until we match using the key
  4793.     -- "all-all-all-all-all".
  4794.     --
  4795.     -- To generate the keys, we index the key subtables using a binary matrix
  4796.     -- with indexes i and j. j is only calculated up to the number of active
  4797.     -- subtables. For example, if there were three active subtables, the matrix
  4798.     -- would look like this, with 0 corresponding to the key fragment "all", and
  4799.     -- 1 corresponding to other key fragments.
  4800.     --
  4801.     --   j 1  2  3
  4802.     -- i  
  4803.     -- 1   1  1  1
  4804.     -- 2   0  1  1
  4805.     -- 3   1  0  1
  4806.     -- 4   0  0  1
  4807.     -- 5   1  1  0
  4808.     -- 6   0  1  0
  4809.     -- 7   1  0  0
  4810.     -- 8   0  0  0
  4811.     --
  4812.     -- Values of j higher than the number of active subtables are set
  4813.     -- to the string "all".
  4814.     --
  4815.     -- A key for cfg.protectionCategories is constructed for each value of i.
  4816.     -- The position of the value in the key is determined by the keypos field in
  4817.     -- each subtable.
  4818.     --]]
  4819.     local cats = cfg.protectionCategories
  4820.     for i = 1, 2^noActive do
  4821.         local key = {}
  4822.         for j, t in ipairs(attemptOrder) do
  4823.             if j &gt; noActive then
  4824.                 key[t.keypos] = 'all'
  4825.             else
  4826.                 local quotient = i / 2 ^ (j - 1)
  4827.                 quotient = math.ceil(quotient)
  4828.                 if quotient % 2 == 1 then
  4829.                     key[t.keypos] = t.val
  4830.                 else
  4831.                     key[t.keypos] = 'all'
  4832.                 end
  4833.             end
  4834.         end
  4835.         key = table.concat(key, '|')
  4836.         local attempt = cats[key]
  4837.         if attempt then
  4838.             return makeCategoryLink(attempt, title.text)
  4839.         end
  4840.     end
  4841.     return ''
  4842. end
  4843.  
  4844. function Protection:isIncorrect()
  4845.     local expiry = self.expiry
  4846.     return not self:shouldHaveProtectionCategory()
  4847.         or type(expiry) == 'number' and expiry &lt; os.time()
  4848. end
  4849.  
  4850. function Protection:isTemplateProtectedNonTemplate()
  4851.     local action, namespace = self.action, self.title.namespace
  4852.     return self.level == 'templateeditor'
  4853.         and (
  4854.             (action ~= 'edit' and action ~= 'move')
  4855.             or (namespace ~= 10 and namespace ~= 828)
  4856.         )
  4857. end
  4858.  
  4859. function Protection:makeCategoryLinks()
  4860.     local msg = self._cfg.msg
  4861.     local ret = {self:makeProtectionCategory()}
  4862.     if self:isIncorrect() then
  4863.         ret[#ret + 1] = makeCategoryLink(
  4864.             msg['tracking-category-incorrect'],
  4865.             self.title.text
  4866.         )
  4867.     end
  4868.     if self:isTemplateProtectedNonTemplate() then
  4869.         ret[#ret + 1] = makeCategoryLink(
  4870.             msg['tracking-category-template'],
  4871.             self.title.text
  4872.         )
  4873.     end
  4874.     return table.concat(ret)
  4875. end
  4876.  
  4877. --------------------------------------------------------------------------------
  4878. -- Blurb class
  4879. --------------------------------------------------------------------------------
  4880.  
  4881. local Blurb = {}
  4882. Blurb.__index = Blurb
  4883.  
  4884. Blurb.bannerTextFields = {
  4885.     text = true,
  4886.     explanation = true,
  4887.     tooltip = true,
  4888.     alt = true,
  4889.     link = true
  4890. }
  4891.  
  4892. function Blurb.new(protectionObj, args, cfg)
  4893.     return setmetatable({
  4894.         _cfg = cfg,
  4895.         _protectionObj = protectionObj,
  4896.         _args = args
  4897.     }, Blurb)
  4898. end
  4899.  
  4900. -- Private methods --
  4901.  
  4902. function Blurb:_formatDate(num)
  4903.     -- Formats a Unix timestamp into dd Month, YYYY format.
  4904.     lang = lang or mw.language.getContentLanguage()
  4905.     local success, date = pcall(
  4906.         lang.formatDate,
  4907.         lang,
  4908.         self._cfg.msg['expiry-date-format'] or 'j F Y',
  4909.         '@' .. tostring(num)
  4910.     )
  4911.     if success then
  4912.         return date
  4913.     end
  4914. end
  4915.  
  4916. function Blurb:_getExpandedMessage(msgKey)
  4917.     return self:_substituteParameters(self._cfg.msg[msgKey])
  4918. end
  4919.  
  4920. function Blurb:_substituteParameters(msg)
  4921.     if not self._params then
  4922.         local parameterFuncs = {}
  4923.  
  4924.         parameterFuncs.CURRENTVERSION     = self._makeCurrentVersionParameter
  4925.         parameterFuncs.EDITREQUEST        = self._makeEditRequestParameter
  4926.         parameterFuncs.EXPIRY             = self._makeExpiryParameter
  4927.         parameterFuncs.EXPLANATIONBLURB   = self._makeExplanationBlurbParameter
  4928.         parameterFuncs.IMAGELINK          = self._makeImageLinkParameter
  4929.         parameterFuncs.INTROBLURB         = self._makeIntroBlurbParameter
  4930.         parameterFuncs.INTROFRAGMENT      = self._makeIntroFragmentParameter
  4931.         parameterFuncs.PAGETYPE           = self._makePagetypeParameter
  4932.         parameterFuncs.PROTECTIONBLURB    = self._makeProtectionBlurbParameter
  4933.         parameterFuncs.PROTECTIONDATE     = self._makeProtectionDateParameter
  4934.         parameterFuncs.PROTECTIONLEVEL    = self._makeProtectionLevelParameter
  4935.         parameterFuncs.PROTECTIONLOG      = self._makeProtectionLogParameter
  4936.         parameterFuncs.TALKPAGE           = self._makeTalkPageParameter
  4937.         parameterFuncs.TOOLTIPBLURB       = self._makeTooltipBlurbParameter
  4938.         parameterFuncs.TOOLTIPFRAGMENT    = self._makeTooltipFragmentParameter
  4939.         parameterFuncs.VANDAL             = self._makeVandalTemplateParameter
  4940.        
  4941.         self._params = setmetatable({}, {
  4942.             __index = function (t, k)
  4943.                 local param
  4944.                 if parameterFuncs[k] then
  4945.                     param = parameterFuncs[k](self)
  4946.                 end
  4947.                 param = param or ''
  4948.                 t[k] = param
  4949.                 return param
  4950.             end
  4951.         })
  4952.     end
  4953.    
  4954.     msg = msg:gsub('${(%u+)}', self._params)
  4955.     return msg
  4956. end
  4957.  
  4958. function Blurb:_makeCurrentVersionParameter()
  4959.     -- A link to the page history or the move log, depending on the kind of
  4960.     -- protection.
  4961.     local pagename = self._protectionObj.title.prefixedText
  4962.     if self._protectionObj.action == 'move' then
  4963.         -- We need the move log link.
  4964.         return makeFullUrl(
  4965.             'Special:Log',
  4966.             {type = 'move', page = pagename},
  4967.             self:_getExpandedMessage('current-version-move-display')
  4968.         )
  4969.     else
  4970.         -- We need the history link.
  4971.         return makeFullUrl(
  4972.             pagename,
  4973.             {action = 'history'},
  4974.             self:_getExpandedMessage('current-version-edit-display')
  4975.         )
  4976.     end
  4977. end
  4978.  
  4979. function Blurb:_makeEditRequestParameter()
  4980.     local mEditRequest = require('Module:Submit an edit request')
  4981.     local action = self._protectionObj.action
  4982.     local level = self._protectionObj.level
  4983.    
  4984.     -- Get the edit request type.
  4985.     local requestType
  4986.     if action == 'edit' then
  4987.         if level == 'autoconfirmed' then
  4988.             requestType = 'semi'
  4989.         elseif level == 'extendedconfirmed' then
  4990.             requestType = 'extended'
  4991.         elseif level == 'templateeditor' then
  4992.             requestType = 'template'
  4993.         end
  4994.     end
  4995.     requestType = requestType or 'full'
  4996.    
  4997.     -- Get the display value.
  4998.     local display = self:_getExpandedMessage('edit-request-display')
  4999.  
  5000.     return mEditRequest._link{type = requestType, display = display}
  5001. end
  5002.  
  5003. function Blurb:_makeExpiryParameter()
  5004.     local expiry = self._protectionObj.expiry
  5005.     if type(expiry) == 'number' then
  5006.         return self:_formatDate(expiry)
  5007.     else
  5008.         return expiry
  5009.     end
  5010. end
  5011.  
  5012. function Blurb:_makeExplanationBlurbParameter()
  5013.     -- Cover special cases first.
  5014.     if self._protectionObj.title.namespace == 8 then
  5015.         -- MediaWiki namespace
  5016.         return self:_getExpandedMessage('explanation-blurb-nounprotect')
  5017.     end
  5018.  
  5019.     -- Get explanation blurb table keys
  5020.     local action = self._protectionObj.action
  5021.     local level = self._protectionObj.level
  5022.     local talkKey = self._protectionObj.title.isTalkPage and 'talk' or 'subject'
  5023.  
  5024.     -- Find the message in the explanation blurb table and substitute any
  5025.     -- parameters.
  5026.     local explanations = self._cfg.explanationBlurbs
  5027.     local msg
  5028.     if explanations[action][level] and explanations[action][level][talkKey] then
  5029.         msg = explanations[action][level][talkKey]
  5030.     elseif explanations[action][level] and explanations[action][level].default then
  5031.         msg = explanations[action][level].default
  5032.     elseif explanations[action].default and explanations[action].default[talkKey] then
  5033.         msg = explanations[action].default[talkKey]
  5034.     elseif explanations[action].default and explanations[action].default.default then
  5035.         msg = explanations[action].default.default
  5036.     else
  5037.         error(string.format(
  5038.             'could not find explanation blurb for action "%s", level "%s" and talk key "%s"',
  5039.             action,
  5040.             level,
  5041.             talkKey
  5042.         ), 8)
  5043.     end
  5044.     return self:_substituteParameters(msg)
  5045. end
  5046.  
  5047. function Blurb:_makeImageLinkParameter()
  5048.     local imageLinks = self._cfg.imageLinks
  5049.     local action = self._protectionObj.action
  5050.     local level = self._protectionObj.level
  5051.     local msg
  5052.     if imageLinks[action][level] then
  5053.         msg = imageLinks[action][level]
  5054.     elseif imageLinks[action].default then
  5055.         msg = imageLinks[action].default
  5056.     else
  5057.         msg = imageLinks.edit.default
  5058.     end
  5059.     return self:_substituteParameters(msg)
  5060. end
  5061.  
  5062. function Blurb:_makeIntroBlurbParameter()
  5063.     if self._protectionObj:isTemporary() then
  5064.         return self:_getExpandedMessage('intro-blurb-expiry')
  5065.     else
  5066.         return self:_getExpandedMessage('intro-blurb-noexpiry')
  5067.     end
  5068. end
  5069.  
  5070. function Blurb:_makeIntroFragmentParameter()
  5071.     if self._protectionObj:isTemporary() then
  5072.         return self:_getExpandedMessage('intro-fragment-expiry')
  5073.     else
  5074.         return self:_getExpandedMessage('intro-fragment-noexpiry')
  5075.     end
  5076. end
  5077.  
  5078. function Blurb:_makePagetypeParameter()
  5079.     local pagetypes = self._cfg.pagetypes
  5080.     return pagetypes[self._protectionObj.title.namespace]
  5081.         or pagetypes.default
  5082.         or error('no default pagetype defined', 8)
  5083. end
  5084.  
  5085. function Blurb:_makeProtectionBlurbParameter()
  5086.     local protectionBlurbs = self._cfg.protectionBlurbs
  5087.     local action = self._protectionObj.action
  5088.     local level = self._protectionObj.level
  5089.     local msg
  5090.     if protectionBlurbs[action][level] then
  5091.         msg = protectionBlurbs[action][level]
  5092.     elseif protectionBlurbs[action].default then
  5093.         msg = protectionBlurbs[action].default
  5094.     elseif protectionBlurbs.edit.default then
  5095.         msg = protectionBlurbs.edit.default
  5096.     else
  5097.         error('no protection blurb defined for protectionBlurbs.edit.default', 8)
  5098.     end
  5099.     return self:_substituteParameters(msg)
  5100. end
  5101.  
  5102. function Blurb:_makeProtectionDateParameter()
  5103.     local protectionDate = self._protectionObj.protectionDate
  5104.     if type(protectionDate) == 'number' then
  5105.         return self:_formatDate(protectionDate)
  5106.     else
  5107.         return protectionDate
  5108.     end
  5109. end
  5110.  
  5111. function Blurb:_makeProtectionLevelParameter()
  5112.     local protectionLevels = self._cfg.protectionLevels
  5113.     local action = self._protectionObj.action
  5114.     local level = self._protectionObj.level
  5115.     local msg
  5116.     if protectionLevels[action][level] then
  5117.         msg = protectionLevels[action][level]
  5118.     elseif protectionLevels[action].default then
  5119.         msg = protectionLevels[action].default
  5120.     elseif protectionLevels.edit.default then
  5121.         msg = protectionLevels.edit.default
  5122.     else
  5123.         error('no protection level defined for protectionLevels.edit.default', 8)
  5124.     end
  5125.     return self:_substituteParameters(msg)
  5126. end
  5127.  
  5128. function Blurb:_makeProtectionLogParameter()
  5129.     local pagename = self._protectionObj.title.prefixedText
  5130.     if self._protectionObj.action == 'autoreview' then
  5131.         -- We need the pending changes log.
  5132.         return makeFullUrl(
  5133.             'Special:Log',
  5134.             {type = 'stable', page = pagename},
  5135.             self:_getExpandedMessage('pc-log-display')
  5136.         )
  5137.     else
  5138.         -- We need the protection log.
  5139.         return makeFullUrl(
  5140.             'Special:Log',
  5141.             {type = 'protect', page = pagename},
  5142.             self:_getExpandedMessage('protection-log-display')
  5143.         )
  5144.     end
  5145. end
  5146.  
  5147. function Blurb:_makeTalkPageParameter()
  5148.     return string.format(
  5149.         '[[%s:%s#%s|%s]]',
  5150.         mw.site.namespaces[self._protectionObj.title.namespace].talk.name,
  5151.         self._protectionObj.title.text,
  5152.         self._args.section or 'top',
  5153.         self:_getExpandedMessage('talk-page-link-display')
  5154.     )
  5155. end
  5156.  
  5157. function Blurb:_makeTooltipBlurbParameter()
  5158.     if self._protectionObj:isTemporary() then
  5159.         return self:_getExpandedMessage('tooltip-blurb-expiry')
  5160.     else
  5161.         return self:_getExpandedMessage('tooltip-blurb-noexpiry')
  5162.     end
  5163. end
  5164.  
  5165. function Blurb:_makeTooltipFragmentParameter()
  5166.     if self._protectionObj:isTemporary() then
  5167.         return self:_getExpandedMessage('tooltip-fragment-expiry')
  5168.     else
  5169.         return self:_getExpandedMessage('tooltip-fragment-noexpiry')
  5170.     end
  5171. end
  5172.  
  5173. function Blurb:_makeVandalTemplateParameter()
  5174.     return mw.getCurrentFrame():expandTemplate{
  5175.         title="vandal-m",
  5176.         args={self._args.user or self._protectionObj.title.baseText}
  5177.     }
  5178. end
  5179.  
  5180. -- Public methods --
  5181.  
  5182. function Blurb:makeBannerText(key)
  5183.     -- Validate input.
  5184.     if not key or not Blurb.bannerTextFields[key] then
  5185.         error(string.format(
  5186.             '"%s" is not a valid banner config field',
  5187.             tostring(key)
  5188.         ), 2)
  5189.     end
  5190.  
  5191.     -- Generate the text.
  5192.     local msg = self._protectionObj.bannerConfig[key]
  5193.     if type(msg) == 'string' then
  5194.         return self:_substituteParameters(msg)
  5195.     elseif type(msg) == 'function' then
  5196.         msg = msg(self._protectionObj, self._args)
  5197.         if type(msg) ~= 'string' then
  5198.             error(string.format(
  5199.                 'bad output from banner config function with key "%s"'
  5200.                     .. ' (expected string, got %s)',
  5201.                 tostring(key),
  5202.                 type(msg)
  5203.             ), 4)
  5204.         end
  5205.         return self:_substituteParameters(msg)
  5206.     end
  5207. end
  5208.  
  5209. --------------------------------------------------------------------------------
  5210. -- BannerTemplate class
  5211. --------------------------------------------------------------------------------
  5212.  
  5213. local BannerTemplate = {}
  5214. BannerTemplate.__index = BannerTemplate
  5215.  
  5216. function BannerTemplate.new(protectionObj, cfg)
  5217.     local obj = {}
  5218.     obj._cfg = cfg
  5219.  
  5220.     -- Set the image filename.
  5221.     local imageFilename = protectionObj.bannerConfig.image
  5222.     if imageFilename then
  5223.         obj._imageFilename = imageFilename
  5224.     else
  5225.         -- If an image filename isn't specified explicitly in the banner config,
  5226.         -- generate it from the protection status and the namespace.
  5227.         local action = protectionObj.action
  5228.         local level = protectionObj.level
  5229.         local namespace = protectionObj.title.namespace
  5230.         local reason = protectionObj.reason
  5231.        
  5232.         -- Deal with special cases first.
  5233.         if (
  5234.             namespace == 10
  5235.             or namespace == 828
  5236.             or reason and obj._cfg.indefImageReasons[reason]
  5237.             )
  5238.             and action == 'edit'
  5239.             and level == 'sysop'
  5240.             and not protectionObj:isTemporary()
  5241.         then
  5242.             -- Fully protected modules and templates get the special red "indef"
  5243.             -- padlock.
  5244.             obj._imageFilename = obj._cfg.msg['image-filename-indef']
  5245.         else
  5246.             -- Deal with regular protection types.
  5247.             local images = obj._cfg.images
  5248.             if images[action] then
  5249.                 if images[action][level] then
  5250.                     obj._imageFilename = images[action][level]
  5251.                 elseif images[action].default then
  5252.                     obj._imageFilename = images[action].default
  5253.                 end
  5254.             end
  5255.         end
  5256.     end
  5257.     return setmetatable(obj, BannerTemplate)
  5258. end
  5259.  
  5260. function BannerTemplate:renderImage()
  5261.     local filename = self._imageFilename
  5262.         or self._cfg.msg['image-filename-default']
  5263.         or 'Transparent.gif'
  5264.     return makeFileLink{
  5265.         file = filename,
  5266.         size = (self.imageWidth or 20) .. 'px',
  5267.         alt = self._imageAlt,
  5268.         link = self._imageLink,
  5269.         caption = self.imageCaption
  5270.     }
  5271. end
  5272.  
  5273. --------------------------------------------------------------------------------
  5274. -- Banner class
  5275. --------------------------------------------------------------------------------
  5276.  
  5277. local Banner = setmetatable({}, BannerTemplate)
  5278. Banner.__index = Banner
  5279.  
  5280. function Banner.new(protectionObj, blurbObj, cfg)
  5281.     local obj = BannerTemplate.new(protectionObj, cfg) -- This doesn't need the blurb.
  5282.     obj.imageWidth = 40
  5283.     obj.imageCaption = blurbObj:makeBannerText('alt') -- Large banners use the alt text for the tooltip.
  5284.     obj._reasonText = blurbObj:makeBannerText('text')
  5285.     obj._explanationText = blurbObj:makeBannerText('explanation')
  5286.     obj._page = protectionObj.title.prefixedText -- Only makes a difference in testing.
  5287.     return setmetatable(obj, Banner)
  5288. end
  5289.  
  5290. function Banner:__tostring()
  5291.     -- Renders the banner.
  5292.     makeMessageBox = makeMessageBox or require('Module:Message box').main
  5293.     local reasonText = self._reasonText or error('no reason text set', 2)
  5294.     local explanationText = self._explanationText
  5295.     local mbargs = {
  5296.         page = self._page,
  5297.         type = 'protection',
  5298.         image = self:renderImage(),
  5299.         text = string.format(
  5300.             "'''%s'''%s",
  5301.             reasonText,
  5302.             explanationText and '&lt;br /&gt;' .. explanationText or ''
  5303.         )
  5304.     }
  5305.     return makeMessageBox('mbox', mbargs)
  5306. end
  5307.  
  5308. --------------------------------------------------------------------------------
  5309. -- Padlock class
  5310. --------------------------------------------------------------------------------
  5311.  
  5312. local Padlock = setmetatable({}, BannerTemplate)
  5313. Padlock.__index = Padlock
  5314.  
  5315. function Padlock.new(protectionObj, blurbObj, cfg)
  5316.     local obj = BannerTemplate.new(protectionObj, cfg) -- This doesn't need the blurb.
  5317.     obj.imageWidth = 20
  5318.     obj.imageCaption = blurbObj:makeBannerText('tooltip')
  5319.     obj._imageAlt = blurbObj:makeBannerText('alt')
  5320.     obj._imageLink = blurbObj:makeBannerText('link')
  5321.     obj._indicatorName = cfg.padlockIndicatorNames[protectionObj.action]
  5322.         or cfg.padlockIndicatorNames.default
  5323.         or 'pp-default'
  5324.     return setmetatable(obj, Padlock)
  5325. end
  5326.  
  5327. function Padlock:__tostring()
  5328.     local frame = mw.getCurrentFrame()
  5329.     -- The nowiki tag helps prevent whitespace at the top of articles.
  5330.     return frame:extensionTag{name = 'nowiki'} .. frame:extensionTag{
  5331.         name = 'indicator',
  5332.         args = {name = self._indicatorName},
  5333.         content = self:renderImage()
  5334.     }
  5335. end
  5336.  
  5337. --------------------------------------------------------------------------------
  5338. -- Exports
  5339. --------------------------------------------------------------------------------
  5340.  
  5341. local p = {}
  5342.  
  5343. function p._exportClasses()
  5344.     -- This is used for testing purposes.
  5345.     return {
  5346.         Protection = Protection,
  5347.         Blurb = Blurb,
  5348.         BannerTemplate = BannerTemplate,
  5349.         Banner = Banner,
  5350.         Padlock = Padlock,
  5351.     }
  5352. end
  5353.  
  5354. function p._main(args, cfg, title)
  5355.     args = args or {}
  5356.     cfg = cfg or require(CONFIG_MODULE)
  5357.  
  5358.     local protectionObj = Protection.new(args, cfg, title)
  5359.  
  5360.     local ret = {}
  5361.  
  5362.     -- If a page's edit protection is equally or more restrictive than its
  5363.     -- protection from some other action, then don't bother displaying anything
  5364.     -- for the other action (except categories).
  5365.     if not yesno(args.catonly) and (protectionObj.action == 'edit' or
  5366.         args.demolevel or
  5367.         not getReachableNodes(
  5368.             cfg.hierarchy,
  5369.             protectionObj.level
  5370.         )[effectiveProtectionLevel('edit', protectionObj.title)])
  5371.     then
  5372.         -- Initialise the blurb object
  5373.         local blurbObj = Blurb.new(protectionObj, args, cfg)
  5374.    
  5375.         -- Render the banner
  5376.         if protectionObj:shouldShowLock() then
  5377.             ret[#ret + 1] = tostring(
  5378.                 (yesno(args.small) and Padlock or Banner)
  5379.                 .new(protectionObj, blurbObj, cfg)
  5380.             )
  5381.         end
  5382.     end
  5383.  
  5384.     -- Render the categories
  5385.     if yesno(args.category) ~= false then
  5386.         ret[#ret + 1] = protectionObj:makeCategoryLinks()
  5387.     end
  5388.    
  5389.     return table.concat(ret)   
  5390. end
  5391.  
  5392. function p.main(frame, cfg)
  5393.     cfg = cfg or require(CONFIG_MODULE)
  5394.  
  5395.     -- Find default args, if any.
  5396.     local parent = frame.getParent and frame:getParent()
  5397.     local defaultArgs = parent and cfg.wrappers[parent:getTitle():gsub('/sandbox$', '')]
  5398.  
  5399.     -- Find user args, and use the parent frame if we are being called from a
  5400.     -- wrapper template.
  5401.     getArgs = getArgs or require('Module:Arguments').getArgs
  5402.     local userArgs = getArgs(frame, {
  5403.         parentOnly = defaultArgs,
  5404.         frameOnly = not defaultArgs
  5405.     })
  5406.  
  5407.     -- Build the args table. User-specified args overwrite default args.
  5408.     local args = {}
  5409.     for k, v in pairs(defaultArgs or {}) do
  5410.         args[k] = v
  5411.     end
  5412.     for k, v in pairs(userArgs) do
  5413.         args[k] = v
  5414.     end
  5415.     return p._main(args, cfg)
  5416. end
  5417.  
  5418. return p</text>
  5419.       <sha1>ktral6mmlw9hhlxwgaq85237qklrdx8</sha1>
  5420.     </revision>
  5421.   </page>
  5422.   <page>
  5423.     <title>Module:Protection banner/config</title>
  5424.     <ns>828</ns>
  5425.     <id>42982788</id>
  5426.     <revision>
  5427.       <id>1069763876</id>
  5428.       <parentid>1053458493</parentid>
  5429.       <timestamp>2022-02-03T23:49:55Z</timestamp>
  5430.       <contributor>
  5431.         <username>Andrybak</username>
  5432.         <id>23735172</id>
  5433.       </contributor>
  5434.       <comment>add categorization into [[:Category:Wikipedia extended-confirmed-protected modules]] per [[Special:Diff/1069761256|request]]</comment>
  5435.       <model>Scribunto</model>
  5436.       <format>text/plain</format>
  5437.       <text bytes="46228" xml:space="preserve">-- This module provides configuration data for [[Module:Protection banner]].
  5438.  
  5439. return {
  5440.  
  5441. --------------------------------------------------------------------------------
  5442. --
  5443. --                                BANNER DATA
  5444. --
  5445. --------------------------------------------------------------------------------
  5446.  
  5447. --[[
  5448. -- Banner data consists of six fields:
  5449. -- * text - the main protection text that appears at the top of protection
  5450. --   banners.
  5451. -- * explanation - the text that appears below the main protection text, used
  5452. --   to explain the details of the protection.
  5453. -- * tooltip - the tooltip text you see when you move the mouse over a small
  5454. --   padlock icon.
  5455. -- * link - the page that the small padlock icon links to.
  5456. -- * alt - the alt text for the small padlock icon. This is also used as tooltip
  5457. --   text for the large protection banners.
  5458. -- * image - the padlock image used in both protection banners and small padlock
  5459. --   icons.
  5460. --
  5461. -- The module checks in three separate tables to find a value for each field.
  5462. -- First it checks the banners table, which has values specific to the reason
  5463. -- for the page being protected. Then the module checks the defaultBanners
  5464. -- table, which has values specific to each protection level. Finally, the
  5465. -- module checks the masterBanner table, which holds data for protection
  5466. -- templates to use if no data has been found in the previous two tables.
  5467. --
  5468. -- The values in the banner data can take parameters. These are specified
  5469. -- using ${TEXTLIKETHIS} (a dollar sign preceding a parameter name
  5470. -- enclosed in curly braces).
  5471. --
  5472. --                          Available parameters:
  5473. --
  5474. -- ${CURRENTVERSION} - a link to the page history or the move log, with the
  5475. -- display message "current-version-edit-display" or
  5476. -- "current-version-move-display".
  5477. --
  5478. -- ${EDITREQUEST} - a link to create an edit request for the current page.
  5479. --
  5480. -- ${EXPLANATIONBLURB} - an explanation blurb, e.g. "Please discuss any changes
  5481. -- on the talk page; you may submit a request to ask an administrator to make
  5482. -- an edit if it is minor or supported by consensus."
  5483. --
  5484. -- ${IMAGELINK} - a link to set the image to, depending on the protection
  5485. -- action and protection level.
  5486. --
  5487. -- ${INTROBLURB} - the PROTECTIONBLURB parameter, plus the expiry if an expiry
  5488. -- is set. E.g. "Editing of this page by new or unregistered users is currently
  5489. -- disabled until dd Month YYYY."
  5490. --
  5491. -- ${INTROFRAGMENT} - the same as ${INTROBLURB}, but without final punctuation
  5492. -- so that it can be used in run-on sentences.
  5493. --
  5494. -- ${PAGETYPE} - the type of the page, e.g. "article" or "template".
  5495. -- Defined in the cfg.pagetypes table.
  5496. --
  5497. -- ${PROTECTIONBLURB} - a blurb explaining the protection level of the page, e.g.
  5498. -- "Editing of this page by new or unregistered users is currently disabled"
  5499. --
  5500. -- ${PROTECTIONDATE} - the protection date, if it has been supplied to the
  5501. -- template.
  5502. --
  5503. -- ${PROTECTIONLEVEL} - the protection level, e.g. "fully protected" or
  5504. -- "semi-protected".
  5505. --
  5506. -- ${PROTECTIONLOG} - a link to the protection log or the pending changes log,
  5507. -- depending on the protection action.
  5508. --
  5509. -- ${TALKPAGE} - a link to the talk page. If a section is specified, links
  5510. -- straight to that talk page section.
  5511. --
  5512. -- ${TOOLTIPBLURB} - uses the PAGETYPE, PROTECTIONTYPE and EXPIRY parameters to
  5513. -- create a blurb like "This template is semi-protected", or "This article is
  5514. -- move-protected until DD Month YYYY".
  5515. --
  5516. -- ${VANDAL} - links for the specified username (or the root page name)
  5517. -- using Module:Vandal-m.
  5518. --
  5519. --                                 Functions
  5520. --
  5521. -- For advanced users, it is possible to use Lua functions instead of strings
  5522. -- in the banner config tables. Using functions gives flexibility that is not
  5523. -- possible just by using parameters. Functions take two arguments, the
  5524. -- protection object and the template arguments, and they must output a string.
  5525. --
  5526. -- For example:
  5527. --
  5528. -- text = function (protectionObj, args)
  5529. --     if protectionObj.level == 'autoconfirmed' then
  5530. --         return 'foo'
  5531. --     else
  5532. --         return 'bar'
  5533. --     end
  5534. -- end
  5535. --
  5536. -- Some protection object properties and methods that may be useful:
  5537. -- protectionObj.action - the protection action
  5538. -- protectionObj.level - the protection level
  5539. -- protectionObj.reason - the protection reason
  5540. -- protectionObj.expiry - the expiry. Nil if unset, the string "indef" if set
  5541. --     to indefinite, and the protection time in unix time if temporary.
  5542. -- protectionObj.protectionDate - the protection date in unix time, or nil if
  5543. --     unspecified.
  5544. -- protectionObj.bannerConfig - the banner config found by the module. Beware
  5545. --     of editing the config field used by the function, as it could create an
  5546. --     infinite loop.
  5547. -- protectionObj:isProtected - returns a boolean showing whether the page is
  5548. --     protected.
  5549. -- protectionObj:isTemporary - returns a boolean showing whether the expiry is
  5550. --     temporary.
  5551. -- protectionObj:isIncorrect - returns a boolean showing whether the protection
  5552. --     template is incorrect.
  5553. --]]
  5554.  
  5555. -- The master banner data, used if no values have been found in banners or
  5556. -- defaultBanners.
  5557. masterBanner = {
  5558.     text = '${INTROBLURB}',
  5559.     explanation = '${EXPLANATIONBLURB}',
  5560.     tooltip = '${TOOLTIPBLURB}',
  5561.     link = '${IMAGELINK}',
  5562.     alt = 'Page ${PROTECTIONLEVEL}'
  5563. },
  5564.  
  5565. -- The default banner data. This holds banner data for different protection
  5566. -- levels.
  5567. -- *required* - this table needs edit, move, autoreview and upload subtables.
  5568. defaultBanners = {
  5569.     edit = {},
  5570.     move = {},
  5571.     autoreview = {
  5572.         default = {
  5573.             alt = 'Page protected with pending changes',
  5574.             tooltip = 'All edits by unregistered and new users are subject to review prior to becoming visible to unregistered users',
  5575.             image = 'Pending-protection-shackle.svg'
  5576.         }
  5577.     },
  5578.     upload = {}
  5579. },
  5580.  
  5581. -- The banner data. This holds banner data for different protection reasons.
  5582. -- In fact, the reasons specified in this table control which reasons are
  5583. -- valid inputs to the first positional parameter.
  5584. --
  5585. -- There is also a non-standard "description" field that can be used for items
  5586. -- in this table. This is a description of the protection reason for use in the
  5587. -- module documentation.
  5588. --
  5589. -- *required* - this table needs edit, move, autoreview and upload subtables.
  5590. banners = {
  5591.     edit = {
  5592.         blp = {
  5593.             description = 'For pages protected to promote compliance with the'
  5594.                 .. ' [[Wikipedia:Biographies of living persons'
  5595.                 .. '|biographies of living persons]] policy',
  5596.             text = '${INTROFRAGMENT} to promote compliance with'
  5597.                 .. ' [[Wikipedia:Biographies of living persons'
  5598.                 .. "|Wikipedia's&amp;nbsp;policy on&amp;nbsp;the&amp;nbsp;biographies"
  5599.                 .. ' of&amp;nbsp;living&amp;nbsp;people]].',
  5600.             tooltip = '${TOOLTIPFRAGMENT} to promote compliance with the policy on'
  5601.                 .. ' biographies of living persons',
  5602.         },
  5603.         dmca = {
  5604.             description = 'For pages protected by the Wikimedia Foundation'
  5605.                 .. ' due to [[Digital Millennium Copyright Act]] takedown requests',
  5606.             explanation = function (protectionObj, args)
  5607.                 local ret = 'Pursuant to a rights owner notice under the Digital'
  5608.                     .. ' Millennium Copyright Act (DMCA) regarding some content'
  5609.                     .. ' in this article, the Wikimedia Foundation acted under'
  5610.                     .. ' applicable law and took down and restricted the content'
  5611.                     .. ' in question.'
  5612.                 if args.notice then
  5613.                     ret = ret .. ' A copy of the received notice can be found here: '
  5614.                         .. args.notice .. '.'
  5615.                 end
  5616.                 ret = ret .. ' For more information, including websites discussing'
  5617.                     .. ' how to file a counter-notice, please see'
  5618.                     .. " [[Wikipedia:Office actions]] and the article's ${TALKPAGE}."
  5619.                     .. "'''Do not remove this template from the article until the"
  5620.                     .. " restrictions are withdrawn'''."
  5621.                 return ret
  5622.             end,
  5623.             image = 'Office-protection-shackle.svg',
  5624.         },
  5625.         dispute = {
  5626.             description = 'For pages protected due to editing disputes',
  5627.             text = function (protectionObj, args)
  5628.                 -- Find the value of "disputes".
  5629.                 local display = 'disputes'
  5630.                 local disputes
  5631.                 if args.section then
  5632.                     disputes = string.format(
  5633.                         '[[%s:%s#%s|%s]]',
  5634.                         mw.site.namespaces[protectionObj.title.namespace].talk.name,
  5635.                         protectionObj.title.text,
  5636.                         args.section,
  5637.                         display
  5638.                     )
  5639.                 else
  5640.                     disputes = display
  5641.                 end
  5642.  
  5643.                 -- Make the blurb, depending on the expiry.
  5644.                 local msg
  5645.                 if type(protectionObj.expiry) == 'number' then
  5646.                     msg = '${INTROFRAGMENT} or until editing %s have been resolved.'
  5647.                 else
  5648.                     msg = '${INTROFRAGMENT} until editing %s have been resolved.'
  5649.                 end
  5650.                 return string.format(msg, disputes)
  5651.             end,
  5652.             explanation = "This protection is '''not''' an endorsement of the"
  5653.                 .. ' ${CURRENTVERSION}. ${EXPLANATIONBLURB}',
  5654.             tooltip = '${TOOLTIPFRAGMENT} due to editing disputes',
  5655.         },
  5656.         ecp = {
  5657.             description = 'For articles in topic areas authorized by'
  5658.                 .. ' [[Wikipedia:Arbitration Committee|ArbCom]] or'
  5659.                 .. ' meets the criteria for community use',
  5660.             tooltip = 'This ${PAGETYPE} is extended-confirmed protected',
  5661.             alt = 'Extended-protected ${PAGETYPE}',
  5662.         },
  5663.         mainpage = {
  5664.             description = 'For pages protected for being displayed on the [[Main Page]]',
  5665.             text = 'This file is currently'
  5666.                 .. ' [[Wikipedia:This page is protected|protected]] from'
  5667.                 .. ' editing because it is currently or will soon be displayed'
  5668.                 .. ' on the [[Main Page]].',
  5669.             explanation = 'Images on the Main Page are protected due to their high'
  5670.                 .. ' visibility. Please discuss any necessary changes on the ${TALKPAGE}.'
  5671.                 .. '&lt;br /&gt;&lt;span style="font-size:90%;"&gt;'
  5672.                 .. "'''Administrators:''' Once this image is definitely off the Main Page,"
  5673.                 .. ' please unprotect this file, or reduce to semi-protection,'
  5674.                 .. ' as appropriate.&lt;/span&gt;',
  5675.         },
  5676.         office = {
  5677.             description = 'For pages protected by the Wikimedia Foundation',
  5678.             text = function (protectionObj, args)
  5679.                 local ret = 'This ${PAGETYPE} is currently under the'
  5680.                     .. ' scrutiny of the'
  5681.                     .. ' [[Wikipedia:Office actions|Wikimedia Foundation Office]]'
  5682.                     .. ' and is protected.'
  5683.                 if protectionObj.protectionDate then
  5684.                     ret = ret .. ' It has been protected since ${PROTECTIONDATE}.'
  5685.                 end
  5686.                 return ret
  5687.             end,
  5688.             explanation = "If you can edit this page, please discuss all changes and"
  5689.                 .. " additions on the ${TALKPAGE} first. '''Do not remove protection from this"
  5690.                 .. " page unless you are authorized by the Wikimedia Foundation to do"
  5691.                 .. " so.'''",
  5692.             image = 'Office-protection-shackle.svg',
  5693.         },
  5694.         reset = {
  5695.             description = 'For pages protected by the Wikimedia Foundation and'
  5696.                 .. ' "reset" to a bare-bones version',
  5697.             text = 'This ${PAGETYPE} is currently under the'
  5698.                     .. ' scrutiny of the'
  5699.                     .. ' [[Wikipedia:Office actions|Wikimedia Foundation Office]]'
  5700.                     .. ' and is protected.',
  5701.             explanation = function (protectionObj, args)
  5702.                 local ret = ''
  5703.                 if protectionObj.protectionDate then
  5704.                     ret = ret .. 'On ${PROTECTIONDATE} this ${PAGETYPE} was'
  5705.                 else
  5706.                     ret = ret .. 'This ${PAGETYPE} has been'
  5707.                 end
  5708.                 ret = ret .. ' reduced to a'
  5709.                 .. ' simplified, "bare bones" version so that it may be completely'
  5710.                 .. ' rewritten to ensure it meets the policies of'
  5711.                 .. ' [[WP:NPOV|Neutral Point of View]] and [[WP:V|Verifiability]].'
  5712.                 .. ' Standard Wikipedia policies will apply to its rewriting—which'
  5713.                 .. ' will eventually be open to all editors—and will be strictly'
  5714.                 .. ' enforced. The ${PAGETYPE} has been ${PROTECTIONLEVEL} while'
  5715.                 .. ' it is being rebuilt.\n\n'
  5716.                 .. 'Any insertion of material directly from'
  5717.                 .. ' pre-protection revisions of the ${PAGETYPE} will be removed, as'
  5718.                 .. ' will any material added to the ${PAGETYPE} that is not properly'
  5719.                 .. ' sourced. The associated talk page(s) were also cleared on the'
  5720.                 .. " same date.\n\n"
  5721.                 .. "If you can edit this page, please discuss all changes and"
  5722.                 .. " additions on the ${TALKPAGE} first. '''Do not override"
  5723.                 .. " this action, and do not remove protection from this page,"
  5724.                 .. " unless you are authorized by the Wikimedia Foundation"
  5725.                 .. " to do so. No editor may remove this notice.'''"
  5726.  
  5727.                 return ret
  5728.             end,
  5729.             image = 'Office-protection-shackle.svg',
  5730.         },
  5731.         sock = {
  5732.             description = 'For pages protected due to'
  5733.                 .. ' [[Wikipedia:Sock puppetry|sock puppetry]]',
  5734.             text = '${INTROFRAGMENT} to prevent [[Wikipedia:Sock puppetry|sock puppets]] of'
  5735.                 .. ' [[Wikipedia:Blocking policy|blocked]] or'
  5736.                 .. ' [[Wikipedia:Banning policy|banned users]]'
  5737.                 .. ' from editing it.',
  5738.             tooltip = '${TOOLTIPFRAGMENT} to prevent sock puppets of blocked or banned users from'
  5739.                 .. ' editing it',
  5740.         },
  5741.         template = {
  5742.             description = 'For [[Wikipedia:High-risk templates|high-risk]]'
  5743.                 .. ' templates and Lua modules',
  5744.             text = 'This is a permanently [[Help:Protection|protected]] ${PAGETYPE},'
  5745.                 .. ' as it is [[Wikipedia:High-risk templates|high-risk]].',
  5746.             explanation = 'Please discuss any changes on the ${TALKPAGE}; you may'
  5747.                 .. ' ${EDITREQUEST} to ask an'
  5748.                 .. ' [[Wikipedia:Administrators|administrator]] or'
  5749.                 .. ' [[Wikipedia:Template editor|template editor]] to make an edit if'
  5750.                 .. ' it is [[Help:Minor edit#When to mark an edit as a minor edit'
  5751.                 .. '|uncontroversial]] or supported by'
  5752.                 .. ' [[Wikipedia:Consensus|consensus]]. You can also'
  5753.                 .. ' [[Wikipedia:Requests for page protection|request]] that the page be'
  5754.                 .. ' unprotected.',
  5755.             tooltip = 'This high-risk ${PAGETYPE} is permanently ${PROTECTIONLEVEL}'
  5756.                 .. ' to prevent vandalism',
  5757.             alt = 'Permanently protected ${PAGETYPE}',
  5758.         },
  5759.         usertalk = {
  5760.             description = 'For pages protected against disruptive edits by a'
  5761.                 .. ' particular user',
  5762.             text = '${INTROFRAGMENT} to prevent ${VANDAL} from using it to make disruptive edits,'
  5763.                 .. ' such as abusing the'
  5764.                 .. ' &amp;#123;&amp;#123;[[Template:unblock|unblock]]&amp;#125;&amp;#125; template.',
  5765.             explanation = 'If you cannot edit this user talk page and you need to'
  5766.                 .. ' make a change or leave a message, you can'
  5767.                 .. ' [[Wikipedia:Requests for page protection'
  5768.                 .. '#Current requests for edits to a protected page'
  5769.                 .. '|request an edit]],'
  5770.                 .. ' [[Wikipedia:Requests for page protection'
  5771.                 .. '#Current requests for reduction in protection level'
  5772.                 .. '|request unprotection]],'
  5773.                 .. ' [[Special:Userlogin|log in]],'
  5774.                 .. ' or [[Special:UserLogin/signup|create an account]].',
  5775.         },
  5776.         vandalism = {
  5777.             description = 'For pages protected against'
  5778.                 .. ' [[Wikipedia:Vandalism|vandalism]]',
  5779.             text = '${INTROFRAGMENT} due to [[Wikipedia:Vandalism|vandalism]].',
  5780.             explanation = function (protectionObj, args)
  5781.                 local ret = ''
  5782.                 if protectionObj.level == 'sysop' then
  5783.                     ret = ret .. "This protection is '''not''' an endorsement of the"
  5784.                         .. ' ${CURRENTVERSION}. '
  5785.                 end
  5786.                 return ret .. '${EXPLANATIONBLURB}'
  5787.             end,
  5788.             tooltip = '${TOOLTIPFRAGMENT} due to vandalism',
  5789.         }
  5790.     },
  5791.     move = {
  5792.         dispute = {
  5793.             description = 'For pages protected against page moves due to'
  5794.                 .. ' disputes over the page title',
  5795.             explanation = "This protection is '''not''' an endorsement of the"
  5796.                 .. ' ${CURRENTVERSION}. ${EXPLANATIONBLURB}',
  5797.             image = 'Move-protection-shackle.svg'
  5798.         },
  5799.         vandalism = {
  5800.             description = 'For pages protected against'
  5801.                 .. ' [[Wikipedia:Vandalism#Page-move vandalism'
  5802.                 .. ' |page-move vandalism]]'
  5803.         }
  5804.     },
  5805.     autoreview = {},
  5806.     upload = {}
  5807. },
  5808.  
  5809. --------------------------------------------------------------------------------
  5810. --
  5811. --                            GENERAL DATA TABLES
  5812. --
  5813. --------------------------------------------------------------------------------
  5814.  
  5815. --------------------------------------------------------------------------------
  5816. -- Protection blurbs
  5817. --------------------------------------------------------------------------------
  5818.  
  5819. -- This table produces the protection blurbs available with the
  5820. -- ${PROTECTIONBLURB} parameter. It is sorted by protection action and
  5821. -- protection level, and is checked by the module in the following order:
  5822. -- 1. page's protection action, page's protection level
  5823. -- 2. page's protection action, default protection level
  5824. -- 3. "edit" protection action, default protection level
  5825. --
  5826. -- It is possible to use banner parameters inside this table.
  5827. -- *required* - this table needs edit, move, autoreview and upload subtables.
  5828. protectionBlurbs = {
  5829.     edit = {
  5830.         default = 'This ${PAGETYPE} is currently [[Help:Protection|'
  5831.             .. 'protected]] from editing',
  5832.         autoconfirmed = 'Editing of this ${PAGETYPE} by [[Wikipedia:User access'
  5833.             .. ' levels#New users|new]] or [[Wikipedia:User access levels#Unregistered'
  5834.             .. ' users|unregistered]] users is currently [[Help:Protection|disabled]]',
  5835.         extendedconfirmed = 'This ${PAGETYPE} is currently under extended confirmed protection',
  5836.     },
  5837.     move = {
  5838.         default = 'This ${PAGETYPE} is currently [[Help:Protection|protected]]'
  5839.             .. ' from [[Help:Moving a page|page moves]]'
  5840.     },
  5841.     autoreview = {
  5842.         default = 'All edits made to this ${PAGETYPE} by'
  5843.             .. ' [[Wikipedia:User access levels#New users|new]] or'
  5844.             .. ' [[Wikipedia:User access levels#Unregistered users|unregistered]]'
  5845.             .. ' users are currently'
  5846.             .. ' [[Wikipedia:Pending changes|subject to review]]'
  5847.     },
  5848.     upload = {
  5849.         default = 'Uploading new versions of this ${PAGETYPE} is currently disabled'
  5850.     }
  5851. },
  5852.  
  5853.  
  5854. --------------------------------------------------------------------------------
  5855. -- Explanation blurbs
  5856. --------------------------------------------------------------------------------
  5857.  
  5858. -- This table produces the explanation blurbs available with the
  5859. -- ${EXPLANATIONBLURB} parameter. It is sorted by protection action,
  5860. -- protection level, and whether the page is a talk page or not. If the page is
  5861. -- a talk page it will have a talk key of "talk"; otherwise it will have a talk
  5862. -- key of "subject". The table is checked in the following order:
  5863. -- 1. page's protection action, page's protection level, page's talk key
  5864. -- 2. page's protection action, page's protection level, default talk key
  5865. -- 3. page's protection action, default protection level, page's talk key
  5866. -- 4. page's protection action, default protection level, default talk key
  5867. --
  5868. -- It is possible to use banner parameters inside this table.
  5869. -- *required* - this table needs edit, move, autoreview and upload subtables.
  5870. explanationBlurbs = {
  5871.     edit = {
  5872.         autoconfirmed = {
  5873.             subject = 'See the [[Wikipedia:Protection policy|'
  5874.                 .. 'protection policy]] and ${PROTECTIONLOG} for more details. If you'
  5875.                 .. ' cannot edit this ${PAGETYPE} and you wish to make a change, you can'
  5876.                 .. ' ${EDITREQUEST}, discuss changes on the ${TALKPAGE},'
  5877.                 .. ' [[Wikipedia:Requests for page protection'
  5878.                 .. '#Current requests for reduction in protection level'
  5879.                 .. '|request unprotection]], [[Special:Userlogin|log in]], or'
  5880.                 .. ' [[Special:UserLogin/signup|create an account]].',
  5881.             default = 'See the [[Wikipedia:Protection policy|'
  5882.                 .. 'protection policy]] and ${PROTECTIONLOG} for more details. If you'
  5883.                 .. ' cannot edit this ${PAGETYPE} and you wish to make a change, you can'
  5884.                 .. ' [[Wikipedia:Requests for page protection'
  5885.                 .. '#Current requests for reduction in protection level'
  5886.                 .. '|request unprotection]], [[Special:Userlogin|log in]], or'
  5887.                 .. ' [[Special:UserLogin/signup|create an account]].',
  5888.         },
  5889.         extendedconfirmed = {
  5890.             default = 'Extended confirmed protection prevents edits from all unregistered editors'
  5891.                 .. ' and registered users with fewer than 30 days tenure and 500 edits.'
  5892.                 .. ' The [[Wikipedia:Protection policy#extended|policy on community use]]'
  5893.                 .. ' specifies that extended confirmed protection can be applied to combat'
  5894.                 .. ' disruption, if semi-protection has proven to be ineffective.'
  5895.                 .. ' Extended confirmed protection may also be applied to enforce'
  5896.                 .. ' [[Wikipedia:Arbitration Committee|arbitration sanctions]].'
  5897.                 .. ' Please discuss any changes on the ${TALKPAGE}; you may'
  5898.                 .. ' ${EDITREQUEST} to ask for uncontroversial changes supported by'
  5899.                 .. ' [[Wikipedia:Consensus|consensus]].'
  5900.         },
  5901.         default = {
  5902.             subject = 'See the [[Wikipedia:Protection policy|'
  5903.                 .. 'protection policy]] and ${PROTECTIONLOG} for more details.'
  5904.                 .. ' Please discuss any changes on the ${TALKPAGE}; you'
  5905.                 .. ' may ${EDITREQUEST} to ask an'
  5906.                 .. ' [[Wikipedia:Administrators|administrator]] to make an edit if it'
  5907.                 .. ' is [[Help:Minor edit#When to mark an edit as a minor edit'
  5908.                 .. '|uncontroversial]] or supported by [[Wikipedia:Consensus'
  5909.                 .. '|consensus]]. You may also [[Wikipedia:Requests for'
  5910.                 .. ' page protection#Current requests for reduction in protection level'
  5911.                 .. '|request]] that this page be unprotected.',
  5912.             default = 'See the [[Wikipedia:Protection policy|'
  5913.                 .. 'protection policy]] and ${PROTECTIONLOG} for more details.'
  5914.                 .. ' You may [[Wikipedia:Requests for page'
  5915.                 .. ' protection#Current requests for edits to a protected page|request an'
  5916.                 .. ' edit]] to this page, or [[Wikipedia:Requests for'
  5917.                 .. ' page protection#Current requests for reduction in protection level'
  5918.                 .. '|ask]] for it to be unprotected.'
  5919.         }
  5920.     },
  5921.     move = {
  5922.         default = {
  5923.             subject = 'See the [[Wikipedia:Protection policy|'
  5924.                 .. 'protection policy]] and ${PROTECTIONLOG} for more details.'
  5925.                 .. ' The page may still be edited but cannot be moved'
  5926.                 .. ' until unprotected. Please discuss any suggested moves on the'
  5927.                 .. ' ${TALKPAGE} or at [[Wikipedia:Requested moves]]. You can also'
  5928.                 .. ' [[Wikipedia:Requests for page protection|request]] that the page be'
  5929.                 .. ' unprotected.',
  5930.             default = 'See the [[Wikipedia:Protection policy|'
  5931.                 .. 'protection policy]] and ${PROTECTIONLOG} for more details.'
  5932.                 .. ' The page may still be edited but cannot be moved'
  5933.                 .. ' until unprotected. Please discuss any suggested moves at'
  5934.                 .. ' [[Wikipedia:Requested moves]]. You can also'
  5935.                 .. ' [[Wikipedia:Requests for page protection|request]] that the page be'
  5936.                 .. ' unprotected.'
  5937.         }
  5938.     },
  5939.     autoreview = {
  5940.         default = {
  5941.             default = 'See the [[Wikipedia:Protection policy|'
  5942.                 .. 'protection policy]] and ${PROTECTIONLOG} for more details.'
  5943.                 .. ' Edits to this ${PAGETYPE} by new and unregistered users'
  5944.                 .. ' will not be visible to readers until they are accepted by'
  5945.                 .. ' a reviewer. To avoid the need for your edits to be'
  5946.                 .. ' reviewed, you may'
  5947.                 .. ' [[Wikipedia:Requests for page protection'
  5948.                 .. '#Current requests for reduction in protection level'
  5949.                 .. '|request unprotection]], [[Special:Userlogin|log in]], or'
  5950.                 .. ' [[Special:UserLogin/signup|create an account]].'
  5951.         },
  5952.     },
  5953.     upload = {
  5954.         default = {
  5955.             default = 'See the [[Wikipedia:Protection policy|'
  5956.                 .. 'protection policy]] and ${PROTECTIONLOG} for more details.'
  5957.                 .. ' The page may still be edited but new versions of the file'
  5958.                 .. ' cannot be uploaded until it is unprotected. You can'
  5959.                 .. ' request that a new version be uploaded by using a'
  5960.                 .. ' [[Wikipedia:Edit requests|protected edit request]], or you'
  5961.                 .. ' can  [[Wikipedia:Requests for page protection|request]]'
  5962.                 .. ' that the file be unprotected.'
  5963.         }
  5964.     }
  5965. },
  5966.  
  5967. --------------------------------------------------------------------------------
  5968. -- Protection levels
  5969. --------------------------------------------------------------------------------
  5970.  
  5971. -- This table provides the data for the ${PROTECTIONLEVEL} parameter, which
  5972. -- produces a short label for different protection levels. It is sorted by
  5973. -- protection action and protection level, and is checked in the following
  5974. -- order:
  5975. -- 1. page's protection action, page's protection level
  5976. -- 2. page's protection action, default protection level
  5977. -- 3. "edit" protection action, default protection level
  5978. --
  5979. -- It is possible to use banner parameters inside this table.
  5980. -- *required* - this table needs edit, move, autoreview and upload subtables.
  5981. protectionLevels = {
  5982.     edit = {
  5983.         default = 'protected',
  5984.         templateeditor = 'template-protected',
  5985.         extendedconfirmed = 'extended-protected',
  5986.         autoconfirmed = 'semi-protected',
  5987.     },
  5988.     move = {
  5989.         default = 'move-protected'
  5990.     },
  5991.     autoreview = {
  5992.     },
  5993.     upload = {
  5994.         default = 'upload-protected'
  5995.     }
  5996. },
  5997.  
  5998. --------------------------------------------------------------------------------
  5999. -- Images
  6000. --------------------------------------------------------------------------------
  6001.  
  6002. -- This table lists different padlock images for each protection action and
  6003. -- protection level. It is used if an image is not specified in any of the
  6004. -- banner data tables, and if the page does not satisfy the conditions for using
  6005. -- the ['image-filename-indef'] image. It is checked in the following order:
  6006. -- 1. page's protection action, page's protection level
  6007. -- 2. page's protection action, default protection level
  6008. images = {
  6009.     edit = {
  6010.         default = 'Full-protection-shackle.svg',
  6011.         templateeditor = 'Template-protection-shackle.svg',
  6012.         extendedconfirmed = 'Extended-protection-shackle.svg',
  6013.         autoconfirmed = 'Semi-protection-shackle.svg'
  6014.     },
  6015.     move = {
  6016.         default = 'Move-protection-shackle.svg',
  6017.     },
  6018.     autoreview = {
  6019.         default = 'Pending-protection-shackle.svg'
  6020.     },
  6021.     upload = {
  6022.         default = 'Upload-protection-shackle.svg'
  6023.     }
  6024. },
  6025.  
  6026. -- Pages with a reason specified in this table will show the special "indef"
  6027. -- padlock, defined in the 'image-filename-indef' message, if no expiry is set.
  6028. indefImageReasons = {
  6029.     template = true
  6030. },
  6031.  
  6032. --------------------------------------------------------------------------------
  6033. -- Image links
  6034. --------------------------------------------------------------------------------
  6035.  
  6036. -- This table provides the data for the ${IMAGELINK} parameter, which gets
  6037. -- the image link for small padlock icons based on the page's protection action
  6038. -- and protection level. It is checked in the following order:
  6039. -- 1. page's protection action, page's protection level
  6040. -- 2. page's protection action, default protection level
  6041. -- 3. "edit" protection action, default protection level
  6042. --
  6043. -- It is possible to use banner parameters inside this table.
  6044. -- *required* - this table needs edit, move, autoreview and upload subtables.
  6045. imageLinks = {
  6046.     edit = {
  6047.         default = 'Wikipedia:Protection policy#full',
  6048.         templateeditor = 'Wikipedia:Protection policy#template',
  6049.         extendedconfirmed = 'Wikipedia:Protection policy#extended',
  6050.         autoconfirmed = 'Wikipedia:Protection policy#semi'
  6051.     },
  6052.     move = {
  6053.         default = 'Wikipedia:Protection policy#move'
  6054.     },
  6055.     autoreview = {
  6056.         default = 'Wikipedia:Protection policy#pending'
  6057.     },
  6058.     upload = {
  6059.         default = 'Wikipedia:Protection policy#upload'
  6060.     }
  6061. },
  6062.  
  6063. --------------------------------------------------------------------------------
  6064. -- Padlock indicator names
  6065. --------------------------------------------------------------------------------
  6066.  
  6067. -- This table provides the "name" attribute for the &lt;indicator&gt; extension tag
  6068. -- with which small padlock icons are generated. All indicator tags on a page
  6069. -- are displayed in alphabetical order based on this attribute, and with
  6070. -- indicator tags with duplicate names, the last tag on the page wins.
  6071. -- The attribute is chosen based on the protection action; table keys must be a
  6072. -- protection action name or the string "default".
  6073. padlockIndicatorNames = {
  6074.     autoreview = 'pp-autoreview',
  6075.     default = 'pp-default'
  6076. },
  6077.  
  6078. --------------------------------------------------------------------------------
  6079. -- Protection categories
  6080. --------------------------------------------------------------------------------
  6081.  
  6082. --[[
  6083. -- The protection categories are stored in the protectionCategories table.
  6084. -- Keys to this table are made up of the following strings:
  6085. --
  6086. -- 1. the expiry date
  6087. -- 2. the namespace
  6088. -- 3. the protection reason (e.g. "dispute" or "vandalism")
  6089. -- 4. the protection level (e.g. "sysop" or "autoconfirmed")
  6090. -- 5. the action (e.g. "edit" or "move")
  6091. --
  6092. -- When the module looks up a category in the table, first it will will check to
  6093. -- see a key exists that corresponds to all five parameters. For example, a
  6094. -- user page semi-protected from vandalism for two weeks would have the key
  6095. -- "temp-user-vandalism-autoconfirmed-edit". If no match is found, the module
  6096. -- changes the first part of the key to "all" and checks the table again. It
  6097. -- keeps checking increasingly generic key combinations until it finds the
  6098. -- field, or until it reaches the key "all-all-all-all-all".
  6099. --
  6100. -- The module uses a binary matrix to determine the order in which to search.
  6101. -- This is best demonstrated by a table. In this table, the "0" values
  6102. -- represent "all", and the "1" values represent the original data (e.g.
  6103. -- "indef" or "file" or "vandalism").
  6104. --
  6105. --        expiry    namespace reason   level     action
  6106. -- order
  6107. -- 1      1         1         1        1         1
  6108. -- 2      0         1         1        1         1
  6109. -- 3      1         0         1        1         1
  6110. -- 4      0         0         1        1         1
  6111. -- 5      1         1         0        1         1
  6112. -- 6      0         1         0        1         1
  6113. -- 7      1         0         0        1         1
  6114. -- 8      0         0         0        1         1
  6115. -- 9      1         1         1        0         1
  6116. -- 10     0         1         1        0         1
  6117. -- 11     1         0         1        0         1
  6118. -- 12     0         0         1        0         1
  6119. -- 13     1         1         0        0         1
  6120. -- 14     0         1         0        0         1
  6121. -- 15     1         0         0        0         1
  6122. -- 16     0         0         0        0         1
  6123. -- 17     1         1         1        1         0
  6124. -- 18     0         1         1        1         0
  6125. -- 19     1         0         1        1         0
  6126. -- 20     0         0         1        1         0
  6127. -- 21     1         1         0        1         0
  6128. -- 22     0         1         0        1         0
  6129. -- 23     1         0         0        1         0
  6130. -- 24     0         0         0        1         0
  6131. -- 25     1         1         1        0         0
  6132. -- 26     0         1         1        0         0
  6133. -- 27     1         0         1        0         0
  6134. -- 28     0         0         1        0         0
  6135. -- 29     1         1         0        0         0
  6136. -- 30     0         1         0        0         0
  6137. -- 31     1         0         0        0         0
  6138. -- 32     0         0         0        0         0
  6139. --
  6140. -- In this scheme the action has the highest priority, as it is the last
  6141. -- to change, and the expiry has the least priority, as it changes the most.
  6142. -- The priorities of the expiry, the protection level and the action are
  6143. -- fixed, but the priorities of the reason and the namespace can be swapped
  6144. -- through the use of the cfg.bannerDataNamespaceHasPriority table.
  6145. --]]
  6146.  
  6147. -- If the reason specified to the template is listed in this table,
  6148. -- namespace data will take priority over reason data in the protectionCategories
  6149. -- table.
  6150. reasonsWithNamespacePriority = {
  6151.     vandalism = true,
  6152. },
  6153.  
  6154. -- The string to use as a namespace key for the protectionCategories table for each
  6155. -- namespace number.
  6156. categoryNamespaceKeys = {
  6157.     [  2] = 'user',
  6158.     [  3] = 'user',
  6159.     [  4] = 'project',
  6160.     [  6] = 'file',
  6161.     [  8] = 'mediawiki',
  6162.     [ 10] = 'template',
  6163.     [ 12] = 'project',
  6164.     [ 14] = 'category',
  6165.     [100] = 'portal',
  6166.     [828] = 'module',
  6167. },
  6168.  
  6169. protectionCategories = {
  6170.     ['all|all|all|all|all']                     = 'Wikipedia fully protected pages',
  6171.     ['all|all|office|all|all']                  = 'Wikipedia Office-protected pages',
  6172.     ['all|all|reset|all|all']                   = 'Wikipedia Office-protected pages',
  6173.     ['all|all|dmca|all|all']                    = 'Wikipedia Office-protected pages',
  6174.     ['all|all|mainpage|all|all']                = 'Wikipedia fully-protected main page files',
  6175.     ['all|all|all|extendedconfirmed|all']       = 'Wikipedia extended-confirmed-protected pages',
  6176.     ['all|all|ecp|extendedconfirmed|all']       = 'Wikipedia extended-confirmed-protected pages',
  6177.     ['all|template|all|all|edit']               = 'Wikipedia fully protected templates',
  6178.     ['all|all|all|autoconfirmed|edit']          = 'Wikipedia semi-protected pages',
  6179.     ['indef|all|all|autoconfirmed|edit']        = 'Wikipedia indefinitely semi-protected pages',
  6180.     ['all|all|blp|autoconfirmed|edit']          = 'Wikipedia indefinitely semi-protected biographies of living people',
  6181.     ['temp|all|blp|autoconfirmed|edit']         = 'Wikipedia temporarily semi-protected biographies of living people',
  6182.     ['all|all|dispute|autoconfirmed|edit']      = 'Wikipedia pages semi-protected due to dispute',
  6183.     ['all|all|sock|autoconfirmed|edit']         = 'Wikipedia pages semi-protected from banned users',
  6184.     ['all|all|vandalism|autoconfirmed|edit']    = 'Wikipedia pages semi-protected against vandalism',
  6185.     ['all|category|all|autoconfirmed|edit']     = 'Wikipedia semi-protected categories',
  6186.     ['all|file|all|autoconfirmed|edit']         = 'Wikipedia semi-protected files',
  6187.     ['all|portal|all|autoconfirmed|edit']       = 'Wikipedia semi-protected portals',
  6188.     ['all|project|all|autoconfirmed|edit']      = 'Wikipedia semi-protected project pages',
  6189.     ['all|talk|all|autoconfirmed|edit']         = 'Wikipedia semi-protected talk pages',
  6190.     ['all|template|all|autoconfirmed|edit']     = 'Wikipedia semi-protected templates',
  6191.     ['all|user|all|autoconfirmed|edit']         = 'Wikipedia semi-protected user and user talk pages',
  6192.     ['all|all|all|templateeditor|edit']         = 'Wikipedia template-protected pages other than templates and modules',
  6193.     ['all|template|all|templateeditor|edit']    = 'Wikipedia template-protected templates',
  6194.     ['all|template|all|templateeditor|move']    = 'Wikipedia template-protected templates', -- move-protected templates
  6195.     ['all|all|blp|sysop|edit']                  = 'Wikipedia indefinitely protected biographies of living people',
  6196.     ['temp|all|blp|sysop|edit']                 = 'Wikipedia temporarily protected biographies of living people',
  6197.     ['all|all|dispute|sysop|edit']              = 'Wikipedia pages protected due to dispute',
  6198.     ['all|all|sock|sysop|edit']                 = 'Wikipedia pages protected from banned users',
  6199.     ['all|all|vandalism|sysop|edit']            = 'Wikipedia pages protected against vandalism',
  6200.     ['all|category|all|sysop|edit']             = 'Wikipedia fully protected categories',
  6201.     ['all|file|all|sysop|edit']                 = 'Wikipedia fully-protected files',
  6202.     ['all|project|all|sysop|edit']              = 'Wikipedia fully-protected project pages',
  6203.     ['all|talk|all|sysop|edit']                 = 'Wikipedia fully-protected talk pages',
  6204.     ['all|template|all|extendedconfirmed|edit'] = 'Wikipedia extended-confirmed-protected templates',
  6205.     ['all|template|all|sysop|edit']             = 'Wikipedia fully protected templates',
  6206.     ['all|user|all|sysop|edit']                 = 'Wikipedia fully protected user and user talk pages',
  6207.     ['all|module|all|all|edit']                 = 'Wikipedia fully-protected modules',
  6208.     ['all|module|all|templateeditor|edit']      = 'Wikipedia template-protected modules',
  6209.     ['all|module|all|extendedconfirmed|edit']   = 'Wikipedia extended-confirmed-protected modules',
  6210.     ['all|module|all|autoconfirmed|edit']       = 'Wikipedia semi-protected modules',
  6211.     ['all|all|all|sysop|move']                  = 'Wikipedia move-protected pages',
  6212.     ['indef|all|all|sysop|move']                = 'Wikipedia indefinitely move-protected pages',
  6213.     ['all|all|dispute|sysop|move']              = 'Wikipedia pages move-protected due to dispute',
  6214.     ['all|all|vandalism|sysop|move']            = 'Wikipedia pages move-protected due to vandalism',
  6215.     ['all|portal|all|sysop|move']               = 'Wikipedia move-protected portals',
  6216.     ['all|project|all|sysop|move']              = 'Wikipedia move-protected project pages',
  6217.     ['all|talk|all|sysop|move']                 = 'Wikipedia move-protected talk pages',
  6218.     ['all|template|all|sysop|move']             = 'Wikipedia move-protected templates',
  6219.     ['all|user|all|sysop|move']                 = 'Wikipedia move-protected user and user talk pages',
  6220.     ['all|all|all|autoconfirmed|autoreview']    = 'Wikipedia pending changes protected pages',
  6221.     ['all|file|all|all|upload']                 = 'Wikipedia upload-protected files',
  6222. },
  6223.  
  6224. --------------------------------------------------------------------------------
  6225. -- Expiry category config
  6226. --------------------------------------------------------------------------------
  6227.  
  6228. -- This table configures the expiry category behaviour for each protection
  6229. -- action.
  6230. -- * If set to true, setting that action will always categorise the page if
  6231. --   an expiry parameter is not set.
  6232. -- * If set to false, setting that action will never categorise the page.
  6233. -- * If set to nil, the module will categorise the page if:
  6234. --   1) an expiry parameter is not set, and
  6235. --   2) a reason is provided, and
  6236. --   3) the specified reason is not blacklisted in the reasonsWithoutExpiryCheck
  6237. --      table.
  6238.  
  6239. expiryCheckActions = {
  6240.     edit = nil,
  6241.     move = false,
  6242.     autoreview = true,
  6243.     upload = false
  6244. },
  6245.  
  6246. reasonsWithoutExpiryCheck = {
  6247.     blp = true,
  6248.     template = true,
  6249. },
  6250.  
  6251. --------------------------------------------------------------------------------
  6252. -- Pagetypes
  6253. --------------------------------------------------------------------------------
  6254.  
  6255. -- This table produces the page types available with the ${PAGETYPE} parameter.
  6256. -- Keys are namespace numbers, or the string "default" for the default value.
  6257. pagetypes = {
  6258.     [0] = 'article',
  6259.     [6] = 'file',
  6260.     [10] = 'template',
  6261.     [14] = 'category',
  6262.     [828] = 'module',
  6263.     default = 'page'
  6264. },
  6265.  
  6266. --------------------------------------------------------------------------------
  6267. -- Strings marking indefinite protection
  6268. --------------------------------------------------------------------------------
  6269.  
  6270. -- This table contains values passed to the expiry parameter that mean the page
  6271. -- is protected indefinitely.
  6272. indefStrings = {
  6273.     ['indef'] = true,
  6274.     ['indefinite'] = true,
  6275.     ['indefinitely'] = true,
  6276.     ['infinite'] = true,
  6277. },
  6278.  
  6279. --------------------------------------------------------------------------------
  6280. -- Group hierarchy
  6281. --------------------------------------------------------------------------------
  6282.  
  6283. -- This table maps each group to all groups that have a superset of the original
  6284. -- group's page editing permissions.
  6285. hierarchy = {
  6286.     sysop = {},
  6287.     reviewer = {'sysop'},
  6288.     filemover = {'sysop'},
  6289.     templateeditor = {'sysop'},
  6290.     extendedconfirmed = {'sysop'},
  6291.     autoconfirmed = {'reviewer', 'filemover', 'templateeditor', 'extendedconfirmed'},
  6292.     user = {'autoconfirmed'},
  6293.     ['*'] = {'user'}
  6294. },
  6295.  
  6296. --------------------------------------------------------------------------------
  6297. -- Wrapper templates and their default arguments
  6298. --------------------------------------------------------------------------------
  6299.  
  6300. -- This table contains wrapper templates used with the module, and their
  6301. -- default arguments. Templates specified in this table should contain the
  6302. -- following invocation, and no other template content:
  6303. --
  6304. -- {{#invoke:Protection banner|main}}
  6305. --
  6306. -- If other content is desired, it can be added between
  6307. -- &lt;noinclude&gt;...&lt;/noinclude&gt; tags.
  6308. --
  6309. -- When a user calls one of these wrapper templates, they will use the
  6310. -- default arguments automatically. However, users can override any of the
  6311. -- arguments.
  6312. wrappers = {
  6313.     ['Template:Pp']                         = {},
  6314.     ['Template:Pp-extended']                = {'ecp'},
  6315.     ['Template:Pp-blp']                     = {'blp'},
  6316.     -- we don't need Template:Pp-create
  6317.     ['Template:Pp-dispute']                 = {'dispute'},
  6318.     ['Template:Pp-main-page']               = {'mainpage'},
  6319.     ['Template:Pp-move']                    = {action = 'move', catonly = 'yes'},
  6320.     ['Template:Pp-move-dispute']            = {'dispute', action = 'move', catonly = 'yes'},
  6321.     -- we don't need Template:Pp-move-indef
  6322.     ['Template:Pp-move-vandalism']          = {'vandalism', action = 'move', catonly = 'yes'},
  6323.     ['Template:Pp-office']                  = {'office'},
  6324.     ['Template:Pp-office-dmca']             = {'dmca'},
  6325.     ['Template:Pp-pc']                      = {action = 'autoreview', small = true},
  6326.     ['Template:Pp-pc1']                     = {action = 'autoreview', small = true},
  6327.     ['Template:Pp-reset']                   = {'reset'},
  6328.     ['Template:Pp-semi-indef']              = {small = true},
  6329.     ['Template:Pp-sock']                    = {'sock'},
  6330.     ['Template:Pp-template']                = {'template', small = true},
  6331.     ['Template:Pp-upload']                  = {action = 'upload'},
  6332.     ['Template:Pp-usertalk']                = {'usertalk'},
  6333.     ['Template:Pp-vandalism']               = {'vandalism'},
  6334. },
  6335.  
  6336. --------------------------------------------------------------------------------
  6337. --
  6338. --                                 MESSAGES
  6339. --
  6340. --------------------------------------------------------------------------------
  6341.  
  6342. msg = {
  6343.  
  6344. --------------------------------------------------------------------------------
  6345. -- Intro blurb and intro fragment
  6346. --------------------------------------------------------------------------------
  6347.  
  6348. -- These messages specify what is produced by the ${INTROBLURB} and
  6349. -- ${INTROFRAGMENT} parameters. If the protection is temporary they use the
  6350. -- intro-blurb-expiry or intro-fragment-expiry, and if not they use
  6351. -- intro-blurb-noexpiry or intro-fragment-noexpiry.
  6352. -- It is possible to use banner parameters in these messages.
  6353. ['intro-blurb-expiry'] = '${PROTECTIONBLURB} until ${EXPIRY}.',
  6354. ['intro-blurb-noexpiry'] = '${PROTECTIONBLURB}.',
  6355. ['intro-fragment-expiry'] = '${PROTECTIONBLURB} until ${EXPIRY},',
  6356. ['intro-fragment-noexpiry'] = '${PROTECTIONBLURB}',
  6357.  
  6358. --------------------------------------------------------------------------------
  6359. -- Tooltip blurb
  6360. --------------------------------------------------------------------------------
  6361.  
  6362. -- These messages specify what is produced by the ${TOOLTIPBLURB} parameter.
  6363. -- If the protection is temporary the tooltip-blurb-expiry message is used, and
  6364. -- if not the tooltip-blurb-noexpiry message is used.
  6365. -- It is possible to use banner parameters in these messages.
  6366. ['tooltip-blurb-expiry'] = 'This ${PAGETYPE} is ${PROTECTIONLEVEL} until ${EXPIRY}.',
  6367. ['tooltip-blurb-noexpiry'] = 'This ${PAGETYPE} is ${PROTECTIONLEVEL}.',
  6368. ['tooltip-fragment-expiry'] = 'This ${PAGETYPE} is ${PROTECTIONLEVEL} until ${EXPIRY},',
  6369. ['tooltip-fragment-noexpiry'] = 'This ${PAGETYPE} is ${PROTECTIONLEVEL}',
  6370.  
  6371. --------------------------------------------------------------------------------
  6372. -- Special explanation blurb
  6373. --------------------------------------------------------------------------------
  6374.  
  6375. -- An explanation blurb for pages that cannot be unprotected, e.g. for pages
  6376. -- in the MediaWiki namespace.
  6377. -- It is possible to use banner parameters in this message.
  6378. ['explanation-blurb-nounprotect'] = 'See the [[Wikipedia:Protection policy|'
  6379.     .. 'protection policy]] and ${PROTECTIONLOG} for more details.'
  6380.     .. ' Please discuss any changes on the ${TALKPAGE}; you'
  6381.     .. ' may ${EDITREQUEST} to ask an'
  6382.     .. ' [[Wikipedia:Administrators|administrator]] to make an edit if it'
  6383.     .. ' is [[Help:Minor edit#When to mark an edit as a minor edit'
  6384.     .. '|uncontroversial]] or supported by [[Wikipedia:Consensus'
  6385.     .. '|consensus]].',
  6386.  
  6387. --------------------------------------------------------------------------------
  6388. -- Protection log display values
  6389. --------------------------------------------------------------------------------
  6390.  
  6391. -- These messages determine the display values for the protection log link
  6392. -- or the pending changes log link produced by the ${PROTECTIONLOG} parameter.
  6393. -- It is possible to use banner parameters in these messages.
  6394. ['protection-log-display'] = 'protection log',
  6395. ['pc-log-display'] = 'pending changes log',
  6396.  
  6397. --------------------------------------------------------------------------------
  6398. -- Current version display values
  6399. --------------------------------------------------------------------------------
  6400.  
  6401. -- These messages determine the display values for the page history link
  6402. -- or the move log link produced by the ${CURRENTVERSION} parameter.
  6403. -- It is possible to use banner parameters in these messages.
  6404. ['current-version-move-display'] = 'current title',
  6405. ['current-version-edit-display'] = 'current version',
  6406.  
  6407. --------------------------------------------------------------------------------
  6408. -- Talk page
  6409. --------------------------------------------------------------------------------
  6410.  
  6411. -- This message determines the display value of the talk page link produced
  6412. -- with the ${TALKPAGE} parameter.
  6413. -- It is possible to use banner parameters in this message.
  6414. ['talk-page-link-display'] = 'talk page',
  6415.  
  6416. --------------------------------------------------------------------------------
  6417. -- Edit requests
  6418. --------------------------------------------------------------------------------
  6419.  
  6420. -- This message determines the display value of the edit request link produced
  6421. -- with the ${EDITREQUEST} parameter.
  6422. -- It is possible to use banner parameters in this message.
  6423. ['edit-request-display'] = 'submit an edit request',
  6424.  
  6425. --------------------------------------------------------------------------------
  6426. -- Expiry date format
  6427. --------------------------------------------------------------------------------
  6428.  
  6429. -- This is the format for the blurb expiry date. It should be valid input for
  6430. -- the first parameter of the #time parser function.
  6431. ['expiry-date-format'] = 'F j, Y "at" H:i e',
  6432.  
  6433. --------------------------------------------------------------------------------
  6434. -- Tracking categories
  6435. --------------------------------------------------------------------------------
  6436.  
  6437. -- These messages determine which tracking categories the module outputs.
  6438. ['tracking-category-incorrect'] = 'Wikipedia pages with incorrect protection templates',
  6439. ['tracking-category-template'] = 'Wikipedia template-protected pages other than templates and modules',
  6440.  
  6441. --------------------------------------------------------------------------------
  6442. -- Images
  6443. --------------------------------------------------------------------------------
  6444.  
  6445. -- These are images that are not defined by their protection action and protection level.
  6446. ['image-filename-indef'] = 'Full-protection-shackle.svg',
  6447. ['image-filename-default'] = 'Transparent.gif',
  6448.  
  6449. --------------------------------------------------------------------------------
  6450. -- End messages
  6451. --------------------------------------------------------------------------------
  6452. }
  6453.  
  6454. --------------------------------------------------------------------------------
  6455. -- End configuration
  6456. --------------------------------------------------------------------------------
  6457. }</text>
  6458.       <sha1>eeb6sgpzkp24z3uln44ko8rah6nhotf</sha1>
  6459.     </revision>
  6460.   </page>
  6461.   <page>
  6462.     <title>Module:Transclusion count</title>
  6463.     <ns>828</ns>
  6464.     <id>61792794</id>
  6465.     <revision>
  6466.       <id>1061635767</id>
  6467.       <parentid>1057474521</parentid>
  6468.       <timestamp>2021-12-22T22:48:57Z</timestamp>
  6469.       <contributor>
  6470.         <username>GKFX</username>
  6471.         <id>14918984</id>
  6472.       </contributor>
  6473.       <comment>Correction to tabulate function</comment>
  6474.       <model>Scribunto</model>
  6475.       <format>text/plain</format>
  6476.       <text bytes="2174" xml:space="preserve">local p = {}
  6477.  
  6478. function p.fetch(frame)
  6479.     local template = nil
  6480.     local return_value = nil
  6481.  
  6482.     -- Use demo parameter if it exists, otherswise use current template name
  6483.     local namespace = mw.title.getCurrentTitle().namespace
  6484.     if frame.args["demo"] and frame.args["demo"] ~= "" then
  6485.         template = frame.args["demo"]
  6486.     elseif namespace == 10 then -- Template namespace
  6487.         template = mw.title.getCurrentTitle().text
  6488.     elseif namespace == 828 then -- Module namespace
  6489.         template = (mw.site.namespaces[828].name .. ":" .. mw.title.getCurrentTitle().text)
  6490.     end
  6491.  
  6492.     -- If in template or module namespace, look up count in /data
  6493.     if template ~= nil then
  6494.         namespace = mw.title.new(template, "Template").namespace
  6495.         if namespace == 10 or namespace == 828 then
  6496.             template =  mw.ustring.gsub(template, "/doc$", "") -- strip /doc from end
  6497.             local index = mw.ustring.sub(mw.title.new(template).text,1,1)
  6498.             local status, data = pcall(function ()
  6499.                 return(mw.loadData('Module:Transclusion_count/data/' .. (mw.ustring.find(index, "%a") and index or "other")))
  6500.             end)
  6501.             if status then
  6502.                 return_value = tonumber(data[mw.ustring.gsub(template, " ", "_")])
  6503.             end
  6504.         end
  6505.     end
  6506.    
  6507.     -- If database value doesn't exist, use value passed to template
  6508.     if return_value == nil and frame.args[1] ~= nil then
  6509.         local arg1=mw.ustring.match(frame.args[1], '[%d,]+')
  6510.         if arg1 and arg1 ~= '' then
  6511.             return_value = tonumber(frame:callParserFunction('formatnum', arg1, 'R'))
  6512.         end
  6513.     end
  6514.    
  6515.     return return_value
  6516. end
  6517.  
  6518. -- Tabulate this data for [[Wikipedia:Database reports/Templates transcluded on the most pages]]
  6519. function p.tabulate(frame)
  6520.     local list = {}
  6521.     for i = 65, 91 do
  6522.         local data = mw.loadData('Module:Transclusion count/data/' .. ((i == 91) and 'other' or string.char(i)))
  6523.         for name, count in pairs(data) do
  6524.             table.insert(list, {mw.title.new(name, "Template").fullText, count})   
  6525.         end
  6526.     end
  6527.     table.sort(list, function(a, b)
  6528.         return (a[2] == b[2]) and (a[1] &lt; b[1]) or (a[2] &gt; b[2])
  6529.     end)
  6530.     local lang = mw.getContentLanguage();
  6531.     for i = 1, #list do
  6532.         list[i] = ('|-\n| %d || [[%s]] || %s\n'):format(i, list[i][1]:gsub('_', ' '), lang:formatNum(list[i][2]))
  6533.     end
  6534.     return table.concat(list)
  6535. end
  6536.  
  6537. return p</text>
  6538.       <sha1>i4hf4ogs2jqprtn91iinnikcxeyynd9</sha1>
  6539.     </revision>
  6540.   </page>
  6541.   <page>
  6542.     <title>Module:Transclusion count/data/N</title>
  6543.     <ns>828</ns>
  6544.     <id>61481068</id>
  6545.     <revision>
  6546.       <id>1071545058</id>
  6547.       <parentid>1070187547</parentid>
  6548.       <timestamp>2022-02-13T05:15:36Z</timestamp>
  6549.       <contributor>
  6550.         <username>Ahechtbot</username>
  6551.         <id>26109785</id>
  6552.       </contributor>
  6553.       <minor/>
  6554.       <comment>[[Wikipedia:BOT|Bot]]: Updated page.</comment>
  6555.       <model>Scribunto</model>
  6556.       <format>text/plain</format>
  6557.       <text bytes="7724" xml:space="preserve">return {
  6558. ["N"] = 11000,
  6559. ["N/A"] = 20000,
  6560. ["N/a"] = 45000,
  6561. ["NA-Class"] = 14000,
  6562. ["NASTRO_comment"] = 29000,
  6563. ["NBA_Draft_template_list"] = 3200,
  6564. ["NBA_Year"] = 5400,
  6565. ["NBA_color"] = 3600,
  6566. ["NBA_color_cell"] = 4300,
  6567. ["NBA_color_cell2"] = 3400,
  6568. ["NBA_player_statistics_legend"] = 3700,
  6569. ["NBA_player_statistics_start"] = 3300,
  6570. ["NBbox"] = 3400,
  6571. ["NCAA_color_cell"] = 8000,
  6572. ["NCAA_secondary_color_cell"] = 3800,
  6573. ["NED"] = 9500,
  6574. ["NEXTYEAR"] = 6300,
  6575. ["NFLAltPrimaryColor"] = 2100,
  6576. ["NFLAltPrimaryStyle"] = 5700,
  6577. ["NFLAltSecondaryColor"] = 2400,
  6578. ["NFLPrimaryColor"] = 2100,
  6579. ["NFLPrimaryStyle"] = 27000,
  6580. ["NFLSecondaryColor"] = 18000,
  6581. ["NFL_Roster_navbox_template_list"] = 2500,
  6582. ["NFL_Year"] = 21000,
  6583. ["NFL_predraft"] = 4200,
  6584. ["NFL_predraft/check"] = 4200,
  6585. ["NFL_predraft/ftin"] = 4200,
  6586. ["NFT"] = 6200,
  6587. ["NFT_player"] = 30000,
  6588. ["NGA"] = 2900,
  6589. ["NGR"] = 2100,
  6590. ["NHLE"] = 16000,
  6591. ["NHLS_url"] = 2200,
  6592. ["NLD"] = 11000,
  6593. ["NLM_content"] = 2800,
  6594. ["NOINDEX"] = 418000,
  6595. ["NOMIS2011"] = 2200,
  6596. ["NOR"] = 8900,
  6597. ["NRDB_species"] = 5900,
  6598. ["NRHP-PA"] = 2600,
  6599. ["NRHPGoogleMapFootnote"] = 2500,
  6600. ["NRHP_Focus"] = 75000,
  6601. ["NRHP_color"] = 3700,
  6602. ["NRHP_date_for_lists"] = 3100,
  6603. ["NRHP_date_for_lists/dates"] = 3100,
  6604. ["NRHP_header"] = 3300,
  6605. ["NRHP_navigation_box"] = 8000,
  6606. ["NRHP_row"] = 3400,
  6607. ["NRHP_style"] = 69000,
  6608. ["NRHP_url"] = 28000,
  6609. ["NRHP_url/core"] = 30000,
  6610. ["NRISref"] = 72000,
  6611. ["NSWcity"] = 2200,
  6612. ["NSWcity/core"] = 2200,
  6613. ["NUMBEROFSECTIONS"] = 3300,
  6614. ["NYCS_br"] = 2700,
  6615. ["NYCS_time_2"] = 3100,
  6616. ["NZL"] = 6700,
  6617. ["NZ_electorate_link"] = 2200,
  6618. ["Nac"] = 2200,
  6619. ["Namespace_detect"] = 522000,
  6620. ["Namespace_detect_showall"] = 5300,
  6621. ["Nastaliq"] = 9300,
  6622. ["Nat_fs_end"] = 2200,
  6623. ["National_Heritage_List_for_England"] = 20000,
  6624. ["National_Rail_style"] = 5500,
  6625. ["National_Register_of_Historic_Places"] = 29000,
  6626. ["National_Register_of_Historic_Places_in_Massachusetts"] = 4300,
  6627. ["National_Register_of_Historic_Places_in_New_York"] = 5500,
  6628. ["National_Register_of_Historic_Places_in_North_Carolina"] = 2900,
  6629. ["National_Register_of_Historic_Places_in_Pennsylvania"] = 3200,
  6630. ["National_Register_of_Historic_Places_in_Virginia"] = 3100,
  6631. ["National_basketball_squad"] = 3300,
  6632. ["National_field_hockey_squad"] = 3800,
  6633. ["National_football_squad_end"] = 2800,
  6634. ["National_football_squad_start/styles.css"] = 2300,
  6635. ["National_rugby_union_team"] = 3400,
  6636. ["National_squad"] = 39000,
  6637. ["National_squad_no_numbers"] = 16000,
  6638. ["Native_name"] = 13000,
  6639. ["Native_name_checker"] = 76000,
  6640. ["Naval"] = 2100,
  6641. ["Naval/core"] = 2100,
  6642. ["Navbar"] = 939000,
  6643. ["Navbar-collapsible"] = 3400,
  6644. ["Navbar-header"] = 15000,
  6645. ["Navbar-header/styles.css"] = 15000,
  6646. ["Navbox"] = 2940000,
  6647. ["Navbox_GR_munic"] = 3400,
  6648. ["Navbox_Musical_artist"] = 9600,
  6649. ["Navbox_bottom"] = 14000,
  6650. ["Navbox_decade_list"] = 11000,
  6651. ["Navbox_musical_artist"] = 212000,
  6652. ["Navbox_musical_artist/color"] = 212000,
  6653. ["Navbox_rugby_league_squad"] = 5100,
  6654. ["Navbox_season_by_team"] = 3100,
  6655. ["Navbox_top"] = 14000,
  6656. ["Navbox_with_collapsible_groups"] = 130000,
  6657. ["Navbox_with_collapsible_sections"] = 2200,
  6658. ["Navbox_with_columns"] = 23000,
  6659. ["Navboxes"] = 50000,
  6660. ["Navboxes_colour"] = 7200,
  6661. ["Navseasoncats"] = 446000,
  6662. ["Navseasoncats_with_centuries_below_decade"] = 6100,
  6663. ["Navseasoncats_with_decades_below_year"] = 19000,
  6664. ["Navsource"] = 2500,
  6665. ["Navy"] = 18000,
  6666. ["Navy/core"] = 18000,
  6667. ["Nay"] = 13000,
  6668. ["Nbay"] = 5400,
  6669. ["Nbsp"] = 445000,
  6670. ["Nbsp_to_space"] = 17000,
  6671. ["Ndash"] = 17000,
  6672. ["Nee"] = 5300,
  6673. ["NeilBrownPlayers"] = 3100,
  6674. ["New_York_Stock_Exchange"] = 2000,
  6675. ["New_user_bar"] = 57000,
  6676. ["Newbot"] = 4100,
  6677. ["Next_period"] = 19000,
  6678. ["Nfly"] = 2000,
  6679. ["Nihongo"] = 98000,
  6680. ["Nihongo2"] = 12000,
  6681. ["Nihongo_foot"] = 2200,
  6682. ["No"] = 7100,
  6683. ["No."] = 14000,
  6684. ["No2"] = 9100,
  6685. ["NoSpam"] = 4100,
  6686. ["NoSpamEmail"] = 17000,
  6687. ["No_col_break"] = 2100,
  6688. ["No_col_break/styles.css"] = 2100,
  6689. ["No_footnotes"] = 52000,
  6690. ["No_ping"] = 434000,
  6691. ["No_plot"] = 4100,
  6692. ["No_redirect"] = 522000,
  6693. ["No_spam"] = 241000,
  6694. ["No_wrap"] = 2100,
  6695. ["Nobold"] = 360000,
  6696. ["Nobold/styles.css"] = 360000,
  6697. ["Nobr"] = 4000,
  6698. ["Nobreak"] = 28000,
  6699. ["Nochange"] = 2300,
  6700. ["Noflag"] = 2100,
  6701. ["Nofootnotes"] = 2100,
  6702. ["Noindex"] = 4700,
  6703. ["Noitalic"] = 4500,
  6704. ["Noitalic/styles.css"] = 4500,
  6705. ["Nom"] = 33000,
  6706. ["Nominated"] = 5900,
  6707. ["Non-admin_closure"] = 2300,
  6708. ["Non-diffusing_subcategory"] = 12000,
  6709. ["Non-free_2D_art"] = 6300,
  6710. ["Non-free_3D_art"] = 2100,
  6711. ["Non-free_album_cover"] = 192000,
  6712. ["Non-free_audio_sample"] = 8600,
  6713. ["Non-free_biog-pic"] = 21000,
  6714. ["Non-free_book_cover"] = 52000,
  6715. ["Non-free_comic"] = 11000,
  6716. ["Non-free_fair_use"] = 18000,
  6717. ["Non-free_fair_use_in"] = 15000,
  6718. ["Non-free_film_screenshot"] = 5100,
  6719. ["Non-free_game_cover"] = 6500,
  6720. ["Non-free_game_screenshot"] = 4100,
  6721. ["Non-free_historic_image"] = 16000,
  6722. ["Non-free_image_data"] = 8300,
  6723. ["Non-free_image_rationale"] = 9500,
  6724. ["Non-free_in_US"] = 2100,
  6725. ["Non-free_logo"] = 159000,
  6726. ["Non-free_magazine_cover"] = 7400,
  6727. ["Non-free_media"] = 698000,
  6728. ["Non-free_media_data"] = 9400,
  6729. ["Non-free_media_rationale"] = 11000,
  6730. ["Non-free_movie_poster"] = 19000,
  6731. ["Non-free_poster"] = 96000,
  6732. ["Non-free_promotional"] = 9000,
  6733. ["Non-free_school_logo"] = 2300,
  6734. ["Non-free_seal"] = 3500,
  6735. ["Non-free_symbol"] = 6200,
  6736. ["Non-free_television_screenshot"] = 16000,
  6737. ["Non-free_title-card"] = 4700,
  6738. ["Non-free_use_rationale"] = 449000,
  6739. ["Non-free_use_rationale_2"] = 190000,
  6740. ["Non-free_use_rationale_album_cover"] = 127000,
  6741. ["Non-free_use_rationale_book_cover"] = 17000,
  6742. ["Non-free_use_rationale_logo"] = 97000,
  6743. ["Non-free_use_rationale_poster"] = 47000,
  6744. ["Non-free_use_rationale_title-card"] = 2100,
  6745. ["Non-free_use_rationale_video_cover"] = 5600,
  6746. ["Non-free_use_rationale_video_game_cover"] = 9500,
  6747. ["Non-free_video_cover"] = 16000,
  6748. ["Non-free_video_game_cover"] = 20000,
  6749. ["Non-free_video_game_cover/platform"] = 20000,
  6750. ["Non-free_video_game_screenshot"] = 9800,
  6751. ["Non-free_video_game_screenshot/platform"] = 9100,
  6752. ["Non-free_with_permission"] = 2200,
  6753. ["Nonumtoc"] = 2200,
  6754. ["Nonumtoc/styles.css"] = 2200,
  6755. ["Noping"] = 359000,
  6756. ["Noprint"] = 2800,
  6757. ["Noredirect"] = 7900,
  6758. ["Normalwraplink"] = 3100,
  6759. ["NorthAmNative"] = 2600,
  6760. ["North_America_topic"] = 3700,
  6761. ["Northern_Ireland_tasks"] = 19000,
  6762. ["Nospam"] = 13000,
  6763. ["Not-PD-US-expired-min-year"] = 26000,
  6764. ["Not_a_ballot"] = 7400,
  6765. ["Not_a_forum"] = 7300,
  6766. ["Not_a_typo"] = 8200,
  6767. ["Not_around"] = 2700,
  6768. ["Not_done"] = 18000,
  6769. ["Notability"] = 59000,
  6770. ["Notavote"] = 2000,
  6771. ["Notdone"] = 5200,
  6772. ["Note"] = 25000,
  6773. ["NoteFoot"] = 3000,
  6774. ["NoteTag"] = 3000,
  6775. ["Note_label"] = 8600,
  6776. ["Notelist"] = 140000,
  6777. ["Notelist-lr"] = 3000,
  6778. ["Notelist-ua"] = 5500,
  6779. ["Notes"] = 6400,
  6780. ["Noteslist"] = 2800,
  6781. ["Notice"] = 104000,
  6782. ["NovelsWikiProject"] = 19000,
  6783. ["Nowiki_="] = 3800,
  6784. ["Nowrap"] = 1390000,
  6785. ["Npby"] = 2100,
  6786. ["Nq"] = 2800,
  6787. ["Nrut"] = 3400,
  6788. ["Ns"] = 2300,
  6789. ["Ns0"] = 328000,
  6790. ["Ns_has_subpages"] = 1820000,
  6791. ["Nts"] = 15000,
  6792. ["Ntsh"] = 3000,
  6793. ["Number_of_defined_parameters"] = 4400,
  6794. ["Number_table_sorting"] = 27000,
  6795. ["Number_table_sorting_hidden"] = 3000,
  6796. ["Nutshell"] = 3700,
  6797. ["Nw="] = 3800,
  6798. ["Module:NUMBEROFSECTIONS"] = 3300,
  6799. ["Module:Namespace_detect"] = 14300000,
  6800. ["Module:Namespace_detect/config"] = 15600000,
  6801. ["Module:Namespace_detect/data"] = 15600000,
  6802. ["Module:National_squad"] = 52000,
  6803. ["Module:Native_name"] = 85000,
  6804. ["Module:Navbar"] = 6090000,
  6805. ["Module:Navbar/configuration"] = 5410000,
  6806. ["Module:Navbar/styles.css"] = 4790000,
  6807. ["Module:Navbox"] = 4250000,
  6808. ["Module:Navbox/configuration"] = 4120000,
  6809. ["Module:Navbox/styles.css"] = 4120000,
  6810. ["Module:Navbox_top_and_bottom"] = 14000,
  6811. ["Module:Navbox_with_collapsible_groups"] = 135000,
  6812. ["Module:Navboxes"] = 51000,
  6813. ["Module:Navseasoncats"] = 446000,
  6814. ["Module:Nihongo"] = 100000,
  6815. ["Module:No_globals"] = 18000000,
  6816. ["Module:No_ping"] = 434000,
  6817. ["Module:Noinclude"] = 368000,
  6818. ["Module:Ns_has_subpages"] = 1820000,
  6819. ["Module:Number_table_sorting"] = 29000,
  6820. }</text>
  6821.       <sha1>iy6voi40ghymue0cyb4o5zma8u59cvh</sha1>
  6822.     </revision>
  6823.   </page>
  6824.   <page>
  6825.     <title>Module:Yesno</title>
  6826.     <ns>828</ns>
  6827.     <id>38665046</id>
  6828.     <revision>
  6829.       <id>948473803</id>
  6830.       <parentid>948472535</parentid>
  6831.       <timestamp>2020-04-01T06:27:55Z</timestamp>
  6832.       <contributor>
  6833.         <username>MusikAnimal</username>
  6834.         <id>14882394</id>
  6835.       </contributor>
  6836.       <comment>Undid revision 948472533 by [[Special:Contributions/w&gt;Vogone|w&gt;Vogone]] ([[User talk:w&gt;Vogone|talk]])</comment>
  6837.       <model>Scribunto</model>
  6838.       <format>text/plain</format>
  6839.       <text bytes="745" xml:space="preserve">-- Function allowing for consistent treatment of boolean-like wikitext input.
  6840. -- It works similarly to the template {{yesno}}.
  6841.  
  6842. return function (val, default)
  6843.     -- If your wiki uses non-ascii characters for any of "yes", "no", etc., you
  6844.     -- should replace "val:lower()" with "mw.ustring.lower(val)" in the
  6845.     -- following line.
  6846.     val = type(val) == 'string' and val:lower() or val
  6847.     if val == nil then
  6848.         return nil
  6849.     elseif val == true
  6850.         or val == 'yes'
  6851.         or val == 'y'
  6852.         or val == 'true'
  6853.         or val == 't'
  6854.         or val == 'on'
  6855.         or tonumber(val) == 1
  6856.     then
  6857.         return true
  6858.     elseif val == false
  6859.         or val == 'no'
  6860.         or val == 'n'
  6861.         or val == 'false'
  6862.         or val == 'f'
  6863.         or val == 'off'
  6864.         or tonumber(val) == 0
  6865.     then
  6866.         return false
  6867.     else
  6868.         return default
  6869.     end
  6870. end</text>
  6871.       <sha1>swdskn7svew8i9wuydn9uj5l3r2ghcs</sha1>
  6872.     </revision>
  6873.   </page>
  6874. </mediawiki>
Add Comment
Please, Sign In to add comment