Module:NavboxMobile: Difference between revisions

No edit summary
No edit summary
 
(152 intermediate revisions by the same user not shown)
Line 50: Line 50:


local function add_list_styles()
local function add_list_styles()
local frame = mw.getCurrentFrame()
    local frame = mw.getCurrentFrame()
local function add_list_templatestyles(htmlclass, templatestyles)
    local function add_list_templatestyles(htmlclass, templatestyles)
if has_list_class(htmlclass) then
        if has_list_class(htmlclass) then
return frame:extensionTag{
            return frame:extensionTag{
name = 'templatestyles', args = { src = templatestyles }
                name = 'templatestyles', args = { src = templatestyles }
}
            }
else
        else
return ''
            return ''
end
        end
end
    end
       
local hlist_styles = add_list_templatestyles('hlist', cfg.hlist_templatestyles)
    local hlist_styles = add_list_templatestyles('hlist', cfg.hlist_templatestyles)
local plainlist_styles = add_list_templatestyles('plainlist', cfg.plainlist_templatestyles)
    local plainlist_styles = add_list_templatestyles('plainlist', cfg.plainlist_templatestyles)
   
-- a second workaround for [[phab:T303378]]
    -- a second workaround for [[phab:T303378]]
-- when that issue is fixed, we can actually use has_navbar not to emit the
    -- when that issue is fixed, we can actually use has_navbar not to emit the
-- tag here if we want
    -- tag here if we want
if has_navbar() and hlist_styles == '' then
    if has_navbar() and hlist_styles == '' then
hlist_styles = frame:extensionTag{
        hlist_styles = frame:extensionTag{
name = 'templatestyles', args = { src = cfg.hlist_templatestyles }
            name = 'templatestyles', args = { src = cfg.hlist_templatestyles }
}
        }
end
    end
   
-- hlist -> plainlist is best-effort to preserve old Common.css ordering.
    -- hlist -> plainlist is best-effort to preserve old Common.css ordering.
-- this ordering is not a guarantee because most navboxes will emit only
    -- this ordering is not a guarantee because most navboxes will emit only
-- one of these classes [hlist_note]
    -- one of these classes [hlist_note]
return hlist_styles .. plainlist_styles
    return hlist_styles .. plainlist_styles
end
end


Line 95: Line 95:


     -- Combine list styles with base and user styles
     -- Combine list styles with base and user styles
     local list_styles = add_list_styles() -- Ensure this function is defined or required appropriately
     local list_styles = add_list_styles()
     local base_templatestyles = cfg.templatestyles
     local base_templatestyles = cfg.templatestyles
     local templatestyles = add_user_styles(args[cfg.arg.templatestyles])
     local templatestyles = add_user_styles(args[cfg.arg.templatestyles])
Line 141: Line 141:
     end
     end
end
end
local function processItem(item, nowrapitems)
    if not item then
        return '' -- Prevent nil errors
    end
    if item:sub(1, 2) == '{|' then
        -- 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.
        item = item:gsub('|%s*width%s*=%s*"[%d%w]+"', '| width="100%"')
        return '\n' .. item .. '\n'
    end
    if nowrapitems == cfg.keyword.nowrapitems_yes or true then
        local lines = {}
        for line in (item .. '\n'):gmatch('([^\n]*)\n') do
            local prefix, content = line:match('^([*:;#]+)%s*(.*)')
            if prefix and not content:match(cfg.pattern.nowrap) then
                line = format(cfg.nowrap_item, prefix, content)
            end
            table.insert(lines, line)
        end
        item = table.concat(lines, '\n')
    end
    if item:match('^[*:;#]') then
        return '\n' .. item .. '\n'
    end
    -- Ensure no excessive whitespace
    item = mw.ustring.gsub(item, "%s+", " ")
    return item
end


