Module:Navbox

--[[ All of the items in the definition tree for the navbox have an expand method that causes them to produce their markup: expand(state, output) where output is a function that adds its argument to the output, and state is state variables. output's job also includes calling expand on any tables!

The top of the tree is a navbox{} title: Mandatory wikitext to display as the navbox's title Can contain a mix of group{}, list{}, and highlightline{}

group{} are the collapsible groups. title: Mandatory wikitext to title the group with. name: Untranslated name to refer to it in things like the opengroups template parameter. Can contain list{} and highlightline{}

list{} title: Optional title to display at the left. Can contain list{}/line{}, in which case the lists will be formatted tabley/subgroupy, or string/ni{}/l{}, which will be formatted as a hlist. Note that the *first* positional argument determines if it's a table or not. highlightline{} Contents are concatenated (with space between) and displayed in a highlighted line like wp:Template:Navbox before= and after= line{} Like list{} except it formats as a straight run of text rather than hlist. Can only contain string. ni{} or l{} ni{} Invokes (or a facsimile, anyway) l{} Invokes the guts of

text{} You should never need to use this directly. It's what strings are wrapped in.

Strings will be presented literally in the output. Functions are allowed: in the positional arguments to any of the above functions, any function will be evaluated in the same way as navbox{}, and then be replaced by the positional elements of the table it returns. Same deal in the named arguments except the return is used directly. In the interests of non-silly output, strings are joined with a space.

line{ "a", function return { "1", "2" } end, "b" } is equivalent to, line{ "a", "1", "2", "b" }

list{ title = function return "foo" end, "1", "2" } is equivalent to, list{ title = "foo", "1", "2" }

The exported function, navbox, has three template parameters: { [1] = Name of the navbox definition to use name = template name for generating the v.d.e links collapse = null or "navbox" to hide the navbox "groups" to collapse just the groups "open" to leave it entirely open. opengroups = space-delimited string of groups to open. Give hthe group a "name" argument to refer to it here. } ]]

local util = require("Module:Utility_functions") local language = require("Module:Language")

local element = { addDataToOutput = function(self, context, output, options) local oldmod = context.mod context.mod = self.data.mod or oldmod for i, item in ipairs(self.data) do           output((options.surround or {''})[1]) self:expandItem(item, context, output, options.spacer) output((options.surround or {,})[2]) if (i < #(self.data)) and options.separator then output(options.separator) end end context.mod = oldmod end, expandItem = function(self, item, context, output, spacer) if type(item) == "table" then item:expand(context, output) else output(tostring(item or '')) end output(spacer or '') end, expandOrMessage = function(self, item, context, output, spacer, message) if item then self:expandItem(item, context, output, spacer) else output(message) end end }

-- The navbox as a whole. element.navbox = { expand = function(self, context, output) output(' ') output('  ') end }

-- The collapsible groups that are the first division of the navbox. element.group = { expand = function(self, context, output) context.odd = true output(' ') output(' ') -- That two semicolons stops something from wrapping tables in gratuitous divs output('') output(' ') output(' ') self:expandOrMessage(self.data.title, context, output, ' ', '!!! Missing title !!!') output('  ') output('  ') self:addDataToOutput(context, output, {spacer = '', separator='  '}) output(' ') output(' ') end }

-- A group-level thingy that displays text in a contrasting background. element.highlightline = { expand = function(self, context, output) output(' ') self:addDataToOutput(context, output, {spacer = ' '}) output(' ') end }

-- The remaining subdivisions of the navbox; with the title at the left, optionally. element.list = { expand = function(self, context, output) if #(self.data) == 0 then return end output(' ') if self.data.title then output(' ') self:expandItem(self.data.title, context, output) output(' ') output('') else output(' class="navbox-list navbox-even">') end local hasChildLists = (type(self.data[1]) == "table") and ((self.data[1].elementType == "list") or (self.data[1].elementType == "line")) if hasChildLists then output(' ') else context.odd = not context.odd output('') self:addDataToOutput(context, output, {surround = {'',''}}) output('') end output(' ') end }

-- Like list, except it doesn't format as a list. element.line = { expand = function(self, context, output) output(' ') if self.data.title then output(' ' .. tostring(self.data.title) .. ' ') output(' ') else output(' ') end self:addDataToOutput(context, output, {spacer = ' '}) output(' ') end }

-- Invocation of, or at least the equivalent thereof. element.ni = { expand = function(self, context, output) if self.data.icon then output("") else local iconargs = { item = (self.data.gicon or self.data[1]), size=16, mod=(self.data.mod or context.mod or "undefined") }           output(mw.getCurrentFrame:callParserFunction("#icon", iconargs)) end output(" ") local linkargs = { self.data[2] or self.data[1], (self.data[3] or self.data[2]) or self.data[1] } output(language.link(linkargs)) end }

-- Invocation of, or an equivalent. element.l = { expand = function(self, context, output) output(language.link(self.data)) end }

-- literal string element.text = { expand = function(self, context, output) output(self.data) end }

-- This environment is interposed in the one available to the function in definition.navbox. local definitionEnvironment = {}

-- Note that this doesn't handle arguments! --local function invokeInDefinition(func)   local origEnv = getfenv(func)    local newEnv = {}    setmetatable(newenv, { __index = function(tab, key)        return definitionEnvironment[key] or origEnv[key]    end})    setfenv(func, newEnv)    local result = func    setfenv(func, origEnv)    return result end

-- stub because getfenv doesn't exist. local function invokeInDefinition(func) return func end

local function createElement(name, metatable, args) local newthing = { elementType = name, data = {} } setmetatable(newthing, element[name])

local named = {} local positional = {} for k,v in pairs(args) do       if type(k) == "number" then positional[k] = v       else named[k] = v       end end

for k,v in pairs(named) do       if type(v) == "function" then newthing.data[k] = invokeInDefinition(v) else newthing.data[k] = v       end end

for i,v in ipairs(positional) do       if type(v) == "function" then for j,w in ipairs(invokeInDefinition(v)) do               newthing.data[#(newthing.data)+1] = w            end else newthing.data[#(newthing.data)+1] = v       end end

return newthing end

for name, metatable in pairs(element) do   if type(metatable) == "table" then metatable.__index = function(tab, key) return (rawget(tab, key) or rawget(metatable, key)) or rawget(element, key) end definitionEnvironment[name] = function(args) return createElement(name, metatable, args) end end end

local p = {}

function p.navbox(frame) local navboxModule = require("Module:Navbox/" .. frame.args[1]) local navboxData = navboxModule.navbox(       definitionEnvironment.navbox,        definitionEnvironment.highlightline,        definitionEnvironment.group,        definitionEnvironment.list,        definitionEnvironment.line,        definitionEnvironment.ni,        definitionEnvironment.l)    local context = {} context.name = frame.args.name context.collapse = frame.args.collapse context.opengroups = {} -- opengroups is a set, not a list. for str in string.gmatch(frame.args.opengroups or '', "%S+") do       context.opengroups[str] = true end local out = "" local output = function(text) out = out .. tostring(text or '') end navboxData:expand(context, output) return out end

return p