Módulo:Gráfico de casos médicos

A documentação para este módulo pode ser criada na página Módulo:Gráfico de casos médicos/doc

local yesno = require('Module:Yesno')
local barBox = require('Module:Bar box')
local lang = mw.getContentLanguage()
local language = lang:getCode()
local i18n = require('Module:Gráfico de casos médicos/i18n')[language]
assert(i18n, 'no chart translations to: ' .. mw.language.fetchLanguageName(language, 'pt'))
local monthAbbrs = {}
for i = 1, 12 do
				monthAbbrs[i] = lang:formatDate('M', '2020-' .. ('%02d'):format(i))
end
local p = {}
function p._toggleButton(active, customtoggles, id, label)
				local on  = active and '' or ' mw-collapsed'
				local off = active and ' mw-collapsed' or ''
				local outString =
								'<span class="mw-collapsible' .. on  .. customtoggles .. '" id="mw-customcollapsible-' .. id .. '" ' ..
								'style="border:2px solid lightblue">' .. label .. '</span>' ..
								'<span class="mw-collapsible' .. off .. customtoggles .. '" id="mw-customcollapsible-' .. id .. '">' .. label  .. '</span>'
				return outString
end
function p._yearToggleButton(year)
				return p._toggleButton(year.l, ' mw-customtoggle-' .. year.year, year.year, year.year)
end
function p._monthToggleButton(year, month)
				local lmon, label = lang:lc(month.mon), month.mon
				local id = (year or '') .. lmon
				local customtoggles = ' mw-customtoggle-' .. id
				if month.s then
								label = label .. '&nbsp;' .. month.s -- "Mmm ##"
								if month.s ~= month.e then -- "Mmm ##–##"
												label = label .. '–' .. month.e
								end
				else
								customtoggles = customtoggles .. (month.l and customtoggles .. month.l or '')
				end
				for i, combination in ipairs(month.combinations) do
								customtoggles = customtoggles .. ' mw-customtoggle-' .. combination -- up to 2 combinations per month so no need to table.concat()
				end
				return p._toggleButton(false, customtoggles, id, label)
