Module:Typhoon warnings table

From Wikipedia, the free encyclopedia

local getArgs = require('Module:Arguments').getArgs
local yesno = require("Module:Yesno")
local stormColor = require("Module:Tropical cyclone categories")._color
local zh = require("Module:Lang-zh")._Zh

local p = {}

--------------------------------------------------------------------------------
-- Global functions
--------------------------------------------------------------------------------
function p.header(country, time)
	return mw.html.create("tr"):node(
    	mw.html.create("th")
    		:css("font-weight", "normal")
    		:attr("colspan", 3)
    		:wikitext("'''" .. country .. "'''" .. (
    			time and 
    				" (as of " .. time .. ")" or ""
			))
	)
end

--------------------------------------------------------------------------------
---------------------------- COUNTRY-BASED WARNINGS ----------------------------
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- China
--
-- Storm signals in China are defined by the CMA.
--------------------------------------------------------------------------------
-- Note: v (in m/s) = 0.386B^(3/2), where B is the Beaufort force
function p.cma_signals(signal)
	if signal == "4" or signal == "blue" or signal == "b" then
		return {
			level = 4,
			color = "#3265FE",
			name = "Blue typhoon alert",
			name_zh = "台风蓝色预警信号",
			image = "Blue typhoon alert - China.svg",
			details = "Average of [[Beaufort scale|Beaufort force]] 6 (44 km/h; 27 mph) "
		           .. "or higher wind speeds on coasts or land, or gusts of up to Beaufort force 8 "
		           .. "(68 km/h; 42 mph), within 24 hours."
		}
	elseif signal == "3" or signal == "yellow" or signal == "y" then
		return {
			level = 3,
			color = "#FAEC2C",
			name = "Yellow typhoon alert",
			name_zh = "台风黄色预警信号",
			image = "Yellow typhoon alert - China.svg",
			details = "Average of [[Beaufort scale|Beaufort force]] 8 (68 km/h; 42 mph) "
		           .. "or higher wind speeds on coasts or land, or gusts of up to Beaufort force 10 "
		           .. "(95 km/h; 59 mph), within 24 hours."
		}
	elseif signal == "2" or signal == "orange" or signal == "o" then
		return {
			level = 2,
			color = "#F68C1F",
			name = "Orange typhoon alert",
			name_zh = "台风橙色预警信号",
			image = "Orange typhoon alert - China.svg",
			details = "Average of [[Beaufort scale|Beaufort force]] 10 (95 km/h; 59 mph) "
		           .. "or higher wind speeds on coasts or land, or gusts of up to Beaufort force 12 "
		           .. "(125 km/h; 78 mph), within 12 hours."
		}
	elseif signal == "1" or signal == "red" or signal == "r" then
		return {
			level = 1,
			color = "#D62F28",
			name = "Red typhoon alert",
			name_zh = "台风红色预警信号",
			image = "Red typhoon alert - China.svg",
			details = "Average of [[Beaufort scale|Beaufort force]] 12 (125 km/h; 78 mph) "
		           .. "or higher wind speeds on coasts or land, or gusts of up to Beaufort force 14 "
		           .. "(158 km/h; 98 mph), within 6 hours."
		}
	else
		return {
			level = 0,
			color = "#000000",
			name = "Unknown typhoon alert level",
			name_zh = "台风未知预警信号",
			image = "Stop hand nuvola.svg",
			details = "Please make sure that the parameter is a valid signal. "
				   .. "Consult the [[Template:TyphoonWarningsTable#China|documentation]] for more details."
		}
	end
end

function p.cma_level_row(signal)
	return mw.html.create("tr")
		:node(
	    	mw.html.create("td")
	    		:css("width", "700px")
	    		:css("text-align", "center")
	    		:attr("colspan", 3)
	    		:node(
	    			mw.html.create("span")
	    				:css("font-size", "large")
	    				:css("font-weight", "bold")
	    				:wikitext(signal.name)
    			)
    			:wikitext("<br/>" .. zh{["c"] = signal.name_zh, ["labels"] = "no"})
	    		:wikitext("<p>[[File:" .. signal.image .. "|100px|alt=" .. signal.name .. " icon]]</p>")
    			:node(
    				mw.html.create("p")
    					:css("margin", "0")
    					:wikitext(signal.details)
				)
		)
