Module:NavboxMobile: Difference between revisions
EnWikiAdmin (talk | contribs) No edit summary Tag: Reverted |
EnWikiAdmin (talk | contribs) No edit summary |
||
(86 intermediate revisions by the same user not shown) | |||
Line 16: | Line 16: | ||
local listnums = {} | local listnums = {} | ||
-- Helper | -- Function to check if the given HTML class is a list class | ||
local function has_list_class(htmlclass) | |||
local patterns = { | |||
'^' .. htmlclass .. '$', | |||
'%s' .. htmlclass .. '$', | |||
'^' .. htmlclass .. '%s', | |||
'%s' .. htmlclass .. '%s' | |||
} | |||
for arg, _ in pairs(args) do | |||
if type(arg) == 'string' and mw.ustring.find(arg, cfg.pattern.class) then | |||
for _, pattern in ipairs(patterns) do | |||
if mw.ustring.find(args[arg] or '', pattern) then | |||
return true | |||
end | |||
end | |||
end | |||
end | |||
return false | |||
end | |||
-- Function to check if the navbar is enabled | |||
local function has_navbar() | |||
return args[cfg.arg.navbar] ~= cfg.keyword.navbar_off | |||
and args[cfg.arg.navbar] ~= cfg.keyword.navbar_plain | |||
and ( | |||
args[cfg.arg.name] | |||
or mw.getCurrentFrame():getParent():getTitle():gsub(cfg.pattern.sandbox, '') | |||
~= cfg.pattern.navbox | |||
) | |||
end | |||
local function add_list_styles() | |||
local frame = mw.getCurrentFrame() | |||
local function add_list_templatestyles(htmlclass, templatestyles) | |||
if has_list_class(htmlclass) then | |||
return frame:extensionTag{ | |||
name = 'templatestyles', args = { src = templatestyles } | |||
} | |||
else | |||
return '' | |||
end | |||
end | |||
local hlist_styles = add_list_templatestyles('hlist', cfg.hlist_templatestyles) | |||
local plainlist_styles = add_list_templatestyles('plainlist', cfg.plainlist_templatestyles) | |||
-- a second workaround for [[phab:T303378]] | |||
-- when that issue is fixed, we can actually use has_navbar not to emit the | |||
-- tag here if we want | |||
if has_navbar() and hlist_styles == '' then | |||
hlist_styles = frame:extensionTag{ | |||
name = 'templatestyles', args = { src = cfg.hlist_templatestyles } | |||
} | |||
end | |||
-- hlist -> plainlist is best-effort to preserve old Common.css ordering. | |||
-- this ordering is not a guarantee because most navboxes will emit only | |||
-- one of these classes [hlist_note] | |||
return hlist_styles .. plainlist_styles | |||
end | |||
-- Analogous function to add_navbox_styles from Module:Navbox | |||
local function add_navbox_mobile_styles(hiding_templatestyles) | |||
local frame = mw.getCurrentFrame() | |||
-- Function to add user-defined templatestyles | |||
local function add_user_styles(templatestyles) | |||
if templatestyles and templatestyles ~= '' then | |||
return frame:extensionTag{ | |||
name = 'templatestyles', | |||
args = { src = templatestyles } | |||
} | |||
end | |||
return '' | |||
end | |||
-- Combine list styles with base and user styles | |||
local list_styles = add_list_styles() | |||
local base_templatestyles = cfg.templatestyles | |||
local templatestyles = add_user_styles(args[cfg.arg.templatestyles]) | |||
local child_templatestyles = add_user_styles(args[cfg.arg.child_templatestyles]) | |||
-- Combine all styles into a single <div> | |||
return mw.html.create('div') | |||
:addClass(cfg.class.navbox_styles) | |||
:wikitext( | |||
list_styles .. | |||
base_templatestyles .. | |||
templatestyles .. | |||
child_templatestyles .. | |||
table.concat(hiding_templatestyles) | |||
) | |||
:done() | |||
end | |||
-- Work around for style markers (similar to Navbox) | |||
local function move_hiding_templatestyles(args) | |||
local gfind = string.gmatch | |||
local gsub = string.gsub | |||
local templatestyles_markers = {} | |||
local strip_marker_pattern = '(\127[^\127]*UNIQ%-%-templatestyles%-%x+%-QINU[^\127]*\127)' | |||
for k, arg in pairs(args) do | |||
for marker in gfind(arg, strip_marker_pattern) do | |||
table.insert(templatestyles_markers, marker) | |||
end | |||
args[k] = gsub(arg, strip_marker_pattern, '') | |||
end | |||
return templatestyles_markers | |||
end | |||
-- Helper function to trim whitespace | |||
local function trim(s) | local function trim(s) | ||
return (mw.ustring.gsub(s, "^%s*(.-)%s*$", "%1")) | return (mw.ustring.gsub(s, "^%s*(.-)%s*$", "%1")) | ||
end | end | ||
-- Helper function to add newlines based on content | |||
local function addNewline(s) | local function addNewline(s) | ||
if s:match('^[*:;#]') or s:match('^{|') then | if s:match('^[*:;#]') or s:match('^{|') then | ||
return '\n' .. s .. '\n' | return '\n' .. s ..'\n' | ||
else | else | ||
return s | return s | ||
end | end | ||
end | end | ||
local function processItem(item, nowrapitems) | local function processItem(item, nowrapitems) | ||
Line 36: | Line 150: | ||
-- Applying nowrap to lines in a table does not make sense. | -- Applying nowrap to lines in a table does not make sense. | ||
-- Add newlines to compensate for trim of x in |parm=x in a template. | -- Add newlines to compensate for trim of x in |parm=x in a template. | ||
item = item:gsub('|%s*width%s*=%s*"[%d%w]+"', '| width="100%"') | |||
return '\n' .. item .. '\n' | return '\n' .. item .. '\n' | ||
end | end | ||
if nowrapitems == cfg.keyword.nowrapitems_yes then | if nowrapitems == cfg.keyword.nowrapitems_yes or true then | ||
local lines = {} | local lines = {} | ||
for line in (item .. '\n'):gmatch('([^\n]*)\n') do | for line in (item .. '\n'):gmatch('([^\n]*)\n') do | ||
Line 52: | Line 167: | ||
return '\n' .. item .. '\n' | return '\n' .. item .. '\n' | ||
end | end | ||
-- Ensure no excessive whitespace | |||
item = mw.ustring.gsub(item, "%s+", " ") | |||
return item | return item | ||
end | end | ||
-- Function to add a new table row with optional gutter | -- Function to add a new table row with optional gutter | ||
Line 69: | Line 187: | ||
if tableRowAdded and not suppressSpacer then | if tableRowAdded and not suppressSpacer then | ||
tbl:tag('tr') | tbl:tag('tr') | ||
:css('height', ' | :css('height', '2px') | ||
:tag('td') | :tag('td') | ||
:attr('colspan', 2) | :attr('colspan', 2) | ||
:css('background-color', 'transparent') -- Ensure spacer is invisible | :css('background-color', 'transparent') -- Ensure spacer is invisible | ||
end | end | ||
tableRowAdded = true | tableRowAdded = true | ||
return tbl:tag('tr') | return tbl:tag('tr') | ||
end | end | ||
-- Function to render the navigation bar | -- Function to render the navigation bar | ||
local function renderNavBar( | local function renderNavBar(cell) | ||
if | if args.navbar == 'off' then | ||
return -- Don't render anything if Navbar is off | |||
end | end | ||
-- Render the Navbar | |||
cell:wikitext(navbar{ | |||
args.name, | |||
mini = 1, | |||
fontstyle = (args.basestyle or '') .. ';' .. (args.titlestyle or '') .. ';background:none transparent;border:none;' | |||
}) | |||
end | end | ||
-- | -- | ||
-- Title row | |||
-- | |||
local function renderTitleRow(tbl) | local function renderTitleRow(tbl) | ||
if not args.title then return end | if not args.title then return end | ||
local titleRow = addTableRow(tbl) | local titleRow = addTableRow(tbl) | ||
local titleCell = titleRow:tag('th'):attr('scope', 'col') | local titleCell = titleRow:tag('th'):attr('scope', 'col') | ||
:addClass('navboxMobile-title') | :addClass('navboxMobile-title') | ||
:attr('colspan', | :addClass(cfg.class.navbox_list) | ||
:addClass(args[cfg.arg.titleclass]) | |||
:attr('data-level', 1) | |||
:attr('colspan', 2) | |||
:cssText(args.basestyle) | :cssText(args.basestyle) | ||
:cssText(args.titlestyle) | :cssText(args.titlestyle) | ||
: | :css('position', 'relative') -- Ensure relative positioning for absolute children | ||
-- Add the title content, centered across the full width | |||
titleCell | titleCell | ||
:tag('div') | :tag('div') | ||
:css(' | :css('margin', '0 auto') | ||
:css(' | :css('text-align', 'center') | ||
:wikitext(addNewline(args.title)) | :wikitext(addNewline(args.title)) | ||
-- Add the navbar, positioned absolutely in the top-right corner | |||
if has_navbar() then | |||
titleCell | |||
:tag('div') | |||
:addClass('navboxMobile-navbar') | |||
:css('position', 'absolute') | |||
:css('top', '0') | |||
:css('right', '0') | |||
:css('padding', '0.2em') -- Optional: Adjust padding for alignment | |||
:wikitext(navbar{ | |||
args.name, | |||
mini = 1, | |||
fontstyle = (args.basestyle or '') .. ';' .. (args.titlestyle or '') .. ';background:none transparent;border:none;' | |||
}) | |||
end | |||
end | |||
-- | |||
-- Above/Below rows | |||
-- | |||
local function getAboveBelowColspan() | |||
return 2 | |||
end | end | ||
local function renderAboveRow(tbl) | local function renderAboveRow(tbl) | ||
if not args.above then return end | if not args.above then return end | ||
Line 181: | Line 267: | ||
:tag('td') | :tag('td') | ||
:addClass('navboxMobile-abovebelow-content') | :addClass('navboxMobile-abovebelow-content') | ||
:addClass(cfg.class.navbox_list) -- Add consistent list styling | |||
:addClass(args.aboveclass) | :addClass(args.aboveclass) | ||
:cssText(args.basestyle) | :cssText(args.basestyle) | ||
:cssText(args.abovestyle) | :cssText(args.abovestyle) | ||
:css('padding', args.abovepadding or '0.5em') -- Optional padding | :css('padding', args[cfg.arg.abovepadding] or '0.5em') -- Optional padding | ||
:attr('colspan', | :attr('colspan', getAboveBelowColspan()) | ||
:tag('div') | :tag('div') | ||
:wikitext(addNewline(args.above)) | :wikitext(addNewline(args.above)) | ||
end | end | ||
local function renderBelowRow(tbl) | local function renderBelowRow(tbl) | ||
if not args.below then return end | if not args.below then return end | ||
Line 197: | Line 285: | ||
:tag('td') | :tag('td') | ||
:addClass('navboxMobile-abovebelow-content') | :addClass('navboxMobile-abovebelow-content') | ||
:addClass(cfg.class.navbox_list) -- Add consistent list styling | |||
:addClass(args.belowclass) | :addClass(args.belowclass) | ||
:cssText(args.basestyle) | :cssText(args.basestyle) | ||
:cssText(args.belowstyle) | :cssText(args.belowstyle) | ||
:css('padding', args.belowpadding or '0.5em') -- Optional padding | :css('padding', args[cfg.arg.belowpadding] or '0.5em') -- Optional padding | ||
:attr('colspan', | :attr('colspan', getAboveBelowColspan()) | ||
:tag('div') | :tag('div') | ||
:wikitext(addNewline(args.below)) | :wikitext(addNewline(args.below)) | ||
end | end | ||
local function renderListRow(tbl, path, parentLevel) | |||
-- Initialize path and parentLevel | |||
path = path or "" | |||
parentLevel = parentLevel or 2 -- Start at level 2 for child groups | |||
-- Keep track of rendered children to avoid duplicates | |||
: | local renderedChildren = {} | ||
: | |||
: | for i = 1, 10 do | ||
local groupKey = (path ~= "" and "child" .. path .. "_" or "") .. "group" .. i | |||
local listKey = (path ~= "" and "child" .. path .. "_" or "") .. "list" .. i | |||
-- Skip if this group has already been rendered | |||
if not renderedChildren[listKey] and args[listKey] then | |||
renderedChildren[listKey] = true -- Track rendered lists | |||
-- Render the group (heading) | |||
local groupRow = addTableRow(tbl) | |||
local groupCell = groupRow:tag('th') | |||
:attr('scope', 'row') | |||
:addClass('navboxMobile-group-content') | |||
:addClass('navboxMobile-group-level' .. parentLevel) -- Correct level | |||
:attr('data-level', parentLevel) -- Optional attribute for CSS targeting | |||
:wikitext(processItem(args[groupKey])) | |||
-- Check if the list is non-empty before rendering content | |||
if args[listKey] and args[listKey] ~= "" then | |||
if args[listKey] == "child" then | |||
-- Check if the child contains valid groups or lists | |||
local childHasContent = false | |||
for j = 1, 10 do | |||
local childGroupKey = "child" .. (path ~= "" and path .. "_" or "") .. i .. "_group" .. j | |||
local childListKey = "child" .. (path ~= "" and path .. "_" or "") .. i .. "_list" .. j | |||
if args[childGroupKey] or args[childListKey] then | |||
childHasContent = true | |||
break | |||
end | |||
end | |||
-- | if childHasContent then | ||
local | -- Recursively render child lists | ||
local newPath = (path ~= "" and path .. "_" or "") .. i | |||
renderListRow(tbl, tostring(newPath), parentLevel + 1) | |||
end | |||
else | |||
-- Render flat lists | |||
local listRow = addTableRow(tbl) | |||
local listCell = listRow:tag('td') | |||
:attr('colspan', 2) | |||
:addClass('navboxMobile-list-content') -- Base content class | |||
:addClass('navboxMobile-list-level' .. parentLevel) -- Correct level | |||
:addClass((i % 2 == 1) and 'navboxMobile-odd' or 'navboxMobile-even') -- Odd/even class | |||
:addClass(args.listclass or '') -- Apply `listclass` if defined in the template | |||
:wikitext(processItem(args[listKey], args.nowrapitems)) | |||
end | |||
end | |||
end | end | ||
end | end | ||
end | end | ||
-- | -- | ||
-- Tracking categories | |||
-- | |||
local function needsHorizontalLists() | local function needsHorizontalLists() | ||
if border == 'child' or border == 'subgroup' or args.tracking == 'no' then return false end | if border == 'child' or border == 'subgroup' or args.tracking == 'no' then return false end | ||
Line 385: | Line 418: | ||
end | end | ||
-- | -- | ||
-- Main navboxMobile tables | |||
-- | |||
local function renderMainTable() | local function renderMainTable() | ||
local tbl = mw.html.create('table') | local tbl = mw.html.create('table') | ||
:addClass('nowraplinks') | :addClass('nowraplinks') | ||
:addClass( | :addClass(cfg.class.navbox) | ||
:addClass(args.bodyclass) | :addClass(args.bodyclass) | ||
:addClass(args.listclass or '') -- Apply the `listclass` argument globally if applicable | |||
:cssText(args.bodystyle) | :cssText(args.bodystyle) | ||
:cssText(args.style) | :cssText(args.style) | ||
:css('margin-top', '0') | :css('margin-top', '0') | ||
:css('margin-bottom', '0') | :css('margin-bottom', '0') | ||
-- Add the title row | |||
renderTitleRow(tbl) | |||
tbl | -- Add the above row | ||
renderAboveRow(tbl) | |||
-- Render all group and list rows | |||
for _, listnum in ipairs(listnums) do | for _, listnum in ipairs(listnums) do | ||
renderListRow(tbl, listnum) | local listKey = "list" .. listnum | ||
local groupKey = "group" .. listnum | |||
if args[listKey] then | |||
-- Render group row | |||
local groupRow = addTableRow(tbl) | |||
local groupCell = groupRow:tag('th') | |||
:attr('scope', 'row') | |||
:addClass('navboxMobile-group-content') | |||
:addClass('navboxMobile-group-level1') | |||
:addClass(args.listclass or '') -- Apply `listclass` for group rows | |||
:wikitext(processItem(args[groupKey])) | |||
-- Render list row | |||
if args[listKey] == 'child' then | |||
renderListRow(tbl, tostring(listnum), 2) | |||
else | |||
local listRow = addTableRow(tbl) | |||
listRow:tag('td') | |||
:attr('colspan', 2) | |||
:addClass('navboxMobile-list-content') | |||
:addClass((listnum % 2 == 1) and 'navboxMobile-odd' or 'navboxMobile-even') | |||
:addClass(args.listclass or '') -- Apply `listclass` to flat rows | |||
:wikitext(processItem(args[listKey])) | |||
end | |||
end | |||
end | end | ||
-- Add the below row | |||
renderBelowRow(tbl) | renderBelowRow(tbl) | ||
Line 420: | Line 474: | ||
end | end | ||
-- Main function | |||
-- | |||
-- Main NavboxMobile Function | |||
-- | |||
-- In the main navboxMobile function | |||
function p._navboxMobile(navboxMobileArgs) | function p._navboxMobile(navboxMobileArgs) | ||
args = navboxMobileArgs | args = navboxMobileArgs | ||
Line 431: | Line 490: | ||
local hiding_templatestyles = move_hiding_templatestyles(args) | local hiding_templatestyles = move_hiding_templatestyles(args) | ||
-- Collect list numbers | -- Collect top-level list numbers (list1, list2, etc.) | ||
for k, v in pairs(args) do | for k, v in pairs(args) do | ||
if type(k) == 'string' then | if type(k) == 'string' then | ||
Line 450: | Line 509: | ||
-- Create the final HTML with styles | -- Create the final HTML with styles | ||
local res = mw.html.create() | local res = mw.html.create() | ||
res:node(add_navbox_mobile_styles(hiding_templatestyles)) | res:node(add_navbox_mobile_styles(hiding_templatestyles)) | ||
res:node(tbl) | |||
return tostring(res) | return tostring(res) | ||
end | end | ||
-- | |||
-- | |||
-- Main NavboxMobile Function | |||
-- | |||
function p.navboxMobile(frame) | function p.navboxMobile(frame) | ||
if not getArgs then | if not getArgs then |