Module:Class

-- This module implements Template:Class, Template:Class/icon and -- Template:Class/colour.

local mArguments -- lazily loaded local definitions = mw.loadJsonData('Module:Class/definition.json')

local p = {}

-- Local configuration and messages

local cfg = { defaultCode = 'DEFAULT', classPrefix = 'assess-', globalClass = 'assess', defaultClassSuffix = 'default', unboldClassSuffix = 'unbold', catRootFormat = '%s %s', catTopicFormat = '%s %s articles', catBasicFormat = '%s articles', categoryFormat = '%s', templateLocation = 'Template:Class', iconTemplateLocation = 'Template:Class/icon', colourTemplateLocation = 'Template:Class/colour', stylesLocation = 'Module:Class/styles.css', baseColourPath = {'colour', 'base'}, iconPath = {"icon", "file"}, iconDefaultPath = {"icon", "default"}, iconAttribPath = {"icon", "requiresAttribution"}, fullLabelPath = {"labels", "full"}, shortLabelPath = {"labels", "short"}, categoryRootPath = {"categoryRoot"}, tooltipPath = {"labels", "tooltip"}, yes = "yes", no = "no", argumentNames = { class = "class", style = "style" },	getOptions = { --First item is localized argument name, second is case-sensitivity bold = {"bold", false}, header = {"header", false}, image = {"image", false}, rowspan = {"rowspan", false}, fullcategory = {"fullcategory", true}, category = {"category", true}, topic = {"topic", true} } }

-- Argument helper functions

local function getRawArgs(frame, wrapper) --Retrieves the arguments from the frame mArguments = mArguments or require('Module:Arguments') return mArguments.getArgs(frame, {		wrappers = wrapper,		trim = false,		removeBlanks = false	}) end

local function makeInvokeFunction(func, wrapper) --Wraps a general function into an invokable version return function (frame) local args = getRawArgs(frame, wrapper) return func(args) end end

-- String helper functions

local function trim(str) --Trims strings, passes through non-strings without modification return (type(str) == 'string') and mw.text.trim(str) or str end

local function normalizeValue(val) --Normalizes strings, particularly class codes if type(val) == 'string' then val = trim(val):lower end if val == '' then val = nil end return val end

local function ucfirst(str) --Capitalizes the first character of a string return mw.ustring.upper(mw.ustring.sub(str, 1, 1)) .. mw.ustring.sub(str, 2) end

-- Definition helper functions

local function getDefinition(code) --Retrieves the definition and canonical class code for a given code. --Returns two values: the definition object and the canonical class code --string. local canonicalCode = normalizeValue(code) if code == cfg.defaultCode then canonicalCode = code end local class = definitions[canonicalCode] while class and class.alias do		canonicalCode = class.alias class = definitions[class.alias] end if not class then return nil, nil end return class, canonicalCode end

local function getDefault --Shortcut function for retrieving the default definition return getDefinition(cfg.defaultCode) end

local function getProperty(class, default, map) --Retrieves a given property from a string given a class definition, a	--default class definition, and a map for the path to traverse through the --class object. The map should be a sequential table of string property --names, e.g. {"colour", "base"} would retrieve someClass.colour.base local prop, dProp = class, default for k, v in ipairs(map) do		prop = ((type(prop) == 'table') or nil) and prop[v] dProp = ((type(dProp) == 'table') or nil) and dProp[v] end if prop == nil then prop = dProp end return prop end

-- Color functions

function p._colour(code) --Retrieves the base colour for a given code return getProperty(getDefinition(code), getDefault, cfg.baseColourPath) end

function p.colour(frame) --Retrieves the base colour for a given code; is invokable local args = getRawArgs(frame, cfg.colourTemplateLocation) -- Nowiki tags prevent output beginning with "#" from triggering bug 14974. return frame:extensionTag('nowiki', p._colour(args[1])) end

-- Icon functions

function p._icon(args) --Retrieves an icon image and formats it as wikitext local class = getDefinition(args[cfg.argumentNames.class] or args[1]) local default = getDefault local file = getProperty(class, default, cfg.iconPath) local label = getProperty(class, default, cfg.tooltipPath) or		ucfirst(getProperty(class, default, cfg.fullLabelPath)) local attrib = getProperty(class, default, cfg.iconAttribPath) local span = mw.html.create('span')

span :cssText(args[cfg.argumentNames.style]) :attr('title', label) :wikitext(			string.format( '',				file, label, attrib and '' or '|link=' )		)	return tostring(span) end

p.icon = makeInvokeFunction(p._icon, cfg.iconTemplateLocation) --Invokable version of p._icon

-- Class functions

function p._class(args) --Parses its arguments into a table cell with an optional icon, a name --linked to an appropriate category, and appropriate colour styling local classDef, classCode = getDefinition(args[cfg.argumentNames.class] or args[1]) local default = getDefault local iconDefault = getProperty(classDef, default, cfg.iconDefaultPath) local shortLabel = getProperty(classDef, default, cfg.shortLabelPath) local categoryRoot = getProperty(classDef, default, cfg.categoryRootPath) --o is short for "options", go for "get options". Bool true → case-sensitive local o, go = {}, cfg.getOptions for k, v in pairs(go) do		o[k] = v[2] and trim(args[v[1]]) or normalizeValue(args[v[1]]) end

local cell = mw.html.create(o.header and 'th' or 'td') --image=yes forces icon, image=no disables it, otherwise checks default local icon = iconDefault and (o.image ~= cfg.no) or (o.image == cfg.yes) icon = icon and p.icon(args) .. ' ' or ''

local category if o.fullcategory then category = o.fullcategory elseif o.category then category = string.format(cfg.catRootFormat, categoryRoot, o.category) elseif o.topic then category = string.format(cfg.catTopicFormat, categoryRoot, o.topic) else category = string.format(cfg.catBasicFormat, categoryRoot) end local text = string.format(cfg.categoryFormat, category, shortLabel) cell :addClass(cfg.globalClass) :addClass(			o.bold == cfg.no and cfg.classPrefix .. cfg.unboldClassSuffix or nil		) :addClass(cfg.classPrefix .. (classCode or cfg.defaultClassSuffix)) :attr('rowspan', tonumber(o.rowspan)) :wikitext(mw.getCurrentFrame:extensionTag{ name = 'templatestyles', args = {src = cfg.stylesLocation} }, icon, text)

return tostring(cell) end

p.class = makeInvokeFunction(p._class, cfg.templateLocation) --Invokable version of p._class

return p