end

function p.china(outputTable, args)
	local CNsignal = args["CNsignal"]
	
	if CNsignal then
		-- Create the header
		outputTable:node(
			p.header("China", args["CNtime"])
    	)
    	
    	local signalData = p.cma_signals(CNsignal)
		outputTable:node(p.cma_level_row(signalData))
		
		-- Create the footer
		if args["CNsource"] then
			outputTable:node(
				mw.html.create("tr"):node(
			    	mw.html.create("td")
			    		:attr("colspan", 3)
			    		:wikitext("Source: " .. args["CNsource"])
		    	)
	    	)
		end
	end
end

--------------------------------------------------------------------------------
-- Hong Kong
--
-- Storm signals in Hong Kong are defined by the HKO.
--------------------------------------------------------------------------------
function p.hko_signals(signal, direction)
	if signal == 1 then
		return {
			name = "Signal No. 01 - Standby Signal",
			image = "No. 01 Standby Signal.png",
			summary = "A tropical cyclone is centred within 800&nbsp;km of the territory."
		}
	elseif signal == 3 then
		return {
			name = "Signal No. 03 - Strong Wind Signal",
			image = "No. 03 Strong Wind Signal.png",
			summary = "Strong winds generally over Hong Kong at sea level are expected in 12 hours.",
			details = "Expect strong winds with a sustained speed of 41–62&nbsp;km/h and gusts of up to 110&nbsp;km/h."
		}
	elseif signal == 8 then
		local direction = (
			(direction == "ne" or direction == "northeast") and "Northeast" or (
			(direction == "nw" or direction == "northwest") and "Northwest" or (
			(direction == "se" or direction == "southeast") and "Southeast" or (
			(direction == "sw" or direction == "southwest") and "Southwest" or (
				nil
			)))))
		if direction then
			return {
				name = "Signal No. 08 - " .. direction .. " Gale or Storm Signal",
				image = "No. 8 " .. direction .. " Gale or Storm Signal.png",
				summary = "Gale or storm-force winds are expected.",
				details = "Expect strong winds with a sustained speed of 63–117&nbsp;km/h from the "
					.. string.lower(direction) ..
				" quadrant and gusts of up to 180&nbsp;km/h."
			}
		else 
			return {
				name = "Invalid Hong Kong Observatory Signal Direction",
				image = "Stop hand nuvola.svg",
				summary = "Signal No. 8 used without providing a valid wind direction.",
				details = "Use <code>8NE</code>, <code>8SE</code>, <code>8NW</code>, or <code>8SW</code> instead. "
				       .. "Consult the [[Template:TyphoonWarningsTable#Hong Kong|documentation]] for more details."
			}
		end
	elseif signal == 9 then
		return {
			name = "Signal No. 09 - Increasing Gale or Storm Signal",
			image = "No. 09 Increasing Gale or Storm Signal.png",
			summary = "Gale or storm-force winds are increasing or expected to increase significantly in strength.",
			details = "Expect strong winds with a sustained speed of 88–117&nbsp;km/h and gusts no faster than 220&nbsp;km/h."
		}
	elseif signal == 10 then
		return {
			name = "Signal No. 10 - Hurricane Signal",
			image = "No. 10 Hurricane Signal.png",
			summary = "Hurricane-force winds.",
			details = "Expect strong winds above 117 km/h and gusts of more than 220 km/h."
		}
	else 
		return {
			name = "Invalid Hong Kong Observatory Signal",
			image = "Stop hand nuvola.svg",
			summary = "A valid storm signal was not provided in the <code>HKsignal</code> parameter.",
			details = "Please make sure that the parameter is a valid signal. "
			       .. "Consult the [[Template:TyphoonWarningsTable#Hong Kong|documentation]] for more details."
		}
	end
end

