Mô đun:Piechart

local p = {}
local priv = {}
p.__priv = priv
local forPrinting = "-webkit-print-color-adjust: exact; print-color-adjust: exact;"

function p.color(frame)
local index = tonumber(priv.trim(frame.args[1]))
return ' ' .. priv.defaultColor(index)
end

function p.pie(frame)
local json_data = priv.trim(frame.args[1])
local options = {}
if (frame.args.meta) then
options.meta = priv.trim(frame.args.meta)
end
local html = p.renderPie(json_data, options)
return priv.trim(html)
end

function p.setupOptions(user_options)
local options = {
size = 100,
autoscale = false,
ariahidechart = false,
legend = false,
direction = "",
width = "",
caption = "",
footer = "",
labelformat = "",
}
options.style = ""
if user_options and user_options.meta then
local decodeSuccess, rawOptions = pcall(function()
return mw.text.jsonDecode(user_options.meta, mw.text.JSON_TRY_FIXING)
end)
if not decodeSuccess then
rawOptions = false
end
if rawOptions then
if type(rawOptions.size) == "number" then
options.size = math.floor(rawOptions.size)
end
options.autoscale = rawOptions.autoscale or false
if rawOptions.legend then options.legend = true end
if rawOptions.ariahidechart then options.ariahidechart = true end
if (type(rawOptions.direction) == "string") then
local sanitized = rawOptions.direction:gsub("[^a-z0-9%-]", "")
options.direction = 'flex-direction: ' .. sanitized .. ';'
options.width = 'width: max-content;'
end
if (type(rawOptions.width) == "string") then
local sanitized = rawOptions.width:gsub("[^a-z0-9%-]", "")
options.width = 'width: ' .. sanitized .. ';'
end
if (type(rawOptions.caption) == "string") then options.caption = rawOptions.caption end
if (type(rawOptions.footer) == "string") then options.footer = rawOptions.footer end
if (type(rawOptions.labelformat) == "string") then options.labelformat = rawOptions.labelformat end
end
if options.width ~= "" then options.style = options.style .. options.width end
if options.direction ~= "" then options.style = options.style .. options.direction end
end
if (options.legend) then options.ariahidechart = true end
return options
end

p.__priv.legendDebug = false

function p.renderPie(json_data, user_options)
if type(json_data) ~= "string" or json_data "
if not ok then html = html .. priv.renderErrors(data) end
if options.legend then html = html .. p.renderLegend(data, options) end
if p.__priv.legendDebug then return html end
local header, items, footer = p.renderEntries(ok, total, data, options)
html = html .. header .. items .. footer
html = html .. "\n"
return html
end

function priv.boundaryFormatting(diff)
local value = 0.0
if diff grace
end

function priv.sumErrorTracking(sum, items)
-- Disabled on viwiki to reduce category clutter
end

function p.prepareEntries(data, options)
local sum = priv.sumValues(data);
if priv.willAutoscale(sum) then options.autoscale = true end
local ok = true
local no = 0
local total = data
for index, entry in ipairs(data) do
no = no + 1
if not priv.prepareSlice(entry, no, sum, total, options) then
no = no - 1
ok = false
end
end
total = no
return ok, total
end

