Module:Section sizes explained

require('strict');

----------------------------< I 1 8 N _ T >------------------------------------------------------------------

An associateive table of static text used in this module for use when translating this module to other languages.

The values $1 and $2 are replaced with values as stated in the associated message comment

local i18n_t =

----------------------------< E R R O R _ M S G _ M A K E >--------------------------------------------------

common funtion to emit both fatal and non-fatal error messages

template_name used in error message help text link and error category link fetched from MediaWiki

error category emitted only for transclusions in the Talk namespace; only when i18n_t['error category'] has avalue; only when we have not already emitted the category link.

local err_cat_added; -- page-scope boolean flag; true when error cat link has been emmitted; nil elselocal function error_msg_make (template_name, msg, args_t, fatal) local err_cat = ; -- empty string for concatenation

if not err_cat_added and i18n_t['error category'] and 1

mw.title.getCurrentTitle.namespace then err_cat = mw.message.newRawMessage (i18n_t['error category'],):plain; -- create error category wikilink err_cat_added = true; -- we will do this only once end

local err_msg = table.concat ; return err_msg;end

--

local function anchors_remove (section_name) local patterns =

for _, pattern in ipairs (patterns) do section_name = section_name:gsub (pattern, ); -- remove all anchor spans end return section_name;end

----------------------------< R E F S _ R E M O V E >--------------------------------------------------------

remove wikitext reference markup. done this way because we later preprocess the section name to render any templatesthat are present in the section name (there shouldn't be but that doesn't stop editors from including them).preprocessing a section name with reference markup causes MediaWiki to create a reflist; a side effect that wedon't want.

returns modified section name and boolean true when references have been removed; unmodified section name and false else.

local function refs_remove (section_name) local name; -- modified (or unmodified) section name local markup_removed; -- boolean true when reference markup has been removed local count; name, count = section_name:gsub ('', ); -- remove self-closed tags markup_removed = 0 < count; -- count not zero, set true name, count = name:gsub ('[1] ', ); -- remove remaining ref tags and content (if any)

return name, markup_removed or (0 < count)end

----------------------------< S T R I P M A R K E R S _ R E M O V E >----------------------------------------

remove stripmarkers from preprocessed section names. it may be best to preserve tags before section nameis preprocessed to prevent ' from being interpreted as bold markup. It is not possible to do thathere because all nowiki strip markers are only identifiable by the numbers.

returns modified section name and boolean true when stripmarkers have been removed; unmodified section name and false else.

local function stripmarkers_remove (section_name) local count; section_name, count = section_name:gsub ('\127[^\127]*UNIQ%-%-%a+%-[%x]+%-QINU[^\127]*\127', ); return section_name, (0 < count);end

--[=[-------------------------< R E M O V E _ W I K I _ L I N K >---------------------------------------------- Gets the display text from a wikilink like [[A|B]] or B gives B

The str:gsub returns either A|B from a B or B from B or B from B (no wikilink markup).

In l, l:gsub removes the link and pipe (if they exist); the second :gsub trims white space from the labelif str was wrapped in wikilink markup. Presumably, this is because without wikimarkup in str, there is no matchin the initial gsub, the replacement function l doesn't get called.

]=]

local function remove_wiki_link (str) return (str:gsub("%[%[([^%[%]]*)%]%]", function(l) return l:gsub("^[^|]*|(.*)$", "%1"):gsub("^%s*(.-)%s*$", "%1"); end));end

----------------------------< R E M O V E _ C O N T A I N E R >----------------------------------------------

Inspired from above, removes everything between < & >Used to remove html containers from headings to fix breaking section links, but legitimate text within < & > are removed too

returns text and boolean true if modified; text and boolean false else

local function remove_container (str) local count; str, count = str:gsub("<([^>]*)>", function(l) return l:gsub("^%s*(.-)%s*$", ""); end); return str, 0 < countend

--[=[-------------------------< M A K E _ W I K I L I N K >---------------------------------------------------- Makes a wikilink; when both link and display text is provided, returns a wikilink in the form [[L|D]]; if onlylink is provided, returns a wikilink in the form L; if neither are provided or link is omitted, returns anempty string.

]=]