-- Function to add a new table row with optional gutter
-- Function to add a new table row with optional gutter
local function addTableRow(tbl)
local function addTableRow(tbl)
     if tableRowAdded then
    -- Check if the spacer rows are necessary based on the listclass or bodyclass
         tbl
    local suppressSpacer = false
            :tag('tr')
    if args.listclass then
                :css('height', '2px')
        suppressSpacer = suppressSpacer or args.listclass:find("hlist")
                :tag('td')
    end
                    :attr('colspan', 2)
    if args.bodyclass then
        -- Note: You can add additional gutter styling here if needed
        suppressSpacer = suppressSpacer or args.bodyclass:find("hlist")
    end
 
    -- Only add spacer rows if they are not explicitly suppressed
     if tableRowAdded and not suppressSpacer then
         tbl:tag('tr')
            :css('height', '2px')
            :tag('td')
                :attr('colspan', 2)
                :css('background-color', 'transparent') -- Ensure spacer is invisible
     end
     end
     tableRowAdded = true
     tableRowAdded = true
     return tbl:tag('tr')
     return tbl:tag('tr')
Line 157: Line 198:


-- Function to render the navigation bar
-- Function to render the navigation bar
local function renderNavBar(titleCell)
local function renderNavBar(cell)
    local spacerSide = nil
 
     if args.navbar == 'off' then
     if args.navbar == 'off' then
         if args.state == 'plain' then spacerSide = 'right' end
         return -- Don't render anything if Navbar is off
    elseif args.navbar == 'plain' or (not args.name and mw.getCurrentFrame():getParent():getTitle():gsub('/sandbox$', '') == 'Template:navbox') then
        if args.state ~= 'plain' then spacerSide = 'left' end
    else
        if args.state == 'plain' then spacerSide = 'right' end
 
        titleCell:wikitext(navbar{
            args.name,
            mini = 1,
            fontstyle = (args.basestyle or '') .. ';' .. (args.titlestyle or '') ..  ';background:none transparent;border:none;'
        })
     end
     end


     if spacerSide then
     -- Render the Navbar
         titleCell
    cell:wikitext(navbar{
            :tag('span')
        args.name,
                :css('float', spacerSide)
         mini = 1,
                :css('width', '6em')
        fontstyle = (args.basestyle or '') .. ';' .. (args.titlestyle or '') .. ';background:none transparent;border:none;'
                :wikitext(' ')
     })
     end
end
end


--
--
Line 190: Line 219:


     local titleRow = addTableRow(tbl)
     local titleRow = addTableRow(tbl)
    if args.titlegroup then
        titleRow
            :tag('th')
                :attr('scope', 'row')
                :addClass('navboxMobile-group-content')
                :addClass(args.titlegroupclass)
                :cssText(args.basestyle)
                :cssText(args.groupstyle)
                :cssText(args.titlegroupstyle)
                :wikitext(args.titlegroup)
    end
     local titleCell = titleRow:tag('th'):attr('scope', 'col')
     local titleCell = titleRow:tag('th'):attr('scope', 'col')
 
        :addClass('navboxMobile-title')
    if args.titlegroup then
        :addClass(cfg.class.navbox_list)
         titleCell
        :addClass(args[cfg.arg.titleclass])
            :css('border-left', '2px solid #fdfdfd')
         :attr('data-level', 1)
            :css('width', '100%')
        :attr('colspan', 2)
    end
 
    local titleColspan = 2
    -- Adjust colspan based on image presence if needed
    if args.titlegroup then titleColspan = titleColspan - 1 end
 
    titleCell
         :cssText(args.basestyle)
         :cssText(args.basestyle)
         :cssText(args.titlestyle)
         :cssText(args.titlestyle)
         :addClass('navboxMobile-title')
         :css('position', 'relative') -- Ensure relative positioning for absolute children
        :attr('colspan', titleColspan)


     renderNavBar(titleCell)
     -- Add the title content, centered across the full width
    titleCell
        :tag('div')
            :css('margin', '0 auto')
            :css('text-align', 'center')
            :wikitext(addNewline(args.title))


     titleCell
     -- Add the navbar, positioned absolutely in the top-right corner
        :tag('div')
    if has_navbar() then
            :addClass(args.titleclass)
        titleCell
            :css('font-size', '114%')
            :tag('div')
            :wikitext(addNewline(args.title))
                :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
end


Line 244: 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[cfg.arg.abovepadding] or '0.5em') -- Optional padding
             :attr('colspan', getAboveBelowColspan())
             :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)
