Module:Color: Difference between revisions

From Zelda Wiki, the Zelda encyclopedia
Jump to navigation Jump to search
m (support any valid HTML color on talk pages and such)
No edit summary
 
(33 intermediate revisions by 2 users not shown)
Line 1: Line 1:
local utilsError = require("Module:UtilsError")
local p = {}
local h = {}
local data = mw.loadData("Module:Color/Data")
 
local utilsString = require("Module:UtilsString")
local utilsString = require("Module:UtilsString")


local data = mw.loadData("Module:Color/Data")
local CAT_INVALID_ARGS = "[[Category:"..require("Module:Constants/category/invalidArgs").."]]"
local CAT_INVALID_COLOR = "[[Category:Articles using invalid color names]]"


local p = {}
function h.warn(msg)
local utilsError = require("Module:UtilsError")
utilsError.warn(msg)
end


local CAT_DEPRECATED_COLORS = "Category:Articles Using Deprecated Colors"
function h.invalidColor(colorId)
local TransclusionArguments = require("Module:Transclusion Arguments")
h.warn(string.format("<code>%s</code> is not a valid color name. See [[Template:Color]] for a list of supported colors.", colorId))
-- We only store invalid colors for performance reasons
-- Storing all template usages seems to break pages that use the template a lot
TransclusionArguments.store({
module = "Module:Color",
args = {
[1] = colorId,
[2] = text,
},
isValid = false,
})
end


function p.Main(frame)
function p.Main(frame)
local args = frame:getParent().args
local args = frame:getParent().args
return p.color(args[1], args[2])
local coloredText, errorCategories = p.color(args[1], args[2])
return coloredText .. (errorCategories or "")
end
end


Line 18: Line 39:
end
end