function p.hong_kong(outputTable, args)
	local HKsignal = args["HKsignal"]
	
	if HKsignal then
		-- Create the header
		outputTable:node(
			p.header("Hong Kong", args["HKtime"])
    	)
    	
    	HKsignal = string.lower(HKsignal)
    	
    	local signal = nil
    	if HKsignal == "1" then
    		signal = p.hko_signals(1)
    	elseif HKsignal == "3" then
    		signal = p.hko_signals(3)
    	elseif HKsignal == "8" then
    		-- Provide the opportunity for an editor to see that a quadrant is 
    		-- required.
    		signal = p.hko_signals(8)
    	elseif HKsignal == "8nw" then
    		signal = p.hko_signals(8, "nw")
    	elseif HKsignal == "8ne" then
    		signal = p.hko_signals(8, "ne")
    	elseif HKsignal == "8sw" then
    		signal = p.hko_signals(8, "sw")
    	elseif HKsignal == "8se" then
    		signal = p.hko_signals(8, "se")
    	elseif HKsignal == "9" then
    		signal = p.hko_signals(9)
    	elseif HKsignal == "10" then
    		signal = p.hko_signals(10)
    	else 
    		signal = p.hko_signals(HKsignal)
		end
	
		outputTable
			:node(
				mw.html.create("tr"):node(
			    	mw.html.create("td")
			    		:css("width", "700px")
			    		:css("text-align", "center")
			    		:attr("colspan", 3)
			    		:node(
			    			mw.html.create("span")
			    				:css("font-size", "large")
			    				:css("font-weight", "bold")
			    				:wikitext(signal.name)
		    			)
			    		:wikitext("<br/>[[File:" .. signal.image .. "|80px|alt=" .. signal.name .. "]]<br/>")
			    		:node(
			    			mw.html.create("p")
			    				:wikitext(signal.summary)
		    			)
			    		:node(
			    			mw.html.create("p")
			    				:css("font-style", "italic")
			    				:wikitext(signal.details)
	    				)
		    	)
	    	)
	
		-- Create the footer
		if args["HKsource"] then
			outputTable:node(
				mw.html.create("tr"):node(
			    	mw.html.create("td")
			    		:attr("colspan", 3)
			    		:wikitext("Source: " .. args["HKsource"])
		    	)
	    	)
    	end
	else
		return ""
	end
end

--------------------------------------------------------------------------------
-- Macau
--
-- Storm signals in the Macau are defined by the SMG.
--------------------------------------------------------------------------------

function p.smg_signals(signal, direction)
	-- Use the same data from HKO but change the associated icons.
	local output = p.hko_signals(signal, direction)
	output.summary = output.summary:gsub( "Hong Kong", "Macau" )
	if signal == 1 then
		output.image = "Aspecto do sinal nº 1 de tempestade tropical de Macau na dia.png"
	elseif signal == 3 then
		output.image = "Aspecto do sinal nº 3 de tempestade tropical de Macau na dia.png"
	elseif signal == 8 then
		local direction = (
			(direction == "ne" or direction == "northeast") and "NE" or (
			(direction == "nw" or direction == "northwest") and "NW" or (
			(direction == "se" or direction == "southeast") and "SE" or (
			(direction == "sw" or direction == "southwest") and "SW" or (
				nil
			)))))
		if direction then
			output.image = "Aspecto do sinal nº 8 " .. direction .. " de tempestade tropical de Macau na dia.png"
		else
			return {
				name = "Invalid Meteorological and Geophysical Bureau Signal Direction",
				image = "Stop hand nuvola.svg",
				summary = "Signal No. 8 used without providing a valid wind direction.",
				details = "Use <code>8NE</code>, <code>8SE</code>, <code>8NW</code>, or <code>8SW</code> instead. "
				       .. "Consult the [[Template:TyphoonWarningsTable#Macau|documentation]] for more details."
			}
		end
	elseif signal == 9 then
		output.image = "Aspecto do sinal nº 9 de tempestade tropical de Macau na dia.png"
	elseif signal == 10 then
		output.image = "Aspecto do sinal nº 10 de tempestade tropical de Macau na dia.png"
	else 
		return {
			name = "Invalid Meteorological and Geophysical Bureau Signal",
			image = "Stop hand nuvola.svg",
			summary = "A valid storm signal was not provided in the <code>MOsignal</code> parameter.",
			details = "Please make sure that the parameter is a valid signal. "
			       .. "Consult the [[Template:TyphoonWarningsTable#Macau|documentation]] for more details."
		}
	end
	return output