Line 258: 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[cfg.arg.belowpadding] or '0.5em') -- Optional padding
             :attr('colspan', getAboveBelowColspan())
             :attr('colspan', getAboveBelowColspan())
             :tag('div')
             :tag('div')
Line 266: Line 295:
end
end


--
--  List rows
--
local function renderListRow(tbl, listnum)
    local row = addTableRow(tbl)


    -- Process group
    if args['group' .. listnum] then
        local groupCell = row:tag('th')
        groupCell
            :attr('scope', 'row')
            :addClass('navboxMobile-group-content')
            :cssText(args.basestyle)
            :cssText(args.groupstyle)
            :cssText(args['group' .. listnum .. 'style'])
            :wikitext(args['group' .. listnum])
    end


     -- Add table row for the list
local function renderListRow(tbl, path, parentLevel)
     row = addTableRow(tbl)
     -- Initialize path and parentLevel
     local listCell = row:tag('td')
     path = path or ""
    listCell:attr('colspan', 2)
     parentLevel = parentLevel or 2 -- Start at level 2 for child groups
    listCell:css('width', '100%')


     -- Apply dynamic styling
     -- Keep track of rendered children to avoid duplicates
     local rowstyle = (listnum % 2 == 1) and cfg.class.navboxMobile_odd or cfg.class.navboxMobile_even
     local renderedChildren = {}
    listCell
        :addClass('navboxMobile-list-content')
        :addClass(rowstyle)
        :cssText(args.liststyle)
        :cssText(args['list' .. listnum .. 'style'])


     -- Check for nested lists (e.g., child1_groupX)
     for i = 1, 10 do
    local nestedGroup = args['child' .. listnum .. '_group1']
        local groupKey = (path ~= "" and "child" .. path .. "_" or "") .. "group" .. i
    local nestedList = args['child' .. listnum .. '_list1']
        local listKey = (path ~= "" and "child" .. path .. "_" or "") .. "list" .. i


    if nestedGroup or nestedList then
        -- Skip if this group has already been rendered
         local nestedTbl = mw.html.create('table')
         if not renderedChildren[listKey] and args[listKey] then
             :addClass('navboxMobile-subgroup')
             renderedChildren[listKey] = true -- Track rendered lists
            :css('width', '100%')


        for i = 1, 10 do -- Assume a max of 10 nested groups/lists
            -- Render the group (heading)
             local nestedGroup = args['child' .. listnum .. '_group' .. i]
             local groupRow = addTableRow(tbl)
            local nestedList = args['child' .. listnum .. '_list' .. i]
            local groupCell = groupRow:tag('th')
 
                :attr('scope', 'row')
            if not nestedGroup and not nestedList then break end
                :addClass('navboxMobile-group-content')
                :addClass('navboxMobile-group-level' .. parentLevel) -- Correct level
                :attr('data-level', parentLevel) -- Optional attribute for CSS targeting
                :wikitext(processItem(args[groupKey]))


             local nestedRow = addTableRow(nestedTbl)
             -- Check if the list is non-empty before rendering content
 
             if args[listKey] and args[listKey] ~= "" then
             if nestedGroup then
                 if args[listKey] == "child" then
                 nestedRow:tag('th')
                     -- Check if the child contains valid groups or lists
                     :addClass('navboxMobile-group-content')
                    local childHasContent = false
                     :cssText(args.basestyle)
                     for j = 1, 10 do
                    :cssText(args['child' .. listnum .. '_group' .. i .. 'style'])
                        local childGroupKey = "child" .. (path ~= "" and path .. "_" or "") .. i .. "_group" .. j
                     :wikitext(nestedGroup)
                        local childListKey = "child" .. (path ~= "" and path .. "_" or "") .. i .. "_list" .. j
            end
                        if args[childGroupKey] or args[childListKey] then
                            childHasContent = true
                            break
                        end
                     end


            if nestedList then
                    if childHasContent then
                 nestedRow:tag('td')
                        -- Recursively render child lists
                    :addClass('navboxMobile-list-content hlist')
                        local newPath = (path ~= "" and path .. "_" or "") .. i
                    :cssText(args['child' .. listnum .. '_list' .. i .. 'style'])
                        renderListRow(tbl, tostring(newPath), parentLevel + 1)
                    :wikitext(processItem(nestedList))
                    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
        listCell:node(nestedTbl)
    else
        listCell
            :addClass('hlist') -- Add hlist for horizontal styling
            :wikitext(processItem(args['list' .. listnum]))
     end
     end
