Module:Storm categories
local categoryData = require(&quot;Module:Storm categories/categories&quot;)
 local colors = require(&quot;Module:Storm categories/colors&quot;).colors
 local icons = require(&quot;Module:Storm categories/icons&quot;).icons
 local cats = categoryData.cats
 local defaultCategory = categoryData.defaultCategory
 local p = {}
 
 function p.color(frame)
 	return p._color(frame.args[1] or frame:getParent().args[1], false)
 end
 
 function p.name(frame)
 	return p._name(
 		frame.args[1] or frame:getParent().args[1],
 		frame.args[2] or frame:getParent().args[2],
 		false
 	)
 end
 
 function p.sortkey(frame)
 	return p._sortkey(frame.args[1] or frame:getParent().args[1], false)
 end
 
 function p.icon(frame)
 	return p._icon(frame.args[1] or frame:getParent().args[1], false)
 end
 
 function p.isEqual(frame)
 	return p._isEqual(
 		frame.args[1] or frame:getParent().args[1],
 		frame.args[2] or frame:getParent().args[2]
 	) and &quot;yes&quot; or &quot;&quot;
 end
 
 function p._normalizeId(category, fallback)
 	-- Normalize if normalizable, fall back to default if not.
 	return (category ~= nil and string.len(category) ~= 0) and
 		string.gsub(string.lower(category), &quot;[^%w]&quot;, &quot;&quot;) or fallback
 end
 
 function p._color(category, nilIfMissing)
 	-- This looks confusing, but it's actually nested ternaries (for nil checks)
 	local color = p._normalizeId(category, defaultCategory)
 		
 	return colors[color] or ((cats[color] or (
 		nilIfMissing
 		and { color = nil }
 		or cats[defaultCategory]
 	)).color)
 end
 
 function p._name(category, basin, nilIfMissing)
 	local name_def = (cats[
 		p._normalizeId(category, defaultCategory)
 	] or (nilIfMissing and { name = nil } or cats[defaultCategory])).name
 	return type(name_def) == &quot;table&quot; and 
 		(
 			name_def[string.lower(basin or &quot;default&quot;)]
 			or name_def[&quot;default&quot;]
 			or (not nilIfMissing and error(&quot;No default name for basin-based category name.&quot;) or nil)
 		) 
 		or name_def
 end
 
 function p._sortkey(category, nilIfMissing)
 	-- This looks confusing, but it's actually nested ternaries (for nil checks)
 	return (cats[
 		p._normalizeId(category, defaultCategory)
 	] or (nilIfMissing and { sortkey = nil } or cats[defaultCategory])).sortkey
 end
 
 function p._icon(category, nilIfMissing)
 	-- This looks confusing, but it's actually nested ternaries (for nil checks)
 	local icon = p._normalizeId(category, defaultCategory)
 		
 	return icons[icon] or (cats[icon] ~= nil and (
 		cats[icon].icon or cats[&quot;tropicalcyclone&quot;].icon
 	) or (not nilIfMissing and cats[defaultCategory].icon) or nil)
 end
 
 function p._isEqual(category1, category2)
 	-- Checks if two IDs are equal.
 	-- An {{#ifeq}} check does not consider aliases. This function compares two
 	-- IDs in a way that considers category aliases. This works because alias
 	-- assignments are references to the actual table containing the category
 	-- info found in the main category table.
 	-- 
 	-- `false` will be returned if at least one of the categories are nil or
 	-- if the category is not defined.
 	local _category1 = p._normalizeId(category1)
 	local _category2 = p._normalizeId(category2)
 	return (_category1 == _category2 and _category1 ~= nil) or
 		cats[ _category1 ] == cats[ _category2 ] and
 			-- `false` if categories are undefined and not equal.
 			cats[ _category1 ] ~= nil
 end
 
 function p.demo(frame)
 	return require(&quot;Module:Storm categories/demo&quot;).demo(frame)
 end