Module:Map
Jump to navigation
Jump to search
local p = {}
local util = {}
--[[
Splits a list into a table sequence. The items in the list may be separated by
commas, or by semicolons (if items may contain commas), or by "###" (if items
may contain semicolons).
@param {string} listStringm
@returns {table} sequence of list items
]]--
function util.tableFromList(listString)
if type(listString) ~= "string" or listString == "" then return nil end
local separator = (mw.ustring.find(listString, "###", 0, true ) and "###") or
(mw.ustring.find(listString, ";", 0, true ) and ";") or ","
local pattern = "%s*"..separator.."%s*"
return mw.text.split(listString, pattern)
end
function p.main (frame)
return p._main(frame.getParent(frame).args)
end
function p._main (argsIn)
local args = {}
for key, value in pairs(argsIn) do
args[key] = value
end
-- [[MediaWiki:Gadget-maps.js]] already defines default values for these
-- parameters, but the HTML needs to specify an inline width and height
-- upfront to keep the content from jumping around when the gadget loads.
local width = args["width"] or args["frame-width"] or "400px"
if width == "full" then
width = "100%"
elseif not string.match(width, "%d*%D+") then
width = width.."px"
end
args.width=width
height=args["height"] or args["frame-height"] or "400px"
if not string.match(height, "%d*%D+") then
height = height.."px"
end
args.height=height
args.align = args.align or "right"
if args.switch then
args.plain = args.plain or "no"
return tostring(p.switch(args))
else
return tostring(p.comparer(args))
end
end
function p.switch (args)
local variants = util.tableFromList(args["switch"])
local variantArgs = {}
for i = 1, #variants do
variantArgs[i] = {}
end
for key, value in pairs(args) do
if key ~= 'switch' and key ~= 'text' and key ~= 'plain' and key ~= 'align' then
if string.match(value, "^SWITCH:.+") then
local valueVariants = util.tableFromList(string.match(value, "^SWITCH:%s*(.*)"))
local variantIndex = 1
local i = 1
while i <= #valueVariants do
variantArgs[variantIndex][key] = valueVariants[i]
variantIndex = variantIndex + 1
if variantIndex > #variants then break end
i = i+1
end
variantIndex = variantIndex - 1
while variantIndex < #variants do
variantArgs[variantIndex+1][key] = variantArgs[variantIndex][key]
variantIndex = variantIndex+1
end
else
for i = 1, #variants do
variantArgs[i][key] = value
end
end
end
end
local container = mw.html.create("div")
:addClass("switcher-container")
if args["align"] == "left" or args["align"] == "right" then
container:addClass("float"..args["align"])
else -- alignment is "center"
container:addClass("center")
end
for i = 1, #variantArgs do
container
:node(p.comparer(variantArgs[i]))
:tag("span")
:addClass("switcher-label")
:css("display", "none")
:wikitext("Show "..mw.text.trim(variants[i]))
end
if not args["plain"] or string.lower(args["plain"])=="yes" then
return container
end
local classlist = container:getAttr("class")
classlist = mw.ustring.gsub(classlist, "%a*"..args.align, "")
container:attr("class", classlist)
local outerContainer = mw.html.create("div")
:addClass("thumb")
if args.align == "left" or args.align == "right" then
outerContainer:addClass("t"..args.align)
else -- alignment is "center"
outerContainer
:addClass("tnone")
:addClass("center")
end
outerContainer
:tag("div")
:addClass("thumbinner")
:css("width", args.width)
:node(container)
:node(args.text and mw.html.create("div")
:addClass("thumbcaption")
:wikitext(args.text)
)
return outerContainer
end
function p.comparer (args)
local comparer = false
for key, value in pairs(args) do
if string.match(value, "^COMP:.+$") then
comparer = true
end
end
local mapElement
if comparer then
local body1 = {}
local body2 = {}
for key, value in pairs(args) do
if string.match(value, "^COMP:.+$") then
local division = util.tableFromList(string.match(value, "^COMP:%s*(.*)"))
body1[key] = division[1]
body2[key] = division[2]
else
body1[key] = value
body2[key] = value
end
end
mapElement = mw.html.create("div")
:addClass("maplibre-comparison")
:node(p.body(body1))
:node(p.body(body2))
:attr("data-width", args.width)
:attr("data-height", args.height)
else
mapElement = p.body(args)
end
local container;
if args["plain"] and string.lower(args["plain"])~="yes" then
container = mw.html.create("div")
:addClass("thumb")
:node(mw.html.create("div")
:addClass("thumbinner")
:css("width", args.width)
:node(mapElement)
:node(mw.html.create("div")
:addClass("thumbcaption")
:wikitext(args["text"] or "Map")
)
)
if args.align == "left" or args.align == "right" then
container:addClass("t"..args.align)
elseif args.align == "center" then
container:addClass("tnone")
:addClass("center")
end
else
container = mapElement
end
return container
end
function p.body (args)
-- Copy frame arguments verbatim to the container’s data attributes.
local zoom = args.zoom -- defaults in gadget
args["stroke-color"] = args["stroke-color"] or args["stroke-colour"]
local simpleAttrs = {
"lat", "lon", "bearing", "pitch", "layer", "date",
"navigation-position", "full-screen-position", "attribution-position",
"title", "description", "stroke-color", "stroke-width", "stroke-opacity", "fill", "fill-opacity",
"title-key", "description-key", "stroke-color-key", "stroke-width-key", "stroke-opacity-key", "fill-key", "fill-opacity-key"
}
local hasMarker = args.marker ~= "no"
local dataAttrs = {
["data-width"] = args.width,
["data-height"] = args.height,
["data-zoom"] = zoom,
}
for i, attr in ipairs(simpleAttrs) do
dataAttrs["data-" .. attr] = args[attr]
end
local container = mw.html.create("div")
:addClass("maplibre-map")
:attr(dataAttrs)
-- [[MediaWiki:Gadget-maps.js]] already sets these dimensions in an
-- inline style, but the transcluded HTML needs to specify these same
-- dimensions upfront to keep the content from jumping around when the
-- gadget loads.
:css("width", width)
:css("height", height)
-- Numbered series of arguments can start with an unnumbered argument.
-- Also backfill legacy unnumbered arguments.
args.commons1 = args.commons1 or args.commons
args.from1 = args.from1 or args.from -- style args are not overwriten to allow for a default key for all files
args.commons = nil
args.from = nil --prevents calling the same feature twice, since the JS still looks for this argument
args["marker1-lat"] = args["marker1-lat"] or args["marker-lat"] or args.mlat
args["marker1-lon"] = args["marker1-lon"] or args["marker-lon"] or args.mlon
-- Gather numbered series arguments by index so they overlay sequentially and not randomly.
local commonsAttrIndices = {}
local fromAttrIndices = {}
local markerAttrIndices = {}
for key, value in pairs(args) do
local index = mw.ustring.match(key, "^commons(%d+)$")
if index then
table.insert(commonsAttrIndices, tonumber(index))
end
index = mw.ustring.match(key, "^from(%d+)$")
if index then
table.insert(fromAttrIndices, tonumber(index))
end
index = mw.ustring.match(key, "^marker(%d+)-lat$") or
mw.ustring.match(key, "^marker(%d+)-lon")
if index then
table.insert(markerAttrIndices, tonumber(index))
end
end
-- Numerically sort the indices in these arguments.
table.sort(commonsAttrIndices)
table.sort(markerAttrIndices)
table.sort(fromAttrIndices)
--weaves 'from' attributes into 'commons' attributes list
local commonsAttrsIndex = 1 --will always be the most recent index with a value
if #fromAttrIndices > 0 then
for _, i in ipairs(fromAttrIndices) do
if not (commonsAttrIndices[1] == 1) then
table.insert(commonsAttrIndices, 1, 1)
args["commons1"] = args["from"..i]
else
while true do
if #commonsAttrIndices==commonsAttrsIndex then
table.insert(commonsAttrIndices, commonsAttrIndices[commonsAttrsIndex]+1)
args["commons"..commonsAttrsIndex+1] = args["from"..i]
commonsAttrsIndex = commonsAttrsIndex + 1
break
elseif commonsAttrIndices[commonsAttrsIndex+1]-commonsAttrIndices[commonsAttrsIndex] > 1 then
table.insert(commonsAttrIndices, commonsAttrsIndex+1, commonsAttrIndices[commonsAttrsIndex]+1)
args["commons"..commonsAttrIndices[commonsAttrsIndex+1]] = args["from"..i]
commonsAttrsIndex = commonsAttrsIndex + 1
break
else
commonsAttrsIndex = commonsAttrsIndex + 1
end
end
end
end
end
-- Insert a placeholder child element for each GeoJSON overlay hosted on
-- Wikimedia Commons.
if #commonsAttrIndices > 0 then
for _, i in ipairs(commonsAttrIndices) do
container:tag("span")
:addClass("maplibre-map-geojson")
:attr("data-commons", args["commons" .. i])
:attr("data-title", args["title" .. i])
:attr("data-description", args["description" .. i])
:attr("data-stroke-color", args["stroke-color" .. i] or args["stroke-colour" .. i])
:attr("data-stroke-width", args["stroke-width" .. i])
:attr("data-stroke-opacity", args["stroke-opacity" .. i])
:attr("data-fill", args["fill" .. i])
:attr("data-fill-opacity", args["fill-opacity" .. i])
:attr("data-title-key", args["title-key" .. i])
:attr("data-description-key", args["description-key" .. i])
:attr("data-stroke-color-key", args["stroke-color-key" .. i] or args["stroke-colour-key" .. i])
:attr("data-stroke-width-key", args["stroke-width-key" .. i])
:attr("data-stroke-opacity-key", args["stroke-opacity-key" .. i])
:attr("data-fill-key", args["fill-key" .. i])
:attr("data-fill-opacity-key", args["fill-opacity-key" .. i])
end
end
-- Insert a placeholder child element for each marker overlay.
if args.marker ~= "no" and #markerAttrIndices > 0 then
-- Deduplicate indices since marker#-lat and marker#-lon typically come
-- in pairs.
local insertedIndices = {}
for _, i in ipairs(markerAttrIndices) do
if not insertedIndices[i] then
container:tag("span")
:addClass("maplibre-map-marker")
:attr("data-lat", args["marker" .. i .. "-lat"])
:attr("data-lon", args["marker" .. i .. "-lon"])
insertedIndices[i] = true
end
end
end
return container
end
return p