Module:Hatnote group
Implements {{hatnote group}}, for merging multiple hatnotes into a grouped hatnote.
Technical details
The module attempts to find each hatnote-looking div
element, and parse them into space-separated span
elements wrapped in a larger, generic hatnote.
First, it matches every pattern that "looks like a hatnote" in its input text; this is where most problems are likely to originate as the pattern is fairly sensitive to any difference in the form of a hatnote.
From each of the pattern matches, it constructs a table. Each element ("row") of the table corresponds to one original hatnote, and is a table that contains a string with the rendered inner content of the hatnote, and an inner table containing any "extra" classes associated with that hatnote (e.g. selfreference
from a {{self reference}} hatnote, or any classes from the extraclasses
parameter of {{hatnote}}).
It also separately looks for all categories in case they're present outside the hatnote div
element, and adds them to a concatenated string of all "loose categories" to make sure they're not accidentally removed by the hatnote grouping process.
Once all rows are available, it looks at the tables of classes. It adds every class from every row into a table of "universal" classes, then filters out the ones that aren't universal by looking at each row's list and eliminating an item if it isn't present in any one row. Then, "universal" classes are removed from each row's classes table, so that they're not duplicated later.
This processing having been done, each row is formatted with a span
element that includes the extra classes associated with that row, the spans
for each row are joined as a space-separated list, the "loose categories" are tacked on in turn, and then the resulting string is fed into the main hatnote module, with extra classes on the main hatnote being supplied by collating the "universal classes" table into a space-separated list of its keys.
local mHatnote = require('Module:Hatnote')
local p = {}
--Collates key-based table of classes into usable class list
function collateClassList (listTable)
local list = {}
for k, v in pairs(listTable) do
if v and type(k) == "string" then table.insert(list, k) end
end
return table.concat(list, " ")
end
--Passes through single argument from parent frame
function p.group (frame)
return p._group(frame:getParent().args[1], frame:getParent().args.category)
end
function p._group (inputText, category)
--If there's an error element, pass everything through unchanged for easier
--error resolution
if string.find(inputText, '<%a- class="error"', 1, true) then return inputText end
--Heavily reused hatnote data capture pattern
local hatnotePattern =
'(<div role="note" class="' ..
string.gsub(mHatnote.defaultClasses(false), "%-", "%%-") ..
'%s?(.-)">(.-)</div>)'
--Capture hatnote divs and "loose" categories; we'll ignore everything else
local rawDivs = {}
local looseCategories = ''
for x in string.gmatch(inputText, hatnotePattern) do
table.insert(rawDivs, x)
end
for x in string.gmatch(inputText, '%[%[Category:.-%]%]') do
looseCategories = looseCategories .. x
end
--if no inner hatnotes, return an error
if not rawDivs[1] then
return mHatnote.makeWikitextError(
'no inner hatnotes detected',
'Template:Hatnote group',
category
)
end
--Preprocess divs into strings and classes
local innerHatnotes = {}
for k, v in pairs(rawDivs) do
row = {}
row.text = string.gsub(v, hatnotePattern, '%3')
--Here we set class names as keys for easier intersection later
row.classes = {}
for m, w in ipairs(
mw.text.split(
string.gsub(v, hatnotePattern, '%2'),
' ',
true
)
) do
row.classes[mw.text.trim(w)] = true
end
table.insert(innerHatnotes, row)
end
--Identify any universal classes ("hatnote" ignored by omission earlier)
local universalClasses = {}
--clone first classes table to force passing by value rather than reference
for k, v in pairs(innerHatnotes[1].classes) do universalClasses[k] = v end
for k, v in ipairs(innerHatnotes) do
for m, w in pairs(universalClasses) do
universalClasses[m] = (universalClasses[m] and v.classes[m])
end
end
--Remove universal classes from div items, then create class strings per row
for k, v in ipairs(innerHatnotes) do
for m, w in pairs(v.classes) do
if universalClasses[m] then v.classes[m] = nil end
end
v.classString = collateClassList(v.classes)
end
--Process div items into classed span items
local innerSpans = {}
for k, v in ipairs(innerHatnotes) do
table.insert(
innerSpans,
tostring(
mw.html.create("span")
:addClass(v.classString ~= '' and v.classString or nil)
:wikitext(v.text)
)
)
end
--Concatenate spans and categories, and return wrapped as a single hatnote
local outputText = table.concat(innerSpans, " ") .. looseCategories
local hnOptions = {extraclasses = collateClassList(universalClasses)}
return mHatnote._hatnote(outputText, hnOptions)
end
return p