end

function p.macau(outputTable, args)
	local MOsignal = args["MOsignal"]
	
	if MOsignal then
		-- Create the header
		outputTable:node(
			p.header("Macau", args["MOtime"])
    	)
    	
    	MOsignal = string.lower(MOsignal)
    	
    	local signal = nil
    	if MOsignal == "1" then
    		signal = p.smg_signals(1)
    	elseif MOsignal == "3" then
    		signal = p.smg_signals(3)
    	elseif MOsignal == "8" then
    		-- Provide the opportunity for an editor to see that a quadrant is 
    		-- required.
    		signal = p.smg_signals(8)
    	elseif MOsignal == "8nw" then
    		signal = p.smg_signals(8, "nw")
    	elseif MOsignal == "8ne" then
    		signal = p.smg_signals(8, "ne")
    	elseif MOsignal == "8sw" then
    		signal = p.smg_signals(8, "sw")
    	elseif MOsignal == "8se" then
    		signal = p.smg_signals(8, "se")
    	elseif MOsignal == "9" then
    		signal = p.smg_signals(9)
    	elseif MOsignal == "10" then
    		signal = p.smg_signals(10)
    	else 
    		signal = p.smg_signals(MOsignal)
		end
	
		outputTable
			:node(
				mw.html.create("tr"):node(
			    	mw.html.create("td")
			    		:css("width", "700px")
			    		:css("text-align", "center")
			    		:attr("colspan", 3)
			    		:node(
			    			mw.html.create("span")
			    				:css("font-size", "large")
			    				:css("font-weight", "bold")
			    				:wikitext(signal.name)
		    			)
			    		:wikitext("<br/>[[File:" .. signal.image .. "|80px|alt=" .. signal.name .. "]]<br/>")
			    		:node(
			    			mw.html.create("p")
			    				:wikitext(signal.summary)
		    			)
			    		:node(
			    			mw.html.create("p")
			    				:css("font-style", "italic")
			    				:wikitext(signal.details)
	    				)
		    	)
	    	)
	
		-- Create the footer
		if args["MOsource"] then
			outputTable:node(
				mw.html.create("tr"):node(
			    	mw.html.create("td")
			    		:attr("colspan", 3)
			    		:wikitext("Source: " .. args["MOsource"])
		    	)
	    	)
    	end
	else
		return ""
	end
end

--------------------------------------------------------------------------------
-- Philippines
--
-- Storm signals in the Philippines are defined by the PAGASA.
--------------------------------------------------------------------------------

p.pagasa_signals = {
	s5 = {
		name = "Signal #5",
		color = "#CD77CD",
		speed = " 185&nbsp;km/h (115&nbsp;mph) or greater",
		time = "12 hours"
	},
	s4 = {
		name = "Signal #4",
		color = "#FF6060",
		speed = "118–184&nbsp;km/h (73–114&nbsp;mph)",
		time = "12 hours"
	},
	s3 = {
		name = "Signal #3",
		color = "#FFAA00",
		speed = "89–117&nbsp;km/h (55–72&nbsp;mph)",
		time = "18 hours"
	},
	s2 = {
		name = "Signal #2",
		color = "#FFF200",
		speed = "62–88&nbsp;km/h (39–54&nbsp;mph)",
		time = "24 hours"
	},
	s1 = {
		name = "Signal #1",
		color = "#00AAFF",
		speed = "39–61&nbsp;km/h (24–38&nbsp;mph)",
		time = "36 hours"
	}
}

function p.pagasa_row(signal, data, args)
	return mw.html.create("tr")
		:node(
	    	mw.html.create("td")
	    		:css("width", "12.6em")
	    		:css("text-align", "center")
	    		:css("vertical-align", "middle")
	    		:css("color", "black")
	    		:css("background-color", p.pagasa_signals[signal].color)
	    		:wikitext("'''" .. p.pagasa_signals[signal].name .. "'''<br/>")
	    		:wikitext(
	    			"''Winds of " .. p.pagasa_signals[signal].speed .. (
						yesno(args["PHhistorical"]) and "" or " are " .. (
							yesno(args["PHactive"]) and "prevailing or " or ""
						) .. "expected to occur within " .. 
							p.pagasa_signals[signal].time	
					)  .. ".''"
    			)
		)
		:node(
			mw.html.create("td")
			    :css("vertical-align", "middle")
			    :wikitext("\n" .. data .. "\n")
		)