function priv.sumValues(data)
local sum = 0;
for _, entry in ipairs(data) do
local value = entry.value
if not (type(value) ~= "number" or value "
for _, entry in ipairs(data) do
if entry.error then
local entryJson = mw.text.jsonEncode(entry)
html = html .. "\n".. entryJson ..""
end
end
return html .. "\n\n"
end

function priv.prepareSlice(entry, no, sum, total, options)
local autoscale = options.autoscale
local value = entry.value
if (type(value) ~= "number" or value "
end
if options.caption ~= "" then
html = html .. "" .. options.caption .. ""
end
html = html .. "\n"
for _, entry in ipairs(data) do
if not entry.error then html = html .. priv.renderLegendItem(entry, options) end
end
html = html .. "\n\n"
if options.footer ~= "" then
html = html .. "" .. options.footer .. ""
end
if options.caption ~= "" or options.footer ~= "" then html = html .. "\n" end
return html
end

function priv.renderLegendItem(entry, options)
if entry.visible ~= nil and entry.visible == false then return "" end
local label = entry.label
local bcolor = entry.bcolor
local html = "\n"
if p.__priv.legendDebug then forPrinting = "" end
html = html ..
html = html .. ..label..
return html .. ""
end

function p.renderEntries(ok, total, data, options)
-- Fallback cuts if json load fails
p.cuts = {0.000000,0.062832,0.125664,0.188496,0.251327,0.314159,0.376991,0.439823,0.502655}
local success, loadedCuts = pcall(mw.loadJsonData, 'Module:Piechart/cuts.json')
if success then p.cuts = loadedCuts end

local first = true
local previous = 0
local items = ""
for index, entry in ipairs(data) do
if not entry.error then
items = items .. priv.renderItem(previous, entry, options)
previous = previous + entry.value
end
end
local header = priv.renderHeader(options)
local footer = '\n'
return header, items, footer
end

function priv.renderHeader(options)
local bcolor = 'background:888;color:000'
local size = options.size
local aria = ""
if (options.ariahidechart) then aria = 'aria-hidden="true"' end
local style = 'width:'..size..'px;height:'..size..'px;'..bcolor..';'..forPrinting
local html = [[]]
return html
end

function priv.renderItem(previous, entry, options)
local value = entry.value
local label = entry.label
local bcolor = entry.bcolor
if (value 1 then previous = previous - 0.01 end
value = value + 0.02
else
if previous > 1 then previous = previous - 0.1 end
value = value + 0.2
end
if previous + value > 100 then
if previous >= 100 then return "" end
value = 100 - previous
end
local html = ""
if (value >= 50) then
html = priv.sliceWithClass('pie50', 50, value, previous, bcolor, label)
elseif (value >= 25) then
html = priv.sliceWithClass('pie25', 25, value, previous, bcolor, label)
elseif (value >= 12.5) then
html = priv.sliceWithClass('pie12-5', 12.5, value, previous, bcolor, label)
elseif (value >= 7) then
html = priv.sliceWithClass('pie7', 7, value, previous, bcolor, label)
elseif (value >= 5) then
html = priv.sliceWithClass('pie5', 5, value, previous, bcolor, label)
else
local cutIndex = priv.round(value10)
if cutIndex sizeStep) then
local extra = value - sizeStep
transform = priv.rotation(previous + extra)
html = html .. priv.sliceBase(sizeClass, transform, bcolor, label)
end
return html
end

function priv.sliceBase(sizeClass, transform, bcolor, label)
local style = bcolor
if transform ~= "" then style = style .. '; ' .. transform end
return '\n\t'
end

function priv.sliceX(cut, transform, bcolor, label)
local path = 'clip-path: polygon(0% 0%, '..cut..'% 0%, 0 100%)'
return '\n\t'
end

function priv.rotation(value)
if (value > 0.001) then
local f = string.format("%.7f", value / 100)
f = f:gsub("(%d)0+$", "%1")
return "transform: rotate("..f.."turn)"
end
return
end

function priv.formatNum(value)
local v = ""
if (value 0 then color = colorPalette[colorNo] end
return color
end

function priv.defaultColorNo(no, total, size)
local color = 0
local colorGroupSize = 3
local colorGroups = 4
if total == 1 then color = 1
elseif total ]+>", ""):gsub("", "&gt;"):gsub("'", "&39;"):gsub("\"", "&quot;")
return label
end

function p.parseEnumParams(frame)
local args = frame:getParent().args
return priv.parseEnumParams(args)
end

function priv.parseEnumParams(args)
local result = {}
local i = 1
local sum = 0.0
local hasCustomColor = false
while args["value" .. i] do
local entry = { value = tonumber(args["value" .. i]) or 0 }
local label = args["label" .. i]
if label and label ~= "" then entry.label = label end
hasCustomColor = false
local color = args["color" .. i]
if color and color ~= "" then
entry.color = color
hasCustomColor = true
end
table.insert(result, entry)
sum = sum + entry.value
i = i + 1
end
local willAutoscale = priv.willAutoscale(sum)
for _, entry in ipairs(result) do
local label = entry.label
if label and not label:find("%$[a-z]") then
if willAutoscale then entry.label = label .. " $v"
else entry.label = label .. " ($p)" end
end
end

-- Support for 'other' or 'khác'
local langOther = priv.getLangOther()
local colorOther = "FEFDFD"
local otherValue = 100 - sum
local otherParam = args["other"] or args["khác"]
local otherLabelParam = args["other-label"] or args["nhãn khác"]
local otherColorParam = args["other-color"] or args["màu khác"]

if otherParam and otherParam ~= "" then
if otherValue 0.01 then
if hasCustomColor then
table.insert(result, {visible = false, label = langOther .. " ($v)", color = colorOther})
else
table.insert(result, {visible = false, label = langOther .. " ($v)"})
end
end
return mw.text.jsonEncode(result)
end

function priv.getLangOther()
return "Khác"
end

local trueValues = { ["true"] = true, ["1"] = true, ["on"] = true, ["yes"] = true, ["có"] = true }
function priv.isTrueishValue(value)
if not value or value == "" then return nil end
value = priv.trim(value)
if value == "" then return nil end
return trueValues[value:lower()] or false
end

function p.parseMetaParams(frame)
local args = frame:getParent().args
local meta = {}
local thumb = args["thumb"]
if args["value1"] or (thumb and (thumb "right" or thumb "left")) then
meta.size = 200
meta.legend = true
end
if args["meta"] then
local decodeSuccess, tempMeta = pcall(function()
return mw.text.jsonDecode(args["meta"], mw.text.JSON_TRY_FIXING)
end)
if decodeSuccess then meta = tempMeta end
end
if args["size"] then meta.size = tonumber(args["size"]) end
if args["radius"] and tonumber(args["radius"]) then meta.size = 2 tonumber(args["radius"]) end
if args["autoscale"] then meta.autoscale = priv.isTrueishValue(args["autoscale"]) end
if args["legend"] then meta.legend = priv.isTrueishValue(args["legend"]) end
if args["ariahidechart"] then meta.ariahidechart = priv.isTrueishValue(args["ariahidechart"]) end
if args["direction"] and args["direction"] ~= "" then meta.direction = args["direction"]:gsub("[^a-z0-9%-]", "") end
if args["width"] and args["width"] ~= "" then meta.width = args["width"]:gsub("[^a-z0-9%-]", "") end

if args["caption"] and args["caption"] ~= "" then meta.caption = args["caption"] end
if args["chú thích"] and args["chú thích"] ~= "" then meta.caption = args["chú thích"] end

if args["footer"] and args["footer"] ~= "" then meta.footer = args["footer"] end
if args["labelformat"] and args["labelformat"] ~= "" then meta.labelformat = args["labelformat"] end

return mw.text.jsonEncode(meta)
end

return p