local function make_wikilink (link, display) if link and ( ~= link) then if display and ( ~= display) then return table.concat ; else return table.concat ; end end return display or ; -- link not set so return the display textend

--taille|}}

local function size (frame) local template_name = frame:getParent:getTitle; -- get template name for use in error messaging and category name

local section_info_t = ; -- table to hold section names and sizes local section_name_list = -- an interim list that holds just the section names local section_content; -- section content used for counting local section = i18n_t.top or -- i18n_t.top rarely needed mw.message.new ('vector-toc-beginning') -- lead section doesn't have a heading, get the interface message for 'top' :inLanguage (mw.language.getContentLanguage) -- in the current wiki's content language :plain; -- and make sure we have a string local count = ; -- number of bytes in a section including the header text local totcount = ; local lastlevel; local maxlevels; local levelcounts = ; local upperlevel; local highlight; local highlighttot; local total; -- sum of all byte counts local max; -- largest section so far encountered local totmax; -- largest section so far encountered (section total) local _; -- dummy for using gsub to count bytes local lang = mw.language.getContentLanguage; -- language object for number formatting appropriate to local language local s; -- start position of found heading (returned from string.find) local e = 1; -- end position of found heading (returned from string.find) local section_name; -- captured heading name (returned from string.find) local level = ; -- number of leading '=' in heading markup; used for indenting subsections in the rendered list local wl_name; -- anchor and display portion for wikilinks in rendered list

local title_obj = mw.title.new (frame.args[1]) local content = title_obj:getContent; -- get unparsed wikitext from the article if not content then return error_msg_make (template_name, i18n_t['fatal_no_article'],, true); -- emit fatal error message and abandon end

if title_obj.isRedirect then -- redirects don't have sections return error_msg_make (template_name, i18n_t['fatal_redirect'],, true); -- emit fatal error message and abandon end