-- There is intentationally duplicated code at [[Module:Cite]] - see [[wikipedia:Rule of three]]
-- @return the colored text as a string
-- @return any error categories as a string, or nil
function p.color(colorId, text)
function p.color(colorId, text)
if text == nil or text == "" then
h.warn("<code>text</code> parameter is required.")
return text or "", CAT_INVALID_ARGS
end
if colorId == nil or colorId == "" then
h.warn("<code>color</code> parameter is required.")
return text, CAT_INVALID_ARGS
end
if string.find(colorId, "\n", 1, true) or string.find(colorId, "\r", 1, true) or utilsString.startsWith(colorId, " ") or utilsString.endsWith(colorId, " ") then
h.warn("<code>color</code> argument contains invalid whitespace such as newlines or leading/trailing spaces.")
return text, CAT_INVALID_ARGS
end
local colorData = data.colors[colorId]
local colorData = data.colors[colorId]
-- Backwards compatibility for inputting any valid HTML color
if colorData == nil then
-- In articles we should only allow defined game colors, but we can keep support for any HTML color on user and talk pages to avoid breaking user signatures that use the template this way
h.invalidColor(colorId)
if colorData == nil and (mw.title.getCurrentTitle().isTalkPage or mw.title.getCurrentTitle().nsText == "User") then
return text, CAT_INVALID_COLOR
html = mw.html.create("span")
:css("color", colorId)
:wikitext(text)
return tostring(html)
elseif colorData == nil then
utilsError.warn(string.format("<code>%s</code> is not a valid color name. See [[Template:Color]] for the list of supported colors.", colorId))
return text .. "[[Category:Articles Using Invalid Arguments in Template Calls]]"
end
end
local colorValue = colorData.color
local colorValue = colorData.color
local html = mw.html.create("span")
local html = mw.html.create("span")
:addClass("colored-text")
:addClass("zw-color")
:addClass("colored-text-highlight") -- to distinguish from the colored-text added by [[Module:Color]]
:wikitext(text)
:wikitext(text)
if utilsString.startsWith(colorValue, "linear-gradient") then
if utilsString.startsWith(colorValue, "linear-gradient") then
Line 51: Line 78:
local result = tostring(html)
local result = tostring(html)
if colorData.deprecated then
if colorData.deprecated then
utilsError.warn(string.format("Color <code>%s</code> is being discontinued. Please update it to a current color listed at [[Template:Color]].", colorId))
local action = ""
result = result.."[["..CAT_DEPRECATED_COLORS.."]]"
if type(colorData.deprecated) == "string" then
action = string.format("Use <code>%s</code> instead.", colorData.deprecated)
else
action = "See [[Template:Color]] for a list of supported colors."
end
h.warn(string.format("Color <code>%s</code> is being discontinued. %s", colorId, action))
return result, CAT_INVALID_COLOR
end
end
return result
return result
Line 78: Line 111:
}
}
}
}
local games = game and {game} or Franchise.enum({includeCompilations = true})
local games = game and {game} or Franchise.enum({includeNonfiction = true})
local listAllGames = game == nil
local listAllGames = game == nil
local colorKeys = utilsTable.keys(data.colors)
local colorKeys = utilsTable.keys(data.colors)
-- Remove deprecated colors
colorKeys = utilsTable.filter(colorKeys, function(colorKey)
return not data.colors[colorKey].deprecated
end)
local seenColorKeys = {}
for _, game in ipairs(games) do -- "game" here could also be a book like Encyclopedia
for _, game in ipairs(games) do -- "game" here could also be a book like Encyclopedia
-- The added " " is for doing a whole-word match. e,g. SS should match "SS Green" but not "SSHD Green"
-- The added " " is for doing a whole-word match. e,g. SS should match "SS Green" but not "SSHD Green"
Line 90: Line 130:
{
{
colspan = 3,
colspan = 3,
content = Franchise.display(game),
content = string.format('<span id="%s">[[#%s|%s]]</span>', game, game, Franchise.display(game)), -- creates section links to each game
}
}
})
})
end
end
for _, colorKey in ipairs(gameColorKeys) do
p.addRows(tableRows, gameColorKeys)
local colorData = data.colors[colorKey]
seenColorKeys = utilsTable.concat(seenColorKeys, gameColorKeys)
if not colorData.deprecated then
table.insert(tableRows, {
{
content = " ", -- using whitespace so the cell isn't treated as being empty by utilsLayout
styles = {
["background"] = colorData.color,
}
},
"<b><code>"..colorKey.."</code></b>",
colorData.notes or ""
})
end
end
end
end
local remainingColorKeys = utilsTable.difference(colorKeys, seenColorKeys)
if listAllGames and #remainingColorKeys > 0 then
table.insert(tableRows, {
header = true,
{
colspan = 3,
content = string.format("[[#Other|Other]]</span>"),
}
})
p.addRows(tableRows, remainingColorKeys)
end
return utilsLayout.table({
return utilsLayout.table({
caption = "Text Colors",
caption = "Text Colors",
Line 115: Line 154:
hideEmptyColumns = true,
hideEmptyColumns = true,
})
})
end
function p.addRows(tableRows, gameColorKeys)
for _, colorKey in ipairs(gameColorKeys) do
local colorData = data.colors[colorKey]
if not colorData.deprecated then
table.insert(tableRows, {
{
content = " ", -- using whitespace so the cell isn't treated as being empty by utilsLayout
styles = {
["background"] = colorData.color,
}
},
"<b><code>"..colorKey.."</code></b>",
colorData.notes or ""
})
end
end
end
end


Line 128: Line 184:
params = {
params = {
[1] = {
[1] = {
name = "color",
name = "colorId",
required = true,
required = true,
type = "string",
type = "string",
Line 142: Line 198:
examples = {
examples = {
{"TWWHD Vermilion", "merchant's oath"},
{"TWWHD Vermilion", "merchant's oath"},
{"SSHD Fi Blue", "Demise"},
{"SSHD Blue", "Demise"},
{"invalid color", "foo"},
{"invalid color", "foo"},
{"deprecated color", "bar"},
{
desc = "This template no longer supports arbitrary web colors. Use [[Template:Web Color]] instead. Name all in-game colors at [[Module:Color/Data]].",
args = {"#f2a1b5", "custom color"},
},
{
desc = "Leading/trailing whitespace is not allowed in the color name." ,
args = {"SSHD Blue ", "Demise"},
},
{"SSHD\nBlue", "Demise"},
{
desc = "Both parameters are required.",
args = {"", "missing color"},
},
{"missing text"},
{""},
{},
{
desc = "Color names can be discontinued.",
args = {"deprecated color", "bar"},
},
{"replaced color", "bar"},
}
}
},
},
Line 168: Line 244:
}
}


