Module:Navbox with collapsible groups

    From WikiPasokh

    Documentation for this module may be created at Module:Navbox with collapsible groups/doc

    -- This module implements {{Navbox with collapsible groups}}
    
    require('strict')
    local p = {}
    local parentCfg = mw.loadData('Module:Navbox/configuration')
    local thisCfg = mw.loadData('Module:Navbox with collapsible groups/configuration')
    local cfg = {}
    for k, v in pairs(thisCfg) do
    	if type(v) == 'table' then
    		cfg[k] = {}
    		if type(parentCfg[k]) == 'table' then
    			for kk, vv in pairs(parentCfg[k]) do cfg[k][kk] = vv end
    		end
    		for kk, vv in pairs(v) do cfg[k][kk] = vv end
    	end
    end
    local inArray = require("Module:TableTools").inArray
    local getArgs -- lazily initialized
    
    -- helper functions
    local andnum = function(s, n) return string.format(cfg.arg[s..'_and_num'], n) end
    	
    local isblank = function(v) return (v or '') == '' end
    
    local function concatstrings(s)
    	local r = table.concat(s, '')
    	if r:match('^%s*$') then return nil end
    	return r
    end
    	
    local function concatstyles(s)
    	local r = ''
    	for _, v in ipairs(s) do
    		v = mw.text.trim(v, "%s;")
    		if not isblank(v) then r = r .. v .. ';' end
    	end
    	if isblank(r) then return nil end
    	return r
    end
    	
    local function getSubgroup(args, listnum, listText)
    	local subArgs = {
    		[cfg.arg.border] = cfg.keyword.border_subgroup,
    		[cfg.arg.navbar] = cfg.keyword.navbar_plain
    	}
    	local hasSubArgs = false
    	for k, v in pairs(args) do
    		k = tostring(k)
    		for _, w in ipairs(cfg.keyword.subgroups) do
    			w = w .. listnum .. "_"
    			if (#k > #w) and (k:sub(1, #w) == w) then
    				subArgs[k:sub(#w + 1)] = v
    				hasSubArgs = true
    			end
    		end
    	end
    	return hasSubArgs and p._navbox(subArgs) or listText
    end
    
    -- Main functions
    p._navbox = require('Module:Navbox')._navbox
    
    function p._withCollapsibleGroups(pargs)
    	-- table for args passed to navbox
    	local targs = {}
    
    	-- process args
    	local passthrough = {
    		[cfg.arg.above]=true,[cfg.arg.aboveclass]=true,[cfg.arg.abovestyle]=true,
    		[cfg.arg.basestyle]=true,
    		[cfg.arg.below]=true,[cfg.arg.belowclass]=true,[cfg.arg.belowstyle]=true,
    		[cfg.arg.bodyclass]=true,[cfg.arg.bodystyle]=true,
    		[cfg.arg.border]=true,
    		[cfg.arg.groupclass]=true,
    		[cfg.arg.image]=true,[cfg.arg.imageclass]=true,[cfg.arg.imagestyle]=true,
    		[cfg.arg.imageleft]=true,[cfg.arg.imageleftstyle]=true,
    		[cfg.arg.listclass]=true,
    		[cfg.arg.name]=true,
    		[cfg.arg.navbar]=true,
    		[cfg.arg.state]=true,
    		[cfg.arg.style]=true,
    		[cfg.arg.title]=true,[cfg.arg.titleclass]=true,[cfg.arg.titlestyle]=true
    	}
    	for k,v in pairs(pargs) do
    		if k and type(k) == 'string' then
    			if passthrough[k] then
    				targs[k] = v
    			elseif (k:match(cfg.pattern.num)) then
    				local n = k:match(cfg.pattern.num)
    				local list_and_num = andnum('list', n)
    				if ((k:match(cfg.pattern.listnum) or k:match(cfg.pattern.contentnum))
    						and targs[list_and_num] == nil
    						and pargs[andnum('group', n)] == nil
    						and pargs[andnum('sect', n)] == nil
    						and pargs[andnum('section', n)] == nil) then
    					targs[list_and_num] = concatstrings({
    						pargs[list_and_num] or '',
    						pargs[andnum('content', n)] or ''
    					})
    					if (targs[list_and_num] and inArray(cfg.keyword.subgroups, targs[list_and_num])) then
    						targs[list_and_num] = getSubgroup(pargs, n, targs[list_and_num])
    					end
    				elseif ((k:match(cfg.pattern.groupnum) or k:match(cfg.pattern.sectnum) or k:match(cfg.pattern.sectionnum))
    						and targs[list_and_num] == nil) then
    					local titlestyle = concatstyles({
    						pargs[cfg.arg.groupstyle] or '',
    						pargs[cfg.arg.secttitlestyle] or '', 
    						pargs[andnum('groupstyle', n)] or '', 
    						pargs[andnum('sectiontitlestyle', n)] or ''
    					})
    					local liststyle = concatstyles({
    						pargs[cfg.arg.liststyle] or '',
    						pargs[cfg.arg.contentstyle] or '', 
    						pargs[andnum('liststyle', n)] or '', 
    						pargs[andnum('contentstyle', n)] or ''
    					})
    					local title = concatstrings({
    						pargs[andnum('group', n)] or '',
    						pargs[andnum('sect', n)] or '',
    						pargs[andnum('section', n)] or ''
    					})
    					local list = concatstrings({
    						pargs[list_and_num] or '', 
    						pargs[andnum('content', n)] or ''
    					})
    					if list and inArray(cfg.keyword.subgroups, list) then
    						list = getSubgroup(pargs, n, list)
    					end
    					local abbr_and_num = andnum('abbr', n)
    					local state = (pargs[abbr_and_num] and pargs[abbr_and_num] == pargs[cfg.arg.selected]) 
    						and cfg.keyword.state_uncollapsed
    						or (pargs[andnum('state', n)] or cfg.keyword.state_collapsed)
    					
    					targs[list_and_num] =p._navbox({
    						cfg.keyword.border_child,
    						[cfg.arg.navbar] = cfg.keyword.navbar_plain,
    						[cfg.arg.state] = state,
    						[cfg.arg.basestyle] = pargs[cfg.arg.basestyle],
    						[cfg.arg.title] = title,
    						[cfg.arg.titlestyle] = titlestyle,
    						[andnum('list', 1)] = list,
    						[cfg.arg.liststyle] = liststyle,
    						[cfg.arg.listclass] = pargs[andnum('listclass', n)],
    						[cfg.arg.image] = pargs[andnum('image', n)],
    						[cfg.arg.imageleft] = pargs[andnum('imageleft', n)],
    						[cfg.arg.listpadding] = pargs[cfg.arg.listpadding]
    					})
    				end
    			end
    		end
    	end
    	-- ordering of style and bodystyle
    	targs[cfg.arg.style] = concatstyles({targs[cfg.arg.style] or '', targs[cfg.arg.bodystyle] or ''})
    	targs[cfg.arg.bodystyle] = nil
    	
    	-- child or subgroup
    	if targs[cfg.arg.border] == nil then targs[cfg.arg.border] = pargs[1] end
    
    	return p._navbox(targs)
    end
    
    -- Template entry points
    function p.navbox (frame, boxtype)
    	local function readArgs(args, prefix)
    		-- Read the arguments in the order they'll be output in, to make references
    		-- number in the right order.
    		local _
    		_ = args[prefix .. cfg.arg.title]
    		_ = args[prefix .. cfg.arg.above]
    		-- Limit this to 20 as covering 'most' cases (that's a SWAG) and because
    		-- iterator approach won't work here
    		for i = 1, 20 do
    			_ = args[prefix .. andnum('group', i)]
    			if inArray(cfg.keyword.subgroups, args[prefix .. andnum('list', i)]) then
    				for _, v in ipairs(cfg.keyword.subgroups) do
    					readArgs(args, prefix .. v .. i .. "_")
    				end
    			end
    		end
    		_ = args[prefix .. cfg.arg.below]
    	end
    
    	if not getArgs then
    		getArgs = require('Module:Arguments').getArgs
    	end
    	local args = getArgs(frame, {wrappers = {cfg.pattern[boxtype or 'navbox']}})
    	readArgs(args, "")
    	return p['_'..(boxtype or 'navbox')](args)
    end
    
    p['with collapsible groups'] = function (frame)
    	return p.navbox(frame, 'withCollapsibleGroups')
    end
    
    local q = {}
    q._navbox = p._withCollapsibleGroups
    q.navbox = p['with collapsible groups']
    return q