end

function p.philippines(outputTable, args)
	local PH1 = args["PH1"]
	local PH2 = args["PH2"]
	local PH3 = args["PH3"]
	local PH4 = args["PH4"]
	local PH5 = args["PH5"]
	
	if PH1 or PH2 or PH3 or PH4 or PH5 then
		-- Create the header
		outputTable:node(
			p.header("Philippines", args["PHtime"])
    	)
    	
    	if PH5 then
    		outputTable:node(p.pagasa_row("s5", PH5, args))
    	end
    	if PH4 then
    		outputTable:node(p.pagasa_row("s4", PH4, args))
    	end
    	if PH3 then
    		outputTable:node(p.pagasa_row("s3", PH3, args))
    	end
    	if PH2 then
    		outputTable:node(p.pagasa_row("s2", PH2, args))
    	end
    	if PH1 then
    		outputTable:node(p.pagasa_row("s1", PH1, args))
    	end
	
		-- Create the footer
		if args["PHsource"] then
			outputTable:node(
				mw.html.create("tr"):node(
			    	mw.html.create("td")
			    		:attr("colspan", 3)
			    		:wikitext("Source: " .. args["PHsource"])
		    	)
	    	)
    	end
	else
		return ""
	end
end

--------------------------------------------------------------------------------
-- South Korea
--
-- Typhoon advisories and warnings in Korea are defined by the KMA.
--------------------------------------------------------------------------------
function p.south_korea(outputTable, args, frame)
	if args["KRA"] or args["KRW"] then
		-- Create the header
		outputTable:node(
			p.header("South Korea", args["KRtime"])
    	)
	end
	
	if args["KRA"] then
		outputTable:node(mw.html.create("tr"):node(
			mw.html.create("td")
				:node(
					mw.html.create("span")
	    				:css("font-size", "large")
	    				:css("font-weight", "bold")
	    				:node(
	    					mw.html.create("span")
	    						:attr("aria-role", "presentation")
	    						:css("display", "inline-block")
	    						:css("background-color", "#ffb300")
	    						:css("height", "1.2em")
	    						:css("width", "0.4em")
	    						:css("margin-right", "0.4em")
	    						:css("vertical-align", "middle")
						)
	    				:wikitext("Typhoon Advisory")
				)
				:node(
					mw.html.create("p")
						:wikitext("A typhoon advisory has been issued for the following areas.")
						:wikitext(args["KRsource"] and (" Refer to official information for more details.") or "")
				)
				:wikitext("\n" .. args["KRA"] .. "\n")
				:node(
					mw.html.create("p")
						:wikitext("''Expect strong winds, [[wind wave]]s, heavy rain, and [[storm surge]]s.''")
				)
		))
	end
	
	if args["KRW"] then
		outputTable:node(mw.html.create("tr"):node(
			mw.html.create("td")
				:node(
					mw.html.create("span")
	    				:css("font-size", "large")
	    				:css("font-weight", "bold")
	    				:node(
	    					mw.html.create("span")
	    						:attr("aria-role", "presentation")
	    						:css("display", "inline-block")
	    						:css("background-color", "red")
	    						:css("height", "1.2em")
	    						:css("width", "0.4em")
	    						:css("margin-right", "0.4em")
	    						:css("vertical-align", "middle")
						)
	    				:wikitext("Typhoon Warning")
				)
				:node(
					mw.html.create("p")
						:wikitext("A typhoon warning has been issued for the following areas.")
						:wikitext(args["KRsource"] and (" Refer to official information for more details.") or "")
				)
				:wikitext("\n" .. args["KRW"] .. "\n")
				:node(
					mw.html.create("p")
						:wikitext("''Expect precipitation over 200mm or warning-level winds, [[wind wave]]s, or storm surges.''")
				)
		))
	end
	
	if args["KRA"] or args["KRW"] then
		-- Create the footer
		if args["KRsource"] then
			outputTable:node(
				mw.html.create("tr"):node(
			    	mw.html.create("td")
			    		:attr("colspan", 3)
			    		:wikitext("Source: " .. args["KRsource"])
		    	)
	    	)
    	end
	end