end
function p._lastXToggleButton(years, duration, combinationsL)
				local months, id = years[#years].months, 'l' .. duration
				local i, customtoggles = #months, {' mw-customtoggle-' .. id}
				if #years > 1 then
								local year = years[#years].year
								while months[i].l do
												customtoggles[#customtoggles+1] = ' mw-customtoggle-' .. year .. lang:lc(months[i].mon) .. '-' .. id
												if i == 1 then
																if year == years[#years].year then
																				year = years[#years-1].year
																				months = years[#years-1].months
																				i = #months
																else -- either first month is also lastX month or lastX spans more than 2 years, which is not intended yet
																				break
																end
												else
																i = i - 1
												end
								end
				else
								while i > 0 and months[i].l do
												customtoggles[#customtoggles+1] = ' mw-customtoggle-' .. lang:lc(months[i].mon) .. '-' .. id
												i = i - 1
								end
				end
				for i, combinationL in ipairs(combinationsL) do
								customtoggles[#customtoggles+1] = ' mw-customtoggle-' .. combinationL -- up to 3 combinationsL in 90 days
				end
				return p._toggleButton(true, table.concat(customtoggles), id, mw.ustring.format(i18n.lastXDays, duration))
end
function p._buildTogglesBar(dateList, duration, nooverlap)
				local years = {{year=dateList[1].year, months={{mon=dateList[1].mon, combinations={}}}}}
				local months, combinationsL = years[1].months, {}
				local function addMonth(month)
								if month.mon ~= months[#months].mon then -- new month
												if month.year ~= years[#years].year then -- new year
																years[#years+1] = {year=month.year, months={}}
																months = years[#years].months -- switch months list
												end
												months[#months+1] = {mon=month.mon, combinations={}}
								end
				end
				for i = 2, #dateList do -- deduplicate years and months
								if #dateList[i] == 0 then -- specific date
												addMonth(dateList[i])
												months[#months].l = months[#months].l or dateList[i].l -- so that both ...-mon and ...-mon-lX classes are created
								elseif #dateList[i] == 1 then -- interval within month
												addMonth(dateList[i][1])
												months[#months].l = months[#months].l or dateList[i].l
								else -- multimonth interval
												for j, month in ipairs(dateList[i]) do
																addMonth(month)
																months[#months].combinations[#months[#months].combinations+1] = dateList[i].id
												end
												combinationsL[#combinationsL+1] = dateList[i].id:find('-l%d+$') and dateList[i].id
								end
				end
				if nooverlap then
								local lastDate = dateList[#dateList]
								months[#months].e = tonumber(os.date('%d', lastDate.nDate or lastDate.nEndDate or lastDate.nAltEndDate)) -- end of final month
								local i = #dateList
								repeat
												i = i - 1
								until i == 0 or (dateList[i].mon or dateList[i][1].mon) ~= months[#months].mon
								if i == 0 then -- start of first and final month
												months[#months].s = tonumber(os.date('%d', dateList[1].nDate))
								else
												months[#months].s = 1
								end
				end
				years[#years].l = true -- to activate toggle and respective months bar
				local monthToggles, divs = {}, nil
				if #years > 1 then
								local yearToggles, monthsDivs = {}, {}
								for i, year in ipairs(years) do
												yearToggles[#yearToggles+1] = p._yearToggleButton(year)
												monthToggles = {}
												months = year.months
												for j, month in ipairs(months) do
																monthToggles[#monthToggles+1] = p._monthToggleButton(year.year, month)
												end
												monthsDivs[#monthsDivs+1] =
																'<div class="mw-collapsible' .. (year.l and '' or ' mw-collapsed') ..
																'" id="mw-customcollapsible-' .. year.year .. '">' .. table.concat(monthToggles) .. '</div>'
								end
								divs = '<div>' .. table.concat(yearToggles) .. '</div>' .. table.concat(monthsDivs)
				else
								for i, month in ipairs(months) do
												monthToggles[#monthToggles+1] = p._monthToggleButton(nil, month)
								end
								divs = '<div>' .. table.concat(monthToggles) .. '</div>'
				end
				divs = divs .. '<div>' .. p._lastXToggleButton(years, duration, combinationsL) .. '</div>'
				return '<div class="nomobile" style="text-align:center">' .. divs .. '</div>'
end
p._barColors = {
				'#A50026',  --deaths
				'SkyBlue',  --recoveries
				'Tomato',   --cases or altlbl1
				'Gold',     --altlbl2
				'OrangeRed' --altlbl3
}
function p._customBarStacked(args)
				local barargs = {}
				barargs[1] = args[1]
				local function _numwidth(i)
								local nw = args.numwidth:sub(i, i)
								if nw == 'n' then
												return 0
								elseif nw == 't' then
												return 2.45
								elseif nw == 'm' or nw == 'd' then
												return 3.5
								elseif nw == 'w' then
												return 4.55
								elseif nw == 'x' then
												return 5.6
								end
				end
				barargs[2] =
								'<span class="cbs-ibr" style="padding:0 0.3em 0 0; width:' .. _numwidth(1) .. 'em">' .. (args[7] or '') .. '</span>' ..
								'<span class="cbs-ibl" style="width:' .. _numwidth(2) .. 'em">' .. (args[8] or '') .. '</span>'
				if #args.numwidth == 4 then
								local pad = args.numwidth:sub(3, 3) == 'n' and '0' or '0.3em'
								barargs.note2 =
												'<span class="cbs-ibr" style="padding:0 ' .. pad .. ' 0 0; width:' .. _numwidth(3) .. 'em">' .. (args[9] or '') .. '</span>' ..
												'<span class="cbs-ibl" style="width:' .. _numwidth(4) .. 'em">' .. (args[10] or '') .. '</span>'
				end
				for i = 1, 5 do
								barargs[2*i + 1] = p._barColors[i]
								barargs[2*i + 2] = args[i+1] / args.divisor
								barargs['title' .. i] = args[i+1]
				end
				barargs.align = 'cdcc'
				barargs.collapsed = args.collapsed
				barargs.id = args.id
				barargs.rowstyle = args.rowheight and 'line-height:'.. args.rowheight ..';'
				return barBox._stacked(barargs)
end
function p._row(args)
				local barargs = {}
				barargs[1] = (args[1] or '⋮') .. (args.note0 or '')
				barargs[2] = args[2] or 0
				barargs[3] = args[3] or 0
				if args['alttot1'] then
								barargs[4] = args['alttot1']
				elseif args[4] then
								barargs[4] = (args[4] or 0) - (barargs[2] or 0) - (barargs[3] or 0)
				else
								barargs[4] = 0
				end
				barargs[5] = args[5] or 0
				if args['alttot2'] then
								barargs[6] = args['alttot2']
				elseif args[6] then
								barargs[6] = (args[6] or 0) - (barargs[2] or 0) - (barargs[3] or 0)
				else
								barargs[6] = 0
				end
				barargs[7] = args[7]
				local function changeArg(firstright, valuecol, changecol)
								local change = ''
								if args['firstright' .. firstright] then
												change = '(' .. i18n.na .. ')'
								elseif not args[1] and args[valuecol] then
												change = '(=)'
								else
												change = args[changecol] and '(' .. args[changecol] .. ')' or ''
								end
								change = change .. (args['note' .. firstright] or '')
								return change
				end
				barargs[8] = changeArg(1, 7, 8)
				barargs[9] = args[9]
				barargs[10] = changeArg(2, 9, 10)
				barargs.divisor = args.divisor
				barargs.numwidth = args.numwidth
				barargs.rowheight = args.rowheight
				local dates
				if args.collapsible then
								local duration = args.duration
								if args.daysToEnd >= duration then
												barargs.collapsed = 'y'
								else
												barargs.collapsed = ''
								end
								if args.nooverlap and args.daysToEnd < duration then
												barargs.id = 'l' .. duration
								elseif args.nDate then
												dates = {year=tonumber(os.date('%Y', args.nDate)), mon=lang:formatDate('M', os.date('%Y-%m', args.nDate)),
																l=args.daysToEnd < duration and '-l' .. duration, nDate=args.nDate}
												barargs.id = (args.multiyear and dates.year or '') .. lang:lc(dates.mon) .. (dates.l or '')
								else
												local id, y, m, ey, em = {},
																tonumber(os.date('%Y', args.nStartDate or args.nAltStartDate)),
																tonumber(os.date('%m', args.nStartDate or args.nAltStartDate)),
																tonumber(os.date('%Y', args.nEndDate   or args.nAltEndDate  )),
																tonumber(os.date('%m', args.nEndDate   or args.nAltEndDate  ))
												dates = {nStartDate=args.nStartDate, nAltStartDate=args.nAltStartDate, nEndDate=args.nEndDate, nAltEndDate=args.nAltEndDate}
												repeat
																id[#id+1] = (args.multiyear and y or '') .. lang:lc(monthAbbrs[m])
																dates[#dates+1] = {year=y, mon=monthAbbrs[m]}
																y = y + math.floor(m / 12)
																m = m % 12 + 1
												until y == ey and m > em or y > ey
												dates.l = args.daysToEnd < duration and '-l' .. duration
												id = table.concat(id, '-') .. (dates.l or '')
												barargs.id = id
												dates.id = id
								end
				else
								barargs.collapsed = ''
								barargs.id = ''
				end
				return p._customBarStacked(barargs), dates
end
function p._buildBars(args)
				local frame = mw.getCurrentFrame()
				local updatePeriod = 86400 -- temporary implementation only supports daily updates
				local function getUnix(timestamp)
								return lang:formatDate('U', timestamp)
				end
				local rows, prevRow = {}, {}
				for line in mw.text.gsplit(args.data, '\n') do
								local i, barargs = 1, {}
								-- parameter parsing, basic type/missing value handling
								for parameter in mw.text.gsplit(line, ';') do
												if parameter:find('^%s*%a') then
																parameter = mw.text.split(parameter, '=')
																parameter[1] = mw.text.trim(parameter[1])
																if parameter[1]:find('^alttot') then
																				parameter[2] = tonumber(frame:callParserFunction('#expr', parameter[2]))
																else
																				parameter[2] = mw.text.trim(parameter[2])
																				if parameter[1]:find('^firstright') then
																								parameter[2] = yesno(parameter[2])
																				elseif parameter[2] == '' then
																								parameter[2] = nil
																				end
																end
																barargs[parameter[1]] = parameter[2]
												else
																parameter = mw.text.trim(parameter)
																if parameter ~= '' then
																				if i >= 2 and i <= 6 then
																								parameter = tonumber(frame:callParserFunction('#expr', parameter))
																								if not parameter then
																												error(('Data parameters 2 to 6 must not be formatted. i=%d, line=%s'):format(i, line))
																								end
																				end
																				barargs[i] = parameter
																end
																i = i + 1
												end
								end
								local bValid, nDateDiff
								-- get relevant date info based on previous row
								if barargs[1] then
												bValid, barargs.nDate = pcall(getUnix, barargs[1])
												assert(bValid, 'invalid date "' .. barargs[1] .. '"')
												if prevRow.nDate or prevRow.nEndDate then
																nDateDiff = barargs.nDate - (prevRow.nDate or prevRow.nEndDate)
																if nDateDiff > updatePeriod then
																				if nDateDiff == 2 * updatePeriod then
																								prevRow = {nDate=barargs.nDate-updatePeriod}
																								prevRow[1] = os.date('%Y-%m-%d', prevRow.nDate)
																				else
																								prevRow = {nStartDate=(prevRow.nDate or prevRow.nEndDate)+updatePeriod, nEndDate=barargs.nDate-updatePeriod}
																				end
																				rows[#rows+1] = prevRow
																end
												else
																prevRow.nEndDate = barargs.nDate - updatePeriod
																if prevRow.nStartDate == prevRow.nEndDate then
																				prevRow.nDate = prevRow.nEndDate
																				prevRow[1] = os.date('%Y-%m-%d', prevRow.nDate)
																-- as nAltStartDate assumes a minimal multiday interval, it's possible for it to be greater if a true previous span is 1 day
																elseif prevRow.nAltStartDate and prevRow.nAltStartDate >= prevRow.nEndDate then
																				error('a row in a consecutive intervals group is 1 day long and misses the date parameter')
																end
												end
								else
												if barargs.enddate then
																bValid, barargs.nEndDate = pcall(getUnix, barargs.enddate)
																assert(bValid, 'invalid enddate "' .. barargs.enddate .. '"')
												end
												if prevRow.nDate or prevRow.nEndDate then
																barargs.nStartDate = (prevRow.nDate or prevRow.nEndDate) + updatePeriod
																if barargs.nStartDate == barargs.nEndDate then
																				barargs.nDate = barargs.nEndDate
																				barargs[1] = os.date('%Y-%m-%d', barargs.nDate)
																end
												else
																prevRow.nAltEndDate = (prevRow.nStartDate or prevRow.nAltStartDate) + updatePeriod
																barargs.nAltStartDate = prevRow.nAltEndDate + updatePeriod
																if barargs.nEndDate and barargs.nAltStartDate >= barargs.nEndDate then
																				error('a row in a consecutive intervals group is 1 day long and misses the date parameter')
																end
												end
								end
								local function fillCols(col, change)
												local data = args['right' .. col .. 'data']
												local changetype = args['changetype' .. col]
												local value, num, prevnum
												if data == 'alttot1' then
																num = barargs.alttot1 or barargs[4]
																prevnum = prevRow.alttot1 or prevRow[4]
												elseif data == 'alttot2' then
																num = barargs.alttot2 or barargs[6]
																prevnum = prevRow.alttot2 or prevRow[6]
												elseif data then
																num = barargs[data+1]
																prevnum = prevRow[data+1]
												end
												if data and num then -- nothing in column, source found, and data exists
																value = changetype == 'o' and '' or lang:formatNum(num) -- set value to num if changetype isn't 'o'
																if not change and not barargs['firstright' .. col] then
																				if prevnum and prevnum ~= 0 then -- data on previous row
																								if num - prevnum ~= 0 then --data has changed since previous row
																												change = num-prevnum
																												if changetype == 'a' then -- change type is "absolute"
																																if change > 0 then
																																				change = '+' .. lang:formatNum(change)
																																end
																												else -- change type is "percent", "only percent" or undefined
																																local percent = 100 * change / prevnum -- calculate percent
																																local rounding = math.abs(percent) >= 10 and '%.0f' or math.abs(percent) >= 1 and '%.1f' or '%.2f'
																																percent = tonumber(rounding:format(percent)) -- round to two sigfigs
																																if percent > 0 then
																																				change = '+' .. lang:formatNum(percent) .. '%'
																																elseif percent < 0 then
																																				change = lang:formatNum(percent) .. '%'
																																else
																																				change = '='
																																end
																												end
																								else -- data has not changed since previous row
																												change = '='
																								end
																				else -- no data on previous row
																								barargs['firstright' .. col] = true -- set to (n.a.)
																				end
																end
												end
												return value, change
								end
								if not barargs[7] then
												barargs[7], barargs[8] = fillCols(1, barargs[8])
								end
								if not barargs[9] then
												barargs[9], barargs[10] = fillCols(2, barargs[10])
								end
								rows[#rows+1] = barargs
								prevRow = barargs
				end
				-- calculate and pass repetitive (except daysToEnd) parameters to each row
				local lastRow = rows[#rows]
				local total = {lastRow[2] or 0, lastRow[3] or 0, [4]=lastRow[5] or 0}
				total[3] = lastRow.alttot1 or lastRow[4] and lastRow[4] - total[1] - total[2] or 0
				total[5] = lastRow.alttot2 or lastRow[6] and lastRow[6] - total[1] - total[2] or 0
				local divisor = (total[1] + total[2] + total[3] + total[4] + total[5]) / (0.95 * args.barwidth)
				local firstDate, lastDate = rows[1].nDate, lastRow.nDate or lastRow.nEndDate
				local multiyear = os.date('%Y', firstDate) ~= os.date('%Y', lastDate - (args.nooverlap and args.duration * 86400 or 0))
				if args.collapsible ~= false then
								args.collapsible = (lastDate - firstDate) / 86400 >= args.duration
				end
				local bars, dateList = {}, {}
				for i, row in ipairs(rows) do -- build rows
								row.divisor = divisor
								row.numwidth = args.numwidth
								row.rowheight = args.rowheight
								row.collapsible = args.collapsible
								row.duration = args.duration
								row.nooverlap = args.nooverlap
								row.daysToEnd = (lastDate - (row.nDate or row.nEndDate or row.nAltEndDate)) / 86400
								row.multiyear = multiyear
								bars[#bars+1], dateList[#dateList+1] = p._row(row)
				end
				return table.concat(bars), dateList
end
function p._legend0(args)
				return
								'<span style="font-size:90%; margin:0px">' ..
												'<span style="background-color:' .. (args[1] or 'none') ..
												'; border:' .. (args.border or 'none') ..
												'; color:' .. (args[1] or 'none') .. '">' ..
																'&nbsp;&nbsp;&nbsp;&nbsp;' .. '</span>' ..
												'&nbsp;' .. (args[2] or '') .. '</span>'
end
function p._chart(args)
				for key, value in pairs(args) do
								if ({float=1, barwidth=1, numwidth=1, changetype=1})[key:gsub('%d', '')] then
												args[key] = value:lower()
								end
				end
				local barargs = {}
				barargs.css = 'Predefinição:Gráfico de casos médicos/styles.css'
				barargs.float = args.float or 'right'
				args.barwidth = args.barwidth or 'medium'
				local barwidth
				if args.barwidth == 'thin' then
								barwidth = 120
				elseif args.barwidth == 'medium' then
								barwidth = 280
				elseif args.barwidth == 'wide' then
								barwidth = 400
				elseif args.barwidth == 'auto' then
								barwidth = 'auto'
				else
								error('unrecognized barwidth')
				end
				local function _numwidth(i)
								local nw = args.numwidth:sub(i, i)
								if nw == 'n' then
												return 0
								elseif nw == 't' then
												return 40
								elseif nw == 'm' or nw == 'd' then
												return 55
								elseif nw == 'w' then
												return 70
								elseif nw == 'x' then
												return 85
								else
												error('unrecognized numwidth[' .. i .. ']')
								end
				end
				args.numwidth = args.numwidth or 'mm'
				local numwidth = _numwidth(1) + 10 + _numwidth(2)
				local right1width = numwidth
				if #args.numwidth == 4 then
								numwidth = numwidth + _numwidth(3) + _numwidth(4)
								if args.numwidth:sub(3, 3) == 'n' then
												numwidth = numwidth + 6
								else
												numwidth = numwidth + 10
								end
								if not args.right2 then
												right1width = numwidth
								end
				end
				right1width = right1width - 8 -- -8 because of padding
				if tonumber(barwidth) then
								barargs.width = 85 + barwidth + numwidth .. 'px'
								barargs.barwidth = barwidth .. 'px'
				else
								barargs.width = 'auto'
								barargs.barwidth = 'auto'
				end
				local title = {}
				local function spaces(n)
								local nbsp = '&nbsp;'
								return '<span class="nowrap">' .. nbsp:rep(n) .. '</span>'
				end
				local lugar = mw.ustring.gsub(args.lugar, 'the ', '')
				lugar = mw.ustring.upper(mw.ustring.sub(lugar,1,1)) .. mw.ustring.sub(lugar,2)
				local navbartitle =  'Dados da '.. args.surto .. ' de ' .. args.enfermidade .. '/Gráfico de casos médicos ' .. args.lugar
				local navbar = require('Module:Navbar')._navbar
				title[1] = (args.pretitle and args.pretitle .. ' ' or '') ..
									i18n.casesIn .. ' ' .. args.enfermidade .. ' ' .. args.lugar ..
								(args.lugar2 and ', ' .. args.lugar2 or '') ..
								(args.lugar3 and ', ' .. args.lugar3 or '') ..
								(args.posttitle and ' ' .. args.posttitle or '') .. spaces(2) ..'(' ..
								navbar({navbartitle, titleArg=':' .. mw.getCurrentFrame():getParent():getTitle(), mini=1, nodiv=1}) ..
								')<br />'
				title[2] = p._legend0({p._barColors[1], i18n.deaths})
				args.recoveries = args.recoveries == nil and true or args.recoveries
				title[3] = args.recoveries and spaces(3) .. p._legend0({p._barColors[2], args.reclbl or i18n.recoveries}) or ''
				title[4] = args.altlbl1 ~= 'hide' and spaces(3) .. p._legend0({p._barColors[3], args.altlbl1 or i18n.activeCases}) or ''
				title[5] = args.altlbl2 and spaces(3) .. p._legend0({p._barColors[4], args.altlbl2}) or ''
				title[6] = args.altlbl3 and spaces(3) .. p._legend0({p._barColors[5], args.altlbl3}) or ''
				local togglesbar, buildargs = nil, {}
				args.right1 = args.right1 or i18n.noOfCases
				args.duration = args.duration or 15
				args.nooverlap = args.nooverlap or false
				buildargs.barwidth = tonumber(barwidth) or 280
				buildargs.numwidth = args.numwidth
				buildargs.rowheight = args.rowheight
				if args.datapage then
								local externalData = require('Module:Gráfico de casos médicos/data')._externalData
								buildargs.data = externalData(args)
				else
								buildargs.data = args.data
				end
				-- if no right1data and right1 title is cases, use 3rd classification
				buildargs.right1data = args.right1data or args.right1 == i18n.noOfCases and 3
				-- if no right2data and right2 title is deaths, use 1st classification
				buildargs.right2data = args.right2data or (args.right2 == i18n.noOfDeaths or args.right2 == i18n.noOfDeaths2) and 1
				buildargs.changetype1 = (args.changetype1 or args.changetype or ''):sub(1, 1) -- 1st letter
				buildargs.changetype2 = (args.changetype2 or args.changetype or ''):sub(1, 1) -- 1st letter
				buildargs.collapsible = args.collapsible
				buildargs.duration = args.duration
				buildargs.nooverlap = args.nooverlap
				local dateList
				barargs.bars, dateList = p._buildBars(buildargs)
				if buildargs.collapsible then
								togglesbar = p._buildTogglesBar(dateList, args.duration, args.nooverlap)
				end
				title[7] = togglesbar and '<br />' .. togglesbar or ''
				barargs.title = table.concat(title)
				barargs.left1 =
								'<div class="center" style="width:77px">' .. -- 85-8 because of padding
												"'''" .. i18n.date .. "'''" .. '</div>'
				barargs.right1 =
								'<div class="center" style="width:' .. right1width .. 'px">' ..
												"'''" .. args.right1 .. "'''" .. '</div>'
				if args.right2 then
								local right2width = numwidth - right1width - 16 -- -8-8...
								barargs.right2 =
												'<div class="center" style="width:' .. right2width ..'px">' ..
																"'''" .. args.right2 .. "'''" .. '</div>'
				end
				barargs.caption = args.caption
				return barBox._box(barargs)
end
function p.chart(frame)
				local getArgs = require('Module:Arguments').getArgs
				local args = getArgs(frame, {
								valueFunc = function (key, value)
												if value and value ~= '' then
																key = key:gsub('%d', '')
																if ({rowheight=1, duration=1, rightdata=1})[key] then -- if key in {...}
																				return tonumber(value) or value
																end
																if ({recoveries=1, collapsible=1, nooverlap=1})[key] then
																				return yesno(value)
																end
																return value
												end
												return nil
								end
				})
				return p._chart(args)
end
return p