-- For auto-generated doc at Module:Colors/Data/Documentation
function p.Schemas()
p.Schemas = {
return {
Data = {
-- For auto-generated doc at Module:Color/Documentation
type = "record",
color = {
required = true,
colorName = {
properties = {
type = "string",
{
required = true,
name = "colors",
desc = "The name of a color from [[Module:Color/Data]]",
},
text = {
type = "string",
required = true,
required = true,
type = "map",
desc = "The text to color",
keys = {
},
type = "string",
},
},
-- For auto-generated doc at Module:Color/Data/Documentation
values = {
Data = {
type = "record",
type = "record",
properties = {
required = true,
{
properties = {
name = "color",
{
required = true,
name = "colors",
type = "string",
required = true,
desc = "A [https://developer.mozilla.org/en-US/docs/Web/CSS/hex-color hex color] (with preceding #) or a [https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/linear-gradient linear-gradient].",
type = "map",
},
keys = {
{
type = "string",
name = "notes",
},
type = "string",
values = {
desc = "Anything that editors should know about the color – the context in which it appears, other colors it could be confused with, etc.",
type = "record",
},
properties = {
{
{
name = "deprecated",
name = "color",
type = "boolean",
required = true,
desc = "Set this to true for colors that are to be removed or replaced. Pages using this color are added to [[:"..CAT_DEPRECATED_COLORS.."]].<br/>Please indicate in <code>notes</code> what color should be used instead."
type = "string",
desc = "A [https://developer.mozilla.org/en-US/docs/Web/CSS/hex-color hex color] (with preceding #) or a [https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/linear-gradient linear-gradient].",
},
{
name = "isDefault",
type = "boolean",
desc = "Set this to <code>true</code> when the color is the game's default dialogue color. Used by [[Module:Cite]] to set the default text color for citations."
},
{
name = "defaultFor",
type = "array",
items = { type = "string" },
desc = "A list of wiki page names referring to the characters and/or interfaces whose text uses the color by default. Used by [[Module:Cite]] to set the default text color for citations."
},
{
name = "notes",
type = "string",
desc = "Anything that editors should know about the color – the context in which it appears, other colors it could be confused with, etc.",
},
{
name = "deprecated",
oneOf = {
{
type = "string"
},
{
type = "boolean"
},
},
desc = "The name of the color that should be used instead of this color going forward. If multiple colors apply, just put <code>true</code>.",
},
}
}
}
}
Line 205: Line 314:
}
}
}
}
}
end
 
function p.Documentation()
return {
color = {
params = {"colorName", "text"},
returns = {
"Colored text",
"Error categories, or <code>nil</code> if no errors occurred",
},
cases = {
{
args = {"TMC Blue", "guy in green tights"},
expect = {'<span class="zw-color" style="color:#37acbe">guy in green tights</span>', nil},
},
{
args = {"notAColor", "foo"},
expect = {"foo", "[[Category:Articles using invalid color names]]"},
},
},
},
}
end


return p
return p

Latest revision as of 01:50, 21 July 2023

This is the main module for the following templates: In addition, this module exports the following functions.

color

color(colorName, text)

Parameters

Returns

  • Colored text
  • Error categories, or nil if no errors occurred

Examples

#InputOutputResultStatus
1
color("TMC Blue", "guy in green tights")
'<span class="zw-color" style="color:#37acbe">guy in green tights</span>'
guy in green tights
Green check.svg
nil
Green check.svg
2
color("notAColor", "foo")
"foo"
foo
Green check.svg
"[[Category:Articles using invalid color names]]"
Green check.svg

local p = {}
local h = {}
local data = mw.loadData("Module:Color/Data")

local utilsString = require("Module:UtilsString")

local CAT_INVALID_ARGS = "[[Category:"..require("Module:Constants/category/invalidArgs").."]]"
local CAT_INVALID_COLOR = "[[Category:Articles using invalid color names]]"

function h.warn(msg)
	local utilsError = require("Module:UtilsError")
	utilsError.warn(msg)
end

function h.invalidColor(colorId)
	local TransclusionArguments = require("Module:Transclusion Arguments")
	h.warn(string.format("<code>%s</code> is not a valid color name. See [[Template:Color]] for a list of supported colors.", colorId))
	-- We only store invalid colors for performance reasons
	-- Storing all template usages seems to break pages that use the template a lot
	TransclusionArguments.store({
		module = "Module:Color",
		args = {
			[1] = colorId,
			[2] = text,
		},
		isValid = false,
	})
end

function p.Main(frame)
	local args = frame:getParent().args
	local coloredText, errorCategories = p.color(args[1], args[2])
	return coloredText .. (errorCategories or "")
end

function p.List(frame)
	local args = frame:getParent().args
	return p.listColors(args[1])
end

-- @return the colored text as a string
-- @return any error categories as a string, or nil
function p.color(colorId, text)
	if text == nil or text == "" then
		h.warn("<code>text</code> parameter is required.")
		return text or "", CAT_INVALID_ARGS
	end
	if colorId == nil or colorId == "" then
		h.warn("<code>color</code> parameter is required.")
		return text, CAT_INVALID_ARGS
	end
	if string.find(colorId, "\n", 1, true) or string.find(colorId, "\r", 1, true) or utilsString.startsWith(colorId, " ") or utilsString.endsWith(colorId, " ") then
		h.warn("<code>color</code> argument contains invalid whitespace such as newlines or leading/trailing spaces.")
		return text, CAT_INVALID_ARGS
	end
	
	local colorData = data.colors[colorId]
	if colorData == nil then
		h.invalidColor(colorId)
		return text, CAT_INVALID_COLOR
	end
	local colorValue = colorData.color
	local html = mw.html.create("span")
		:addClass("zw-color")
		:wikitext(text)
	if utilsString.startsWith(colorValue, "linear-gradient") then
		-- It's OK to use inline styles here because the color is a "fixed" part of the quote and applies to all Zelda Wiki skins
		-- This enables colors to be defined in one place in module data, which is easier to maintain
		html:css({
			["background-image"] = colorValue,
			["color"] = "transparent",
			["background-clip"] = "text",
			["-webkit-background-clip"] = "text"
		})
	else
		html:css("color", colorValue)
	end
	local result = tostring(html)
	if colorData.deprecated then
		local action = ""
		if type(colorData.deprecated) == "string" then
			action = string.format("Use <code>%s</code> instead.", colorData.deprecated)
		else
			action = "See [[Template:Color]] for a list of supported colors."
		end
		h.warn(string.format("Color <code>%s</code> is being discontinued. %s", colorId, action))
		return result, CAT_INVALID_COLOR
	end
	return result
end

-- lists colors for one game, or all games if game is nil
function p.listColors(game)
	-- Perfomance optimization; only loading dependencies on the documentation pages where they're needed
	local Franchise = require("Module:Franchise")
	local utilsLayout = require("Module:UtilsLayout")
	local utilsString = require("Module:UtilsString")
	local utilsTable = require("Module:UtilsTable")
	
	local tableRows = {
		{
			header = true,
			{
				content = "Color",
				styles = {
					width = "150px"
				},
			},
			"Name",
			"Notes",
		}
	}
	local games = game and {game} or Franchise.enum({includeNonfiction = true})
	local listAllGames = game == nil
	local colorKeys = utilsTable.keys(data.colors)
	
	-- Remove deprecated colors
	colorKeys = utilsTable.filter(colorKeys, function(colorKey)
		return not data.colors[colorKey].deprecated
	end)
	
	local seenColorKeys = {}
	for _, game in ipairs(games) do -- "game" here could also be a book like Encyclopedia
		-- The added " " is for doing a whole-word match. e,g. SS should match "SS Green" but not "SSHD Green"
		local gameColorKeys = utilsTable.filter(colorKeys, utilsString._startsWith(game .. " "))
		table.sort(gameColorKeys)
		if listAllGames and #gameColorKeys > 0 then
			table.insert(tableRows, {
				header = true,
				{
					colspan = 3,
					content = string.format('<span id="%s">[[#%s|%s]]</span>', game, game, Franchise.display(game)), -- creates section links to each game
				}
			})
		end
		p.addRows(tableRows, gameColorKeys)
		seenColorKeys = utilsTable.concat(seenColorKeys, gameColorKeys)
	end

	local remainingColorKeys = utilsTable.difference(colorKeys, seenColorKeys)
	if listAllGames and #remainingColorKeys > 0 then
		table.insert(tableRows, {
			header = true,
			{
				colspan = 3,
				content = string.format("[[#Other|Other]]</span>"),
			}
		})
		p.addRows(tableRows, remainingColorKeys)
	end 
	return utilsLayout.table({
		caption = "Text Colors",
		rows = tableRows,
		hideEmptyColumns = true,
	})
end
function p.addRows(tableRows, gameColorKeys)
	for _, colorKey in ipairs(gameColorKeys) do
		local colorData = data.colors[colorKey]
		if not colorData.deprecated then
			table.insert(tableRows, {
				{
					content = " ", -- using whitespace so the cell isn't treated as being empty by utilsLayout
					styles = {
						["background"] = colorData.color,
					}
				},
				"<b><code>"..colorKey.."</code></b>",
				colorData.notes or ""
			})
		end
	end
end

-- For auto-generated doc at Module:Colors/Data/Documentation
function p.Data()
	return p.listColors()
end

p.Templates = {
	["Color"] = {
		purpose = "This template allows text to be colored such that quotes from games (e.g. [[Template:Cite]]) or other media have the same color highlights as the source text. These colors convey meaningful empasis and are essential to Zelda Wiki's [[Guidelines:Terminology|terminology guidelines]].",
		format = "inline",
		params = {
			[1] = {
				name = "colorId",
				required = true,
				type = "string",
				desc = "The name of the color - see supported colors below"
			},
			[2] = {
				name = "text",
				required = true,
				type = "string",
				desc = "The text to color"
			}
		},
		examples = {
			{"TWWHD Vermilion", "merchant's oath"},
			{"SSHD Blue", "Demise"},
			{"invalid color", "foo"},
			{
				desc = "This template no longer supports arbitrary web colors. Use [[Template:Web Color]] instead. Name all in-game colors at [[Module:Color/Data]].",
				args = {"#f2a1b5", "custom color"},
			},
			{
				desc = "Leading/trailing whitespace is not allowed in the color name." ,
				args = {"SSHD Blue ", "Demise"},
			},
			{"SSHD\nBlue", "Demise"},
			{
				desc = "Both parameters are required.",
				args = {"", "missing color"},
			},
			{"missing text"},
			{""},
			{},
			{
				desc = "Color names can be discontinued.",
				args = {"deprecated color", "bar"},
			},
			{"replaced color", "bar"},
		}
	},
	["Color/List"] = {
		purpose = "This template lists the colors that [[Template:Color]] supports.",
		format = "inline",
		params = {
			[1] = {
				name = "game",
				type = "string",
				enum = {
					reference = "[[Data:Franchise]]" 
				},
				desc = "If specified, the template will output the colors for the given game only. Otherwise, all colors will be listed.",
				canOmit = true,
			}
		},
		examples = {
			{"BotW"},
			{"E"},
		}
	}
}

function p.Schemas()
	return {
		-- For auto-generated doc at Module:Color/Documentation
		color = {
			colorName = {
				type = "string",
				required = true,
				desc = "The name of a color from [[Module:Color/Data]]",
			},
			text = {
				type = "string",
				required = true,
				desc = "The text to color",
			},
		},
		-- For auto-generated doc at Module:Color/Data/Documentation
		Data = {
			type = "record",
			required = true,
			properties = {
				{
					name = "colors",
					required = true,
					type = "map",
					keys = {
						type = "string",
					},
					values = {
						type = "record",
						properties = {
							{
								name = "color",
								required = true,
								type = "string",
								desc = "A [https://developer.mozilla.org/en-US/docs/Web/CSS/hex-color hex color] (with preceding #) or a [https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/linear-gradient linear-gradient].",
							},
							{
								name = "isDefault",
								type = "boolean",
								desc = "Set this to <code>true</code> when the color is the game's default dialogue color. Used by [[Module:Cite]] to set the default text color for citations."
							},			
							{
								name = "defaultFor",
								type = "array",
								items = { type = "string" },
								desc = "A list of wiki page names referring to the characters and/or interfaces whose text uses the color by default. Used by [[Module:Cite]] to set the default text color for citations."
							},
							{
								name = "notes",
								type = "string",
								desc = "Anything that editors should know about the color – the context in which it appears, other colors it could be confused with, etc.",
							},
							{
								name = "deprecated",
								oneOf = {
									{
										type = "string"
									},
									{
										type = "boolean"
									},
								},
								desc = "The name of the color that should be used instead of this color going forward. If multiple colors apply, just put <code>true</code>.",
							},
						}
					}
				}
			}
		}
	}
end

function p.Documentation()
	return {
		color = {
		params = {"colorName", "text"},
		returns = {
			"Colored text",
			"Error categories, or <code>nil</code> if no errors occurred",
		},
		cases = {
			{
				args = {"TMC Blue", "guy in green tights"},
				expect = {'<span class="zw-color" style="color:#37acbe">guy in green tights</span>', nil},
			},
			{
				args = {"notAColor", "foo"},
				expect = {"foo", "[[Category:Articles using invalid color names]]"},
			},
		},
	},
	}
end

return p