end

--------------------------------------------------------------------------------
-- Taiwan
--
-- Sea and Land Typhoon Warnings in Taiwan are defined by the CWB.
--------------------------------------------------------------------------------
function p.taiwan(outputTable, args, frame)
	if args["TW"] then
		-- Create the header
		outputTable:node(
			p.header("Taiwan", args["TWtime"])
    	)
    	
    	-- Graphic cell
    	local graphic = mw.html.create("td")
			:attr("rowspan", "4")
			:css("padding-right", "32px")
			:css("white-space", "nowrap")
			:css("box-sizing", "border-box")
			:node(
				-- Red block
				mw.html.create("div")
					:css("display", "inline-block")
					:css("height", "64px")
					:css("width", "24px")
					:css("margin-right", "16px")
					:css("background-color", "red")
					:css("vertical-align", "top")
			)
			:wikitext(
				-- Icon
				"[[File:CWB Typhoon Warning Icon.svg|64px|alt=Sea and Land Typhoon Warning icon]]"
			)
			
		local content = mw.html.create("td")
			:node(
				mw.html.create("span")
    				:css("font-size", "large")
    				:css("font-weight", "bold")
    				:wikitext("Sea and Land Typhoon Warning")
			)
			:node(
				mw.html.create("p")
					:wikitext("A sea and land typhoon warning has been issued for the following areas.")
			)
			:node(frame:expandTemplate{ title = 'div col', args = { colwidth = "10em" } }) -- No module equivalent
			:wikitext("\n" .. args["TW"] .. "\n")
			:node(frame:expandTemplate{ title = 'div col end' }) -- No module equivalent
			:node(
				mw.html.create("p")
					:wikitext("''Expect winds stronger than 55 km/h within 18 hours.''")
			)
    	
    	-- Create the inner table
    	local iTable = mw.html.create("table")
    	iTable:node(
    		mw.html.create("tr")
    			:node(graphic)
    			:node(content)
		)
		
		outputTable:node(
			mw.html.create("tr"):node(
		    	mw.html.create("td")
		    		:attr("colspan", "3")
		    		:node(iTable)
	    	)
    	)
		
		-- Create the footer
		if args["TWsource"] then
			outputTable:node(
				mw.html.create("tr"):node(
			    	mw.html.create("td")
			    		:attr("colspan", 3)
			    		:wikitext("Source: " .. args["TWsource"])
		    	)
	    	)
    	end
	else
		return ""
	end
end

--------------------------------------------------------------------------------
---------------------------- AGENCY-BASED WARNINGS -----------------------------
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- National Weather Service, Tiyan, Guam
--
-- Storm signals (Typhoon and Tropical Storm Warnings, etc.) issued by the NWS.
--------------------------------------------------------------------------------
p.nws_criteria = {
	tyw = {
		name = "Typhoon Warning",
		color = stormColor("cat5"),
		description = "Typhoon conditions ($speed) expected within $duration.",
		speed = "over 118&nbsp;km/h (74&nbsp;mph)",
		duration = "24 hours"
	},
	tya = {
		name = "Typhoon Watch",
		color = stormColor("cat4"),
		description = "Typhoon conditions ($speed) possible within $duration.",
		speed = "over 118&nbsp;km/h (74&nbsp;mph)",
		duration = "24 hours"
	},
	trw = {
		name = "Tropical Storm Warning",
		color = stormColor("cat3"),
		description = "Tropical storm conditions ($speed) expected within $duration.",
		speed = "88–117&nbsp;km/h (55–73&nbsp;mph)",
		duration = "24 hours"
	},
	tra = {
		name = "Tropical Storm Watch",
		color = stormColor("cat2"),
		description = "Tropical storm conditions ($speed) possible within $duration.",
		speed = "88–117&nbsp;km/h (55–73&nbsp;mph)",
		duration = "24 hours"
	},
	gaw = {
		name = "Gale Warning",
		color = stormColor("ts"),
		description = "Gale conditions ($speed) expected within $duration.",
		speed = "63-87&nbsp;km/h (39–72&nbsp;mph)",
		duration = "24 hours"
	}
}

