-- ATTENTION: Please edit this code at https://de.wikipedia.org/wiki/Modul:Graph
-- This way all wiki languages can stay in sync. Thank you!
---
-- BUGS: X-Axis label format bug? (xAxisFormat =) https://en.wikipedia.org/wiki/Template_talk:Graph:Chart#X-Axis_label_format_bug?_(xAxisFormat_=)
-- linewidths - doesnt work for two values (eg 0, 1) but work if added third value of both are zeros? Same for marksStroke - probably bug in Graph extension
-- clamp - "clamp" used to avoid marks outside marks area, "clip" should be use instead but not working in Graph extension, see https://phabricator.wikimedia.org/T251709
-- TODO:
-- marks:
-- - line strokeDash + serialization,
-- - symStroke serialization
-- - symbolsNoFill serialization
-- - arbitrary SVG path symbol shape as symbolsShape argument
-- - annotations
-- - vertical / horizontal line at specific values [DONE] 2020-09-01
-- - rectangle shape for x,y data range
-- - graph type serialization (deep rebuild reqired)
-- - second axis (deep rebuild required - assignment of series to one of two axies)  
-- Version History (_PLEASE UPDATE when modifying anything_):
-- 2020-09-01 Vertical and horizontal line annotations
-- 2020-08-08 New logic for "nice" for x axis (problem with scale when xType = "date") and grid
-- 2020-06-21 Serializes symbol size
-- transparent symbosls (from line colour) - buggy (incorrect opacity on overlap with line)
-- Linewidth serialized with "linewidths"
-- Variable symbol size and shape of symbols on line charts, default showSymbols = 2, default symbolsShape = circle, symbolsStroke = 0
-- p.chartDebuger for easy debug and JSON output
-- 2020-06-07 Allow lowercase variables for use with Template:Wikidata list
-- 2020-05-27 Map: allow specification which feature to display and changing the map
-- 2020-04-08 Change default showValues.fontcolor from black to persistentGrey
-- 2020-04-06 Logarithmic scale outputs wrong axis labels when "nice"=true
-- 2020-03-11 Allow user-defined scale types, e.g. logarithmic scale
-- 2019-11-08 Apply color-inversion-friendliness to legend title, labels, and xGrid
-- 2019-01-24 Allow comma-separated lists to contain values with commas
-- 2018-10-13 Fix browser color-inversion issues via #54595d per mw:Template:Graph:PageViews
-- 2018-09-16 Allow disabling the legend for templates
-- 2018-09-10 Allow grid lines
-- 2018-08-26 Use user-defined order for stacked charts
-- 2018-02-11 Force usage of explicitely provided x minimum and/or maximum values, rotation of x labels
-- 2017-08-08 Added showSymbols param to show symbols on line charts
-- 2016-05-16 Added encodeTitleForPath to help all path-based APIs graphs like pageviews
-- 2016-03-20 Allow omitted data for charts, labels for line charts with string (ordinal) scale at point location
-- 2016-01-28 For maps, always use wikiraw:// protocol. https:// will be disabled soon.  
local p = {}  
--add debug text to this string with eg. debuglog = debuglog .. "" .. "\n\n" .. "- " .. debug.traceback .. "result type: ".. type(result) .. " result: \n\n" .. mw.dumpObject(result)
--invoke chartDebuger to get graph JSON and this string
debuglog = "Debug " .. "\n\n"  
local baseMapDirectory = "Module:Graph/"
local persistentGrey = "#54595d"  
local shapes = {}
shapes = {
circle = "circle", x= "M-.5,-.5L.5,.5M.5,-.5L-.5,.5" , square = "square",
cross = "cross", diamond = "diamond", triangle_up = "triangle-up",
triangle_down = "triangle-down", triangle_right = "triangle-",
triangle_left = "triangle-",
banana = "m -0.5281,0.2880 0.0020,0.0192 m 0,0 c 0.1253,0.0543 0.2118,0.0679 0.3268,0.0252 0.1569,-0.0582 0.3663,-0.1636 0.4607,-0.3407 0.0824,-0.1547 0.1202,-0.2850 0.0838,-0.4794 l 0.0111,-0.1498 -0.0457,-0.0015 c -0.0024,0.3045 -0.1205,0.5674 -0.3357,0.7414 -0.1409,0.1139 -0.3227,0.1693 -0.5031,0.1856 m 0,0 c 0.1804,-0.0163 0.3622,-0.0717 0.5031,-0.1856 0.2152,-0.1739 0.3329,-0.4291 0.3357,-0.7414 l -0.0422,0.0079 c 0,0 -0.0099,0.1111 -0.0227,0.1644 -0.0537,0.1937 -0.1918,0.3355 -0.3349,0.4481 -0.1393,0.1089 -0.2717,0.2072 -0.4326,0.2806 l -0.0062,0.0260"
}  
local function numericArray(csv)
if not csv then return end  
local list = mw.text.split(csv, "%s*,%s*")
local result = {}
local isInteger = true
for i = 1, #list do
if list[i] == "" then
result[i] = nil
else
result[i] = tonumber(list[i])
if not result[i] then return end
if isInteger then
local int, frac = math.modf(result[i])
isInteger = frac == 0.0
end
end
end  
return result, isInteger
end  
local function stringArray(text)
if not text then return end  
local list = mw.text.split(mw.ustring.gsub(tostring(text), "\\,", "<COMMA>"), ",", true)
for i = 1, #list do
list[i] = mw.ustring.gsub(mw.text.trim(list[i]), "<COMMA>", ",")
end
return list
end  
local function isTable(t) return type(t) == "table" end  
local function copy(x)
if type(x) == "table" then
local result = {}
for key, value in pairs(x) do result[key] = copy(value) end
return result
else
return x
end
end  
function p.map
-- map path data for geographic objects
local basemap = .args.basemap or "Template:Graph:Map/Inner/Worldmap2c-json" -- WorldMap name and/or location may vary from wiki to wiki
-- scaling factor
local scale = tonumber(.args.scale) or 100
-- map projection, see https://github.com/mbostock/d3/wiki/Geo-Projections
local projection = .args.projection or "equirectangular"
-- defaultValue for geographic objects without data
local defaultValue = .args.defaultValue or .args.defaultvalue
local scaleType = .args.scaleType or .args.scaletype or "linear"
-- minimaler Wertebereich (nur für numerische Daten)
local domainMin = tonumber(.args.domainMin or .args.domainmin)
-- maximaler Wertebereich (nur für numerische Daten)
local domainMax = tonumber(.args.domainMax or .args.domainmax)
-- Farbwerte der Farbskala (nur für numerische Daten)
local colorScale = .args.colorScale or .args.colorscale or "category10"
-- show legend
local legend = .args.legend
-- the map feature to display
local feature = .args.feature or "countries"
-- map
local = numericArray(.args.)
-- format JSON output
local formatJson = .args.formatjson  
-- map data are key-value pairs: keys are non-lowercase strings (ideally ISO codes) which need to match the "id" values of the map path data
local values = {}
local isNumbers = nil
for name, value in pairs(.args) do
if mw.ustring.find(name, "^[^%l]+$") and value and value ~= "" then
if isNumbers == nil then isNumbers = tonumber(value) end
local data = { id = name, v = value }
if isNumbers then data.v = tonumber(data.v) end
table.insert(values, data)
end
end
if not defaultValue then
if isNumbers then defaultValue = 0 else defaultValue = "silver" end
end  
-- create highlight scale
local scales
if isNumbers then
if colorScale then colorScale = string.lower(colorScale) end
if colorScale == "category10" or colorScale == "category20" then else colorScale = stringArray(colorScale) end
scales =
{
{
name = "color",
type = scaleType,
domain = { data = "highlights", field = "v" },
range = colorScale,
nice = true,
zero = false
}
}
if domainMin then scales[1].domainMin = domainMin end
if domainMax then scales[1].domainMax = domainMax end  
local exponent = string.match(scaleType, "pow%s+(%d+%.?%d+)") -- check for exponent
if exponent then
scales[1].type = "pow"
scales[1].exponent = exponent
end
end  
-- create legend
if legend then
legend =
{
{
fill = "color",
offset = 120,
properties =
{
title = { fontSize = { value = 14 } },
labels = { fontSize = { value = 12 } },
legend =
{
stroke = { value = "silver" },
strokeWidth = { value = 1.5 }
}
}
}
}
end  
-- get map url
local basemapUrl
if (string.sub(basemap, 1, 10) == "wikiraw://") then
basemapUrl = basemap
else
-- if not a (supported) url look for a colon as namespace separator. If prepend default map directory name.
if not string.find(basemap, ":") then basemap = baseMapDirectory .. basemap end
basemapUrl = "wikiraw:///" .. mw.uri.encode(mw.title.new(basemap).prefixedText, "PATH")
end  
local output =
{
version = 2,