end
end




Line 378: Line 399:
local function getTrackingCategories()
local function getTrackingCategories()
     local cats = {}
     local cats = {}
     if needsHorizontalLists() then table.insert(cats, 'Navigational boxes without horizontal lists') end
     if needsHorizontalLists() then table.insert(cats, cfg.category.horizontal_lists) end
     if hasBackgroundColors() then table.insert(cats, 'navboxMobiles using background colours') end
     if hasBackgroundColors() then table.insert(cats, cfg.category.background_colors) end
     if isIllegible() then table.insert(cats, 'Potentially illegible navboxMobiles') end
     if isIllegible() then table.insert(cats, cfg.category.illegible) end
    if border == 'subgroup' then table.insert(cats, cfg.category.subgroup) end
    if args.tracking == 'no' then table.insert(cats, cfg.category.no_tracking) end
     return cats
     return cats
end
end
Line 401: Line 424:
     local tbl = mw.html.create('table')
     local tbl = mw.html.create('table')
         :addClass('nowraplinks')
         :addClass('nowraplinks')
        :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.style)
        :css('margin-top', '0')
        :css('margin-bottom', '0')


     -- Uncomment and implement collapsibility if needed
     -- Add the title row
     --[[if args.title and (args.state ~= 'plain' and args.state ~= 'off') then
     renderTitleRow(tbl)
        tbl
            :addClass('collapsible')
            :addClass(args.state or 'autocollapse')
    end]]


     tbl:css('border-spacing', 0)
     -- Add the above row
    if border == 'subgroup' or border == 'child' or border == 'none' then
     renderAboveRow(tbl)
        tbl
            :addClass('navboxMobile-subgroup')
            :cssText(args.bodystyle)
            :cssText(args.style)
     else -- Regular navbox - bodystyle and style will be applied to the wrapper table
        tbl
            :addClass('navboxMobile-inner')
            :css('background', 'transparent')
            :css('color', 'inherit')
    end
    tbl:cssText(args.innerstyle)


     renderTitleRow(tbl)
     -- Render all group and list rows
    renderAboveRow(tbl)
     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 434: Line 474:
end
end


--
--  Main NavboxMobile Function
--
-- In the main navboxMobile function
function p._navboxMobile(navboxMobileArgs)
function p._navboxMobile(navboxMobileArgs)
     args = navboxMobileArgs
     args = navboxMobileArgs
Line 444: 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 463: Line 509:
     -- Create the final HTML with styles
     -- Create the final HTML with styles
     local res = mw.html.create()
     local res = mw.html.create()
    -- Inject styles analogous to Module:Navbox
     res:node(add_navbox_mobile_styles(hiding_templatestyles))
     res:node(add_navbox_mobile_styles(hiding_templatestyles))
 
     res:node(tbl)
     -- Handle wrapping based on border parameter
    if border == 'none' then
        -- Wrap the table within a navigation div with ARIA attributes
        local navWrapper = mw.html.create('div')
            :attr('role', 'navigation')
            :attr('aria-label', cfg.aria_label)
            :node(tbl)
        res:node(navWrapper)
    elseif border == 'subgroup' or border == 'child' then
        -- Assume this navboxMobile is inside a parent navboxMobile's list cell
        -- Insert closing and opening divs to manage padding
        res
            :wikitext('</div>') -- Close parent div
            :node(tbl)
            :wikitext('<div>') -- Reopen parent div
    else
        -- Wrap the table within a navigation div with additional classes and styles
        local navWrapper = mw.html.create('div')
            :attr('role', 'navigation')
            :addClass('navboxMobile') -- Add appropriate classes
            :cssText(args.bodystyle)
            :cssText(args.style)
            :css('padding', '2px') -- Adjust padding as needed
            :node(tbl)
        res:node(navWrapper)
    end
 
    -- Render tracking categories
    renderTrackingCategories(res)


     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