-- Note: Originally written on English Wikipedia at https://en.wikipedia.org/wiki/Module:Mapframe  
---
1. Localisation (L10n) settings #####
Replace values in quotes ("") with localised values
---
local L10n = {}  
-- Modue dependencies
local transcluder -- local copy of https://www.mediawiki.org/wiki/Module:Transcluder loaded lazily
-- "Module:No globals" should not be used, at least until all other modules which require this module are not using globals.  
-- Template parameter names (unnumbered versions only)
-- Specify each as either a single string, or a table of strings (aliases)
-- Aliases are checked -to-, i.e. `{ "one", "two" }` is equivalent to using `}` in a template
L10n.para = {
display = "display",
type = "type",
id = { "id", "ids" },
from = "from",
raw = "raw",
title = "title",
description = "description",
strokeColor = { "stroke-color", "stroke-colour" },
stroke,
strokeOpacity = "stroke-opacity",
fill = "fill",
fillOpacity = "fill-opacity",
coord = "coord",
marker = "marker",
markerColor = { "marker-color", "marker-colour" },
markerSize = "marker-size",
radius = { "radius", "radius_m" },
radiusKm = "radius_km",
radiusFt = "radius_ft",
radiusMi = "radius_mi",
edges = "edges",
text = "text",
icon = "icon",
zoom = "zoom",
= "",
plain = "plain",
frame,
frame,
frameCoordinates = { "-coordinates", "-coord" },
frameLatitude = { "-lat", "-latitude" },
frameLongitude = { "-long", "-longitude" },
frame,
switch = "switch",
overlay = "overlay",
overlayBorder = "overlay-",
overlayHorizontalAlignment = "overlay-horizontal-alignment",
overlayVerticalAlignment = "overlay-vertical-alignment",
overlayHorizontalOffset = "overlay-horizontal-offset",
overlayVerticalOffset = "overlay-vertical-offset"
}  
-- Names of other templates this module can extract coordinates from
L10n.template = {
coord = { -- The coord template, as well as templates with output that contains
"Coord", "Coord/sandbox",
"NRHP row", "NRHP row/sandbox",
"WikidataCoord", "WikidataCoord/sandbox", "Wikidatacoord", "Wikidata coord"
}
}  
-- Error messages
L10n.error = {
badDisplayPara = "Invalid display parameter",
noCoords = "Coordinates must be specified on Wikidata or in |" .. ( type(L10n.para.coord)== 'table' and L10n.para.coord[1] or L10n.para.coord ) .. "=",
wikidataCoords = "Coordinates not found on Wikidata",
noCircleCoords = "Circle centre coordinates must be specified, or available via Wikidata",
negativeRadius = "Circle radius must be a positive number",
noRadius = "Circle radius must be specified",
negativeEdges = "Circle edges must be a positive number",
noSwitchPara = "Found only one switch value in |" .. ( type(L10n.para.switch)== 'table' and L10n.para.switch[1] or L10n.para.switch ) .. "=",
oneSwitchLabel = "Found only one label in |" .. ( type(L10n.para.switch)== 'table' and L10n.para.switch[1] or L10n.para.switch ) .. "=",
noSwitchLists = "At least one parameter must have a SWITCH: list",
switchMismatches = "All SWITCH: lists must have the same number of values",  
-- "%s" and "%d" tokens will be replaced with strings and numbers when used
oneSwitchValue = "Found only one switch value in |%s=",
fewerSwitchLabels = "Found %d switch values but only %d labels in |" .. ( type(L10n.para.switch)== 'table' and L10n.para.switch[1] or L10n.para.switch ) .. "=",
noNamedCoords = "No named coordinates found in %s"
}  
-- Other strings
L10n.str = {
-- valid values for display parameter, e.g. (|display=inline) or (|display=title) or (|display=inline,title) or (|display=title,inline)
inline = "inline",
title = "title",
dsep = ",", -- separator between inline and title (comma in the example above)  
-- valid values for type paramter
line = "line", -- geoline feature (e.g. a road)
shape = "shape", -- geoshape feature (e.g. a state or province)
shapeInverse = "shape-inverse", -- geomask feature (the inverse of a geoshape)
data = "data", -- geoJSON data page on Commons
point = "point", -- single point feature (coordinates)
circle = "circle", -- circular area around a point
named = "named", -- all named coordinates in an article or section  
-- Keyword to indicate a switch list. Must NOT use the special characters ^$%.[]*+-?
switch = "SWITCH",  
-- valid values for icon, , and plain parameters
affirmedWords = ' '..table.concat({
"add",
"added",
"affirm",
"affirmed",
"include",
"included",
"on",
"true",
"yes",
"y"
}, ' ')..' ',
declinedWords = ' '..table.concat({
"decline",
"declined",
"exclude",
"excluded",
"false",
"",
"not",
"no",
"n",
"off",
"omit",
"omitted",
"remove",
"removed"
}, ' ')..' '
}  
-- Default values for parameters
L10n.defaults = {
display = L10n.str.inline,
text = "Map",
frame,
frame,
frame,
markerColor = "5E74F3",
markerSize = nil,
strokeColor = "#ff0000",
strokeWidth = 6,
edges = 32, -- number of edges used to approximate a circle
overlayBorder = " solid white",
overlayHorizontalAlignment = "",
overlayHorizontalOffset = "0",
overlayVerticalAlignment = "bottom",
overlayVerticalOffset = "0"
}  
-- #### End of L10n settings ####  
---
Utility methods
---
local util = {}  
--[[
Looks up a parameter value based on the id (a key from the L10n.para table) and
optionally a suffix, for parameters that can be suffixed (e.g. type2 is type
with suffix 2).
@param {table} args key-value pairs of parameter names and their values
@param {string} param_id id for parameter name (key from the L10n.para table)
@param {string} [suffix] suffix for parameter name
@returns {string|nil} parameter value if found, or nil if not found
]]--
function util.getParameterValue(args, param_id, suffix)
suffix = suffix or ''
if type( L10n.para[param_id] ) ~= 'table' then
return args[L10n.para[param_id]..suffix]
end
for _i, paramAlias in ipairs(L10n.para[param_id]) do
if args[paramAlias..suffix] then
return args[paramAlias..suffix]
end
end
return nil
end  
---
Trim whitespace from args, and remove empty args. Also fix control characters.
@param {table} argsTable
@returns {table} trimmed args table
---
function util.trimArgs(argsTable)
local cleanArgs = {}
for key, val in pairs(argsTable) do
if type(key) == 'string' and type(val) == 'string' then
val = val:match('^%s*(.-)%s*$')
if val ~= '' then
-- control characters inside json need to be escaped, but stripping them is simpler
-- See also T214984
-- However, *don't* strip control characters from wikitext (text or description parameters) or you'll break strip markers
-- Alternatively it might be better to only strip control char from raw parameter content
if util.matchesParam('text', key) or util.matchesParam('description', key, key:gsub('^%D+(%d+)$', '%1') ) then
cleanArgs[key] = val
else
cleanArgs[key] = val:gsub('%c',' ')
end
end
else
cleanArgs[key] = val
end
end
return cleanArgs
end  
--nil} - an optional suffix to apply to the value(s) from the localization key
@returns {boolean} true if the name matches the parameter, false otherwise
---
function util.matchesParam(key, name, suffix)
local param = L10n.para[key]
suffix = suffix or ''
if type(param) == 'table' then
for _, v in pairs(param) do
if (v .. suffix) == name then return true end
end
return false
end
return ((param .. suffix) == name)
end  
---
Check if a value is affirmed (one of the values in L10n.str.affirmedWords)
@param {string} val Value to be checked
@returns {boolean} true if affirmed, false otherwise
---
function util.isAffirmed(val)
if not(val) then return false end
return string.find(L10n.str.affirmedWords, ' '..val..' ', 1, true ) and true or false
end  
---
Check if a value is declined (one of the values in L10n.str.declinedWords)
@param {string} val Value to be checked
@returns {boolean} true if declined, false otherwise
---
function util.isDeclined(val)
if not(val) then return false end
return string.find(L10n.str.declinedWords , ' '..val..' ', 1, true ) and true or false
end  
---
Check if the name of a template matches the known coord templates or wrappers
(in L10n.template.coord). The name is normalised when checked, so e.g. the names