-- 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 size = args.size or '16px' 	local span = mw.html.create('span')  	span 		:cssText(args[cfg.argumentNames.style]) 		:attr('title', label) 		:wikitext( 			string.format( 				'%s|' .. size .. '%s|class=noviewer|alt=', 				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