function p.nws_row(signal, data)
	return mw.html.create("tr")
		:node(
	    	mw.html.create("td")
	    		:css("width", "14em")
	    		:css("text-align", "center")
	    		:css("vertical-align", "middle")
	    		:css("color", "black")
	    		:css("background-color", "#" .. p.nws_criteria[signal].color)
	    		:wikitext("'''" .. p.nws_criteria[signal].name .. "'''<br/>")
	    		:wikitext(
	    			"''" ..
	    				string.gsub(
	    					string.gsub(
		    					p.nws_criteria[signal].description,
		    					"%$speed",
		    					p.nws_criteria[signal].speed
	    					),
	    					"%$duration",
	    					p.nws_criteria[signal].duration
    					)
	    			.. "''"
    			)
		)
		:node(
			mw.html.create("td")
			    :css("vertical-align", "middle")
			    :wikitext("\n" .. data .. "\n")
		)
end

function p.nws(outputTable, args)
	local TYW = args["TYW"]
	local TYA = args["TYA"]
	local TRW = args["TRW"] or args["TSW"]
	local TRA = args["TRA"] or args["TSA"]
	local GAW = args["GAW"]
	
	if TYW or TYA or TRW or TRA or GAW then
		-- Create the header
		outputTable:node(
			p.header("National Weather Service", args["NWStime"])
    	)
    	
    	if TYW then
    		outputTable:node(p.nws_row("tyw", TYW))
    	end
    	if TYA then
    		outputTable:node(p.nws_row("tya", TYA))
    	end
    	if TRW then
    		outputTable:node(p.nws_row("trw", TRW))
    	end
    	if TRA then
    		outputTable:node(p.nws_row("tra", TRA))
    	end
    	if GAW then
    		outputTable:node(p.nws_row("gaw", GAW))
    	end
	
		-- Create the footer
		if args["NWSsource"] then
			outputTable:node(
				mw.html.create("tr"):node(
			    	mw.html.create("td")
			    		:attr("colspan", 3)
			    		:wikitext("Source: " .. args["NWSsource"])
		    	)
	    	)
    	end
	else
		return ""
	end
end

--------------------------------------------------------------------------------
-- Template invocation features
--------------------------------------------------------------------------------

function p.main(frame)
	local args = getArgs(frame, {
		trim = true,
		removeBlanks = true
	})

    return p._main(frame, args)
end

function p._main(frame, args)
	-- Generate table
	local finalTable = mw.html.create("table")
		:attr("class", "wikitable typhoon-warnings-table")
		
	if yesno(args["float"]) then
		finalTable:css("float", args["align"] or "left")
	elseif yesno(args["demo"]) then
		finalTable:css("float", "right")
	end
	
	if args["width"] then
		finalTable:css("width", args["width"])
	end
	
	-- Save the table prior to row insertion
	local premake = tostring(finalTable);
	
	-- Generate rows
	p.china(finalTable, args)
	p.hong_kong(finalTable, args)
	p.macau(finalTable, args)
	p.philippines(finalTable, args)
	p.south_korea(finalTable, args, frame)
	p.taiwan(finalTable, args, frame)
	p.nws(finalTable, args)
	
	-- Save the table after row insertion
	local postmake = tostring(finalTable)
	
	-- If there is no difference between the table before insertion and after
	-- insertion, it is fair to assume that there were no arguments given.
	if postmake == premake then
		finalTable:node(
			mw.html.create("tr"):node(
		    	mw.html.create("td")
		    		:attr("colspan", 3)
	    			:css("color", "black")
		    		:css("background-color", "#" .. stormColor("td"))
		    		:wikitext("No '''tropical cyclone watches or warnings''' posted at this time.")
	    	)
    	)
    	postmake = tostring(finalTable)
	end

    -- Output
    return tostring(postmake) 
    	.. (yesno(args["clear"]) and ("\n" .. frame:expandTemplate{
    		title = "clear"
    	}) or "")
end

return p