Documentation for this module may be created at Module:Storm categories/demo/doc

--
-- 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{ 
				-- <ref name="hash">details</ref>
				name = 'ref',
				content = mw.ustring.gsub(details, "%[%[File:([^%|]+)[^%]]*%]%]", "[[:File:%1]]"),
				args = { 
					name = hash, 
					group = getmetatable(group)["refgroupname"]
				}
			}
		end
		
		-- <ref name="hash"/>
		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 [[MOS:COLOR|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 [[MOS:COLOR|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 [[MOS:COLOR|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 ("<br/>"
				.. tostring(
					mw.html.create("abbr")
						:attr("title", "Contrast to black")
						:wikitext("CTB")
				) .. ": " .. string.format("%.2f", catColorBlackRatio) .. " (" .. contrastLevel(catColorBlackRatio) .. ")<br/>"
				.. tostring(
					mw.html.create("abbr")
						:attr("title", "Contrast to links")
						:wikitext("CTL")
				) .. ": " .. string.format("%.2f", catColorLinkRatio) .. " (" .. contrastLevel(catColorLinkRatio) .. ")<br/>"
				.. 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 ({{color box|#"
				.. cat["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 "''<span style=\"color:gray\">N/A</span>''"
					))
				: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("''<span style=\"color:gray\">not available</span>''"))
					: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("''<span style=\"color:gray\">not available</span>''"))
			else
				row
					:node(mw.html.create("td")
						:attr("colspan", "6")
						:wikitext("''<span style=\"color:gray\">not available</span>''"))
			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