-- -- This module handles demonstration and list of possible values for all storm -- category templates! Please test this module on Module:Storm categories/demo/doc -- before publishing to avoid errros. Thank you! -- local colorRatio = require("Module:Color contrast")._ratio local TableTools = require("Module:TableTools") local stormcats = require("Module:Storm categories") local cats = require("Module:Storm categories/categories").cats local colors = require("Module:Storm categories/colors").colors local icons = require("Module:Storm categories/icons").icons local p = {}  local customKeys = {} for k, v in pairs(colors) do 	table.insert(customKeys, k) end for k, v in pairs(icons) do 	table.insert(customKeys, k) end TableTools.removeDuplicates(customKeys)  function tableEmpty(_table) 	for k, v in pairs(_table) do 		return false 	end 	return true end  --- Generates and renderds the demo table. -- @param frame The Scribunto frame. function p.demo(frame) 	local plain = (frame.args["plain"] or frame:getParent().args["plain"] or "") ~= "" 	local verbose = (frame.args["verbose"] or frame:getParent().args["verbose"] or "") ~= "" 	 	local legend = setmetatable({}, { refgroupname = "" }) 	local errors = setmetatable({}, { refgroupname = "E" }) 	local warnings = setmetatable({}, { refgroupname = "W" }) 	 	--- Builds the entire reflist 	-- @param _group The group to build for. Uses `legend` by default. 	function buildReflist(_group) 		group = _group or legend 		return frame:expandTemplate{ title = 'reflist', args = { 			group = getmetatable(group)["refgroupname"] 		} } 	end 	 	--- Returns a single <ref> tag containing the legend provided and 	-- attaches the note definition to the list of notes. 	-- @param details The details of the legend. 	-- @param _group The group to write in. Uses `legend` by default. 	function createLegend(details, _group) 		hash = string.sub(mw.hash.hashValue("md5", details), 0, 8) 		group = _group or legend 		if group[hash] == nil then 			group[hash] = plain and "" or frame:extensionTag{  				-- details 				name = 'ref', 				content = mw.ustring.gsub(details, "%[%[File:([^%|]+)[^%]]*%]%]", ":File:%1"), 				args = {  					name = hash,  					group = getmetatable(group)["refgroupname"] 				} 			} 		end 		 		--  		return plain and "" or frame:extensionTag{ name = 'ref', args = {  			name = hash, 			group = getmetatable(group)["refgroupname"] 		} } 	end 	 	--- Checks for color contrast issues and tags accordingly. 	-- Returns the refernce tag, so this should be used in conjunction with an 	-- HTML node's :wikitext function. 	function contrastCheck(color) 		local catColorBlackRatio = colorRatio({ "#" .. color, "black" }) 		local catColorLinkRatio = colorRatio({ "#" .. color, "#0645ad" }) 		local catColorVisitedLinkRatio = colorRatio({ "#" .. color, "#0b0080" }) 		 		local finalWikitext = "" 		if catColorBlackRatio == "?" or catColorLinkRatio == "?" or catColorVisitedLinkRatio == "?" then 			finalWikitext = finalWikitext .. 				createLegend("This color must be a hexadecimal color.", errors) 		else 			if catColorBlackRatio < 4.5 then 				finalWikitext = finalWikitext .. 					createLegend("This color has contrast issues with black (not WCAG 2.0 AA-compatible). It will be unusable on all infoboxes and storm season summaries.", errors) 			end 			if catColorLinkRatio < 4.5 then 				finalWikitext = finalWikitext .. 					createLegend("This color has contrast issues with links (not WCAG 2.0 AA-compatible). It should not be used in conjunction with a link.", warnings) 			end 			if catColorVisitedLinkRatio < 4.5 then 				finalWikitext = finalWikitext .. 					createLegend("This color has contrast issues with visited links (not WCAG 2.0 AA-compatible with #0b0080). It should not be used in conjunction with a visited link.", warnings) 			end 			if actualCat == "c0c0c0" and cat[sortkey] ~= 0 then 				finalWikitext = finalWikitext .. 					createLegend("This category is using a color reserved specifically for the \"unknown\" category.", warnings) 			end 		end 		return finalWikitext 	end 	 	function colorInfo(color, verbose, extra) 		local catColorBlackRatio = colorRatio({ "#" .. color, "black" }) 		local catColorLinkRatio = colorRatio({ "#" .. color, "#0645ad" }) 		local catColorVisitedLinkRatio = colorRatio({ "#" .. color, "#0b0080" }) 		 		local nc = tostring(mw.html.create("abbr") 			:wikitext("NC") 			:attr("title", "Does not satisfy the minimum WCAG 2.1 compliance level for color contrast (AA)") 		); 		local aa = tostring(mw.html.create("abbr") 			:wikitext("AA") 			:attr("title", "WCAG 2.1 Level AA: Acceptable compliance") 		); 		local aaa = tostring(mw.html.create("abbr") 			:wikitext("AAA") 			:attr("title", "WCAG 2.1 Level AAA: Optimal compliance") 		); 		 		function contrastLevel(contrast) 			return contrast >= 7 and aaa or (contrast >= 4.5 and aa or nc) 		end 		 		return mw.html.create("td") 			:attr("data-sort-value", math.min(catColorBlackRatio)) 			:wikitext( 				"#" .. color .. (extra or "") .. contrastCheck(color) .. (verbose and ("" 				.. tostring( 					mw.html.create("abbr") 						:attr("title", "Contrast to black") 						:wikitext("CTB") 				) .. ": " .. string.format("%.2f", catColorBlackRatio) .. " (" .. contrastLevel(catColorBlackRatio) .. ")" 				.. tostring( 					mw.html.create("abbr") 						:attr("title", "Contrast to links") 						:wikitext("CTL") 				) .. ": " .. string.format("%.2f", catColorLinkRatio) .. " (" .. contrastLevel(catColorLinkRatio) .. ")" 				.. tostring( 					mw.html.create("abbr") 						:attr("title", "Contrast to visited links") 						:wikitext("CTVL") 				) .. ": " .. string.format("%.2f", catColorVisitedLinkRatio) .. " (" .. contrastLevel(catColorVisitedLinkRatio) .. ")") or "") 			) 	end 	 	local categoryTable = mw.html.create("table") 		:addClass("wikitable") 		:addClass("sortable") 		:attr("style", "width: 100%") 		 	categoryTable 		:node( 			mw.html.create("tr") 				:node(mw.html.create("th"):wikitext("Icon") 					:attr("class", "unsortable") 					:attr("rowspan", "2") 					:css("width", "0")) 				:node(mw.html.create("th"):wikitext("ID") 					:attr("rowspan", "2")) 				:node(mw.html.create("th"):wikitext("Name") 					:attr("colspan", "2")) 				:node(mw.html.create("th"):wikitext("Color") 					:attr("rowspan", "2") 					:attr("colspan", "2")) 				:node(mw.html.create("th"):wikitext("Sortkey") 					:attr("colspan", "2")) 		):node( 			mw.html.create("tr") 				:node(mw.html.create("th"):wikitext("Basin")) 				:node(mw.html.create("th"):wikitext("Name")) 				:node(mw.html.create("th"):wikitext("Basin")) 				:node(mw.html.create("th"):wikitext("Sortkey") 					:attr("data-sort-type", "number")) 		) 	 	for name, cat in TableTools.sortedPairs(cats) do 		local rows = { mw.html.create("tr") } 		local row = rows[1] 		 		local actualIcon = stormcats._icon(name) 		local icon = mw.html.create("td") 			:wikitext(actualIcon) 		if cat["icon"] ~= nil and actualIcon ~= cat["icon"] then 			icon:wikitext( 				createLegend("Overriden from original icon (" .. cat["icon"] .. ")") 			) 		end 			 		local id = mw.html.create("td") 			:wikitext(name) 		local actualColor = stormcats._color(name) 		local colorPreview = mw.html.create("td") 			:attr("style", "background-color: #" .. actualColor .. "; padding: 0; width: 1.8em") 		local color = colorInfo( 			actualColor, 			verbose, 			actualColor ~= cat["color"] and createLegend( 				"Overriden from original color ( #" 				.. cat["color"] 				.. ")" 			) or "" 		):css("width", "0"):css("white-space", "nowrap") 		 		local sortkeyCategory = mw.html.create("td") 			:attr("data-sort-value", cat["sortkey"]) 		local sortkey = mw.html.create("td") 			:attr("data-sort-value", cat["sortkey"]) 			:wikitext(cat["sortkey"]) 			 		if cat["sortkey"] < 0 then 			sortkeyCategory:wikitext("Invalid") 		elseif cat["sortkey"] < 20000 then 			sortkeyCategory:wikitext("Global") 		elseif cat["sortkey"] < 30000 then 			sortkeyCategory:wikitext("Historical") 		elseif cat["sortkey"] < 40000 then 			sortkeyCategory:wikitext("SWIO") 		elseif cat["sortkey"] < 50000 then 			sortkeyCategory:wikitext("Aus/Fiji") 		elseif cat["sortkey"] < 60000 then 			sortkeyCategory:wikitext("NIO") 		elseif cat["sortkey"] < 80000 then 			sortkeyCategory:wikitext("WPAC") 		elseif cat["sortkey"] < 90000 then 			sortkeyCategory:wikitext("Atl/EPac/SAtl") 		elseif cat["sortkey"] < 100000 then 			sortkeyCategory:attr("style", "color: gray") 			sortkeyCategory:wikitext("Global") 		else 			sortkeyCategory:wikitext("Invalid") 		end 		 		if type(cat["name"]) == "string" then 			local name = mw.html.create("td") 				:attr("colspan", "2") 				:wikitext(cat["name"]) 			row:node(icon) 			row:node(id) 			row:node(name) 		else 			local nameTableLength = TableTools.size(cat["name"]) 			icon:attr("rowspan", nameTableLength) 			id:attr("rowspan", nameTableLength) 			colorPreview:attr("rowspan", nameTableLength) 			color:attr("rowspan", nameTableLength) 			sortkeyCategory:attr("rowspan", nameTableLength) 			sortkey:attr("rowspan", nameTableLength) 			 			row:node(icon) 			row:node(id) 			local firstDone = false 			for key, basinName in TableTools.sortedPairs(cat["name"]) do 				if firstDone then 					local nameRow = mw.html.create("tr") 					 					nameRow 						:node(mw.html.create("td"):wikitext(key)) 						:node(mw.html.create("td"):wikitext(basinName)) 						 					table.insert(rows, nameRow)	 				else 					firstDone = true 					row 						:node(mw.html.create("td"):wikitext(key)) 						:node(mw.html.create("td"):wikitext(basinName)) 				end 			end 		end 		 		row:node(colorPreview) 		row:node(color) 		row:node(sortkeyCategory) 		row:node(sortkey) 		 		for _, _row in TableTools.sortedPairs(rows) do 			categoryTable:node(_row) 		end 	end 	 	for name, _ in TableTools.sortedPairs(TableTools.listToSet(customKeys)) do 		if cats[name] == nil then 			local row = mw.html.create("tr") 			 			local icon = stormcats._icon(name, true) 			row 				:node(mw.html.create("td") 					:wikitext( 						icon ~= nil 						and icon 						or "N/A" 					)) 				:node(mw.html.create("td"):wikitext(name)) 			 			local color = stormcats._color(name, true) 			-- Add more conditions eventually 			if color ~= nil then 				row 					:node(mw.html.create("td") 						:attr("colspan", "2") 						:wikitext("not available")) 					:node(mw.html.create("td") 						:attr("style", "background-color: #" .. color .. "; padding: 0; width: 1.8em")) 					:node(colorInfo(color, verbose)) 						:css("width", "0") 						:css("white-space", "nowrap") 					:node(mw.html.create("td") 						:attr("colspan", "2") 						:wikitext("not available")) 			else 				row 					:node(mw.html.create("td") 						:attr("colspan", "6") 						:wikitext("not available")) 			end 			categoryTable:node(row) 		end 	end 	 	out = "" 	if not plain and not tableEmpty(errors) then 		out = out 			.. tostring(mw.html.create("h4"):wikitext("Error")) 		    .. tostring(mw.html.create("p"):wikitext("This table contains errors than need to be addressed immediately, as it may cause errors on a large amount of pages.")) 		    .. buildReflist(errors) 	end 	if not plain and not tableEmpty(legend) then 		out = out 			.. tostring(mw.html.create("h4"):wikitext("Legend")) 		    .. buildReflist(legend) 	end 	out = out .. tostring(categoryTable) 	if not plain and not tableEmpty(warnings) then 		out = out 			.. tostring(mw.html.create("h4"):wikitext("Warnings")) 		    .. tostring(mw.html.create("p"):wikitext("This table contains warnings than should be addressed. Please note that some warnings cannot be fully addressed without changes that would require consensus.")) 		    .. buildReflist(warnings) 	end 	 	return mw.text.trim(out) end  return p