section_content = content:match ('(.-)

*'); -- get the lead section if section_content then count[0] = #section_content; -- get a count the bytes in the lead section else return error_msg_make (template_name, i18n_t['fatal_no_sections'],, true); -- emit fatal error message and abandon end

total = count[0]; max = count[0]; table.insert (section_info_t, make_wikilink (frame.args[1], section) .. '|| style="text-align:right"|' .. lang:formatNum (count[0]) .. '|| style="text-align:right"|' .. lang:formatNum (count[0]));

while (1) do -- done this way because some articles reuse section names s, e, section_name = string.find (content, '\n

+ *(.-) *

+', e); -- get start, end, and section name beginning a end of last find; newline must precede '

' heading markup if s then table.insert (section_name_list,); -- save section name and start location of this find else break; end end for i, section_name in ipairs (section_name_list) do local escaped_section_name = string.gsub (section_name[1], '([%(%)%.%%%+%-%*%?%[%^%$%]])', '%%%1'); -- escape lua patterns in section name local pattern = '(

+ *' .. escaped_section_name .. ' *

+.-)

+'; -- make a pattern to get the content of a section section_content = string.match (content, pattern, section_name[2]); -- get the content beginning at the string.find start location if section_content then count[i] = #section_content; -- get a count of the bytes in the section total = total + count[i]; max = max < count[i] and count[i] or max; -- keep track of largest count else -- probably the last section (no proper header follows this section name) pattern = '(

+ *' .. escaped_section_name .. ' *

+.+)'; -- make a new pattern section_content = string.match (content, pattern, section_name[2]); -- try to get content if section_content then count[i] = #section_content; -- get a count the bytes in the section total = total + count[i]; max = max < count[i] and count[i] or max; -- keep track of largest count else count[i] = '—'; -- no content so show that end end

_, level[i] = section_content:find ('^=+'); -- should always be the first n characters of section content end totmax=0; lastlevel=0; maxlevels=7; for j=1,maxlevels do levelcounts[j]=0; end for i=#count,1,-1 do if level[i]=lastlevel then totcount[i]=count[i]; end if level[i]>0 then upperlevel=level[i]-1; levelcounts[upperlevel]=levelcounts[upperlevel]+totcount[i]; end lastlevel=level[i]; if totcount[i]>totmax then totmax=totcount[i]; end end

for i, section_name in ipairs (section_name_list) do if count[i]

max then highlight='background:red;"|'; else local proportion = count[i] / max -- get value of "how much of the max" the count is local gb = 250 - math.floor(250 * proportion) -- approach #f8f9fa [r=248,g=249,b=250] (default wikitable cell color) for small bytecounts highlight = string.format('background:#F8%02X%02X;"|', gb, gb) -- shade the bg as r: 248, g: gb, and b: gb end highlighttot=; -- start the style declaration if totcount[i]

totmax then highlighttot=highlighttot .. 'background:red;'; else local proportion = totcount[i] / totmax -- get value of "how much of the max" the count is local gb = 250 - math.floor(250 * proportion) -- approach #f8f9fa [r=248,g=249,b=250] (default wikitable cell color) for small bytecounts highlighttot=highlighttot .. string.format('background:#F8%02X%02X;', gb, gb) -- shade the bg as r: 248, g: gb, and b: gb end if level[i]

2 then highlighttot=highlighttot .. 'font-weight:bold;'; -- if main section, make it bold elseif totcount[i]

count[i] then highlighttot='color:transparent;'; -- hide totals for subsections with no subsubsections, values required for proper sorting end highlighttot=highlighttot .. '"|'; -- close the style declaration level[i] = (2 < level[i]) and ((level[i]-2) * 1.6) or nil; -- remove offset and mult by 1.6em (same indent as ':' markup which doesn't work in a table)

local markup_removed; -- temp flag to note that the section heading has been modified (references and html-like markup stripped) local modified = false; -- flag to select section heading styling; false: wikilink; true: plain text with error message wl_name, modified = refs_remove (section_name[1]); -- remove all references wl_name = remove_wiki_link (wl_name); -- remove all wikilinks wl_name = wl_name:gsub ('', '__sss_nowiki/__'); -- replace tag with special secret symbol wl_name = frame:preprocess (wl_name); -- render to html wl_name = anchors_remove (wl_name); -- remove known anchor markup; these allowed in section heading so do not bump wl_name = wl_name:gsub ('__sss_nowiki/__', ''); -- replace special secret symbol with tag wl_name, markup_removed = stripmarkers_remove (wl_name); -- remove any strip markers resulting from preprocessing modified = modified or markup_removed; -- update wl_name = wl_name:gsub ('', '\'\); -- italic markup has been converted to html; unconvert so remove_container doesn't remove inappropriately wl_name = wl_name:gsub ('', '\'\'\); -- bold markup has been converted to html; unconvert so remove_container doesn't remove inappropriately wl_name, markup_removed = remove_container (wl_name); -- remove html containers from section headings so that we can link to the section modified = modified or markup_removed; -- update

wl_name = wl_name:gsub ('[%[%]]',); -- replace '[' and ']' characters with html entities so that wikilinked section names work wl_name = mw.text.trim (wl_name); -- trim leading/trailing white space if any because white space buggers up url anchor links local heading_text; if modified then heading_text = table.concat ; -- close help link else heading_text = make_wikilink (frame.args[1] .. '#' .. wl_name:gsub ("+", ), wl_name); -- unmodified rendered as is end

table.insert (section_info_t, table.concat); end

local out = ; -- make a sortable wikitable for output table.insert (out, string.format ('

%s', -- output caption and column headings frame.args.style or , -- value for style= attribute mw.message.newRawMessage (i18n_t['table caption'],):plain ));

table.insert (out, table.concat);

table.insert (out, table.concat (section_info_t, '\n

-\n')); -- section rows with leading pipes (except first row already done) table.insert (out, table.concat); table.insert (out, '\n
'); -- close the wikitable local result = table.concat (out, ); return result; -- because gsub returns string and number of replacementsend

----------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------

return

Notes and References

  1. .-