<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://mywikibiz.com/index.php?action=history&amp;feed=atom&amp;title=Module%3AMedical_cases_chart</id>
	<title>Module:Medical cases chart - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://mywikibiz.com/index.php?action=history&amp;feed=atom&amp;title=Module%3AMedical_cases_chart"/>
	<link rel="alternate" type="text/html" href="https://mywikibiz.com/index.php?title=Module:Medical_cases_chart&amp;action=history"/>
	<updated>2026-06-13T18:39:05Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.35.3</generator>
	<entry>
		<id>https://mywikibiz.com/index.php?title=Module:Medical_cases_chart&amp;diff=478688&amp;oldid=prev</id>
		<title>Zoran: Pywikibot 6.4.0</title>
		<link rel="alternate" type="text/html" href="https://mywikibiz.com/index.php?title=Module:Medical_cases_chart&amp;diff=478688&amp;oldid=prev"/>
		<updated>2021-07-16T04:46:18Z</updated>

		<summary type="html">&lt;p&gt;Pywikibot 6.4.0&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;local yesno = require('Module:Yesno')&lt;br /&gt;
local BarBox = require('Module:Bar box').BarBox&lt;br /&gt;
&lt;br /&gt;
local lang = mw.getContentLanguage()&lt;br /&gt;
local language = lang:getCode()&lt;br /&gt;
local i18n = require('Module:Medical cases chart/i18n')[language]&lt;br /&gt;
assert(i18n, 'no chart translations to: ' .. mw.language.fetchLanguageName(language, 'en'))&lt;br /&gt;
local monthAbbrs = {}&lt;br /&gt;
for i = 1, 12 do&lt;br /&gt;
	monthAbbrs[i] = lang:formatDate('M', '2020-' .. ('%02d'):format(i))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
function p._toggleButton(active, customtoggles, id, label)&lt;br /&gt;
	local on  = active and '' or ' mw-collapsed'&lt;br /&gt;
	local off = active and ' mw-collapsed' or ''&lt;br /&gt;
	local outString =&lt;br /&gt;
		'&amp;lt;span class=&amp;quot;mw-collapsible' .. on  .. customtoggles .. '&amp;quot; id=&amp;quot;mw-customcollapsible-' .. id .. '&amp;quot; ' ..&lt;br /&gt;
		'style=&amp;quot;border:2px solid lightblue&amp;quot;&amp;gt;' .. label .. '&amp;lt;/span&amp;gt;' ..&lt;br /&gt;
		'&amp;lt;span class=&amp;quot;mw-collapsible' .. off .. customtoggles .. '&amp;quot; id=&amp;quot;mw-customcollapsible-' .. id .. '&amp;quot;&amp;gt;' .. label  .. '&amp;lt;/span&amp;gt;'&lt;br /&gt;
	return outString&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p._yearToggleButton(year)&lt;br /&gt;
	return p._toggleButton(year.l, ' mw-customtoggle-' .. year.year, year.year, year.year)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p._monthToggleButton(year, month)&lt;br /&gt;
	local lmon, label = lang:lc(month.mon), month.mon&lt;br /&gt;
	local id = (year or '') .. lmon&lt;br /&gt;
	local customtoggles = ' mw-customtoggle-' .. id&lt;br /&gt;
&lt;br /&gt;
	if month.s then&lt;br /&gt;
		label = label .. '&amp;amp;nbsp;' .. month.s -- &amp;quot;Mmm ##&amp;quot;&lt;br /&gt;
		if month.s ~= month.e then -- &amp;quot;Mmm ##–##&amp;quot;&lt;br /&gt;
			label = label .. '–' .. month.e&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		customtoggles = customtoggles .. (month.l and customtoggles .. month.l or '')&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	for i, combination in ipairs(month.combinations) do&lt;br /&gt;
		customtoggles = customtoggles .. ' mw-customtoggle-' .. combination -- up to 2 combinations per month so no need to table.concat()&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return p._toggleButton(false, customtoggles, id, label)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p._lastXToggleButton(years, duration, combinationsL)&lt;br /&gt;
	local months, id = years[#years].months, 'l' .. duration&lt;br /&gt;
	local i, customtoggles = #months, {' mw-customtoggle-' .. id}&lt;br /&gt;
&lt;br /&gt;
	if #years &amp;gt; 1 then&lt;br /&gt;
		local year = years[#years].year&lt;br /&gt;
		while months[i].l do&lt;br /&gt;
			customtoggles[#customtoggles+1] = ' mw-customtoggle-' .. year .. lang:lc(months[i].mon) .. '-' .. id&lt;br /&gt;
			if i == 1 then&lt;br /&gt;
				if year == years[#years].year then&lt;br /&gt;
					year = years[#years-1].year&lt;br /&gt;
					months = years[#years-1].months&lt;br /&gt;
					i = #months&lt;br /&gt;
				else -- either first month is also lastX month or lastX spans more than 2 years, which is not intended yet&lt;br /&gt;
					break&lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				i = i - 1&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		while i &amp;gt; 0 and months[i].l do&lt;br /&gt;
			customtoggles[#customtoggles+1] = ' mw-customtoggle-' .. lang:lc(months[i].mon) .. '-' .. id&lt;br /&gt;
			i = i - 1&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	for i, combinationL in ipairs(combinationsL) do&lt;br /&gt;
		customtoggles[#customtoggles+1] = ' mw-customtoggle-' .. combinationL -- up to 3 combinationsL in 90 days&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return p._toggleButton(true, table.concat(customtoggles), id, mw.ustring.format(i18n.lastXDays, duration))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p._buildTogglesBar(dateList, duration, nooverlap)&lt;br /&gt;
	local years = {{year=dateList[1].year, months={{mon=dateList[1].mon, combinations={}}}}}&lt;br /&gt;
	local months, combinationsL = years[1].months, {}&lt;br /&gt;
&lt;br /&gt;
	local function addMonth(month)&lt;br /&gt;
		if month.mon ~= months[#months].mon then -- new month&lt;br /&gt;
			if month.year ~= years[#years].year then -- new year&lt;br /&gt;
				years[#years+1] = {year=month.year, months={}}&lt;br /&gt;
				months = years[#years].months -- switch months list&lt;br /&gt;
			end&lt;br /&gt;
			months[#months+1] = {mon=month.mon, combinations={}}&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	for i = 2, #dateList do -- deduplicate years and months&lt;br /&gt;
		if #dateList[i] == 0 then -- specific date&lt;br /&gt;
			addMonth(dateList[i])&lt;br /&gt;
			months[#months].l = months[#months].l or dateList[i].l -- so that both ...-mon and ...-mon-lX classes are created&lt;br /&gt;
		elseif #dateList[i] == 1 then -- interval within month&lt;br /&gt;
			addMonth(dateList[i][1])&lt;br /&gt;
			months[#months].l = months[#months].l or dateList[i].l&lt;br /&gt;
		else -- multimonth interval&lt;br /&gt;
			for j, month in ipairs(dateList[i]) do&lt;br /&gt;
				addMonth(month)&lt;br /&gt;
				months[#months].combinations[#months[#months].combinations+1] = dateList[i].id&lt;br /&gt;
			end&lt;br /&gt;
			combinationsL[#combinationsL+1] = dateList[i].id:find('-l%d+$') and dateList[i].id&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if nooverlap then&lt;br /&gt;
		local lastDate = dateList[#dateList]&lt;br /&gt;
		months[#months].e = tonumber(os.date('%d', lastDate.nDate or lastDate.nEndDate or lastDate.nAltEndDate)) -- end of final month&lt;br /&gt;
&lt;br /&gt;
		local i = #dateList&lt;br /&gt;
		repeat&lt;br /&gt;
			i = i - 1&lt;br /&gt;
		until i == 0 or (dateList[i].mon or dateList[i][1].mon) ~= months[#months].mon&lt;br /&gt;
		if i == 0 then -- start of first and final month&lt;br /&gt;
			months[#months].s = tonumber(os.date('%d', dateList[1].nDate))&lt;br /&gt;
		else&lt;br /&gt;
			months[#months].s = 1&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	years[#years].l = true -- to activate toggle and respective months bar&lt;br /&gt;
&lt;br /&gt;
	local monthToggles, divs = {}, nil&lt;br /&gt;
	if #years &amp;gt; 1 then&lt;br /&gt;
		local yearToggles, monthsDivs = {}, {}&lt;br /&gt;
		for i, year in ipairs(years) do&lt;br /&gt;
			yearToggles[#yearToggles+1] = p._yearToggleButton(year)&lt;br /&gt;
			monthToggles = {}&lt;br /&gt;
			months = year.months&lt;br /&gt;
			for j, month in ipairs(months) do&lt;br /&gt;
				monthToggles[#monthToggles+1] = p._monthToggleButton(year.year, month)&lt;br /&gt;
			end&lt;br /&gt;
			monthsDivs[#monthsDivs+1] =&lt;br /&gt;
				'&amp;lt;div class=&amp;quot;mw-collapsible' .. (year.l and '' or ' mw-collapsed') ..&lt;br /&gt;
				'&amp;quot; id=&amp;quot;mw-customcollapsible-' .. year.year .. '&amp;quot;&amp;gt;' .. table.concat(monthToggles) .. '&amp;lt;/div&amp;gt;'&lt;br /&gt;
		end&lt;br /&gt;
		divs = '&amp;lt;div&amp;gt;' .. table.concat(yearToggles) .. '&amp;lt;/div&amp;gt;' .. table.concat(monthsDivs)&lt;br /&gt;
	else&lt;br /&gt;
		for i, month in ipairs(months) do&lt;br /&gt;
			monthToggles[#monthToggles+1] = p._monthToggleButton(nil, month)&lt;br /&gt;
		end&lt;br /&gt;
		divs = '&amp;lt;div&amp;gt;' .. table.concat(monthToggles) .. '&amp;lt;/div&amp;gt;'&lt;br /&gt;
	end&lt;br /&gt;
	divs = divs .. '&amp;lt;div&amp;gt;' .. p._lastXToggleButton(years, duration, combinationsL) .. '&amp;lt;/div&amp;gt;'&lt;br /&gt;
	return '&amp;lt;div class=&amp;quot;nomobile&amp;quot; style=&amp;quot;text-align:center&amp;quot;&amp;gt;' .. divs .. '&amp;lt;/div&amp;gt;'&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local numwidth = {n=0, t=2.45, m=3.5, d=3.5, w=4.55, x=5.6}&lt;br /&gt;
&lt;br /&gt;
local bkgClasses = {&lt;br /&gt;
	'mcc-d',	--deaths&lt;br /&gt;
	'mcc-r',	--recoveries&lt;br /&gt;
	'mcc-c',	--cases or altlbl1&lt;br /&gt;
	'mcc-a2',	--altlbl2&lt;br /&gt;
	'mcc-a3'	--altlbl3&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function p._customBarStacked(args)&lt;br /&gt;
	local barargs = {}&lt;br /&gt;
&lt;br /&gt;
	barargs[1] = args[1]&lt;br /&gt;
&lt;br /&gt;
	local function _numwidth(i)&lt;br /&gt;
		return args.numwidth:sub(i,i)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if args[7] or args[8] then -- is it acceptable to have one and not the other?&lt;br /&gt;
		barargs[2] =&lt;br /&gt;
			'&amp;lt;span class=mcc-r' .. _numwidth(1) .. '&amp;gt;' .. (args[7] or '') .. '&amp;lt;/span&amp;gt;' ..&lt;br /&gt;
			'&amp;lt;span class=mcc-l' .. _numwidth(2) .. '&amp;gt;' .. (args[8] or '') .. '&amp;lt;/span&amp;gt;'&lt;br /&gt;
	end&lt;br /&gt;
	if #args.numwidth == 4 then&lt;br /&gt;
		barargs.note2 = (args[9] or args[10]) and&lt;br /&gt;
			(args.numwidth:sub(3,3) ~= 'n' and '&amp;lt;span class=mcc-r' .. _numwidth(3) .. '&amp;gt;' .. (args[9] or '') .. '&amp;lt;/span&amp;gt;' or '') ..&lt;br /&gt;
			'&amp;lt;span class=mcc-l' .. _numwidth(4) .. '&amp;gt;' .. (args[10] or '') .. '&amp;lt;/span&amp;gt;'&lt;br /&gt;
		or ''&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	for i = 1, 5 do&lt;br /&gt;
		barargs[i+2] = args[i+1] / args.divisor&lt;br /&gt;
		barargs['title' .. i] = args[i+1]&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	barargs.align = 'cdcc'&lt;br /&gt;
	barargs.bkgclasses = bkgClasses&lt;br /&gt;
	barargs.collapsed = args.collapsed&lt;br /&gt;
	barargs.id = args.id&lt;br /&gt;
&lt;br /&gt;
	return BarBox.stacked(barargs)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p._row(args)&lt;br /&gt;
	local barargs = {}&lt;br /&gt;
&lt;br /&gt;
	barargs[1] = (args[1] or '⋮') .. (args.note0 or '')&lt;br /&gt;
	barargs[2] = args[2] or 0&lt;br /&gt;
	barargs[3] = args[3] or 0&lt;br /&gt;
&lt;br /&gt;
	if args['alttot1'] then&lt;br /&gt;
		barargs[4] = args['alttot1']&lt;br /&gt;
	elseif args[4] then&lt;br /&gt;
		barargs[4] = (args[4] or 0) - (barargs[2] or 0) - (barargs[3] or 0)&lt;br /&gt;
	else&lt;br /&gt;
		barargs[4] = 0&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	barargs[5] = args[5] or 0&lt;br /&gt;
&lt;br /&gt;
	if args['alttot2'] then&lt;br /&gt;
		barargs[6] = args['alttot2']&lt;br /&gt;
	elseif args[6] then&lt;br /&gt;
		barargs[6] = (args[6] or 0) - (barargs[2] or 0) - (barargs[3] or 0)&lt;br /&gt;
	else&lt;br /&gt;
		barargs[6] = 0&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	barargs[7] = args[7]&lt;br /&gt;
&lt;br /&gt;
	local function changeArg(firstright, valuecol, changecol)&lt;br /&gt;
		local change = ''&lt;br /&gt;
		if args['firstright' .. firstright] then&lt;br /&gt;
			change = '(' .. i18n.na .. ')'&lt;br /&gt;
		elseif not args[1] and args[valuecol] then&lt;br /&gt;
			change = '(=)'&lt;br /&gt;
		else&lt;br /&gt;
			change = args[changecol] and '(' .. args[changecol] .. ')' or ''&lt;br /&gt;
		end&lt;br /&gt;
		change = change .. (args['note' .. firstright] or '')&lt;br /&gt;
&lt;br /&gt;
		return change ~= '' and change&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	barargs[8] = changeArg(1, 7, 8)&lt;br /&gt;
	barargs[9] = args[9]&lt;br /&gt;
	barargs[10] = changeArg(2, 9, 10)&lt;br /&gt;
&lt;br /&gt;
	barargs.divisor = args.divisor&lt;br /&gt;
	barargs.numwidth = args.numwidth&lt;br /&gt;
&lt;br /&gt;
	local dates&lt;br /&gt;
	if args.collapsible then&lt;br /&gt;
		local duration = args.duration&lt;br /&gt;
		if args.daysToEnd &amp;gt;= duration then&lt;br /&gt;
			barargs.collapsed = true&lt;br /&gt;
		else&lt;br /&gt;
			barargs.collapsed = false&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		if args.nooverlap and args.daysToEnd &amp;lt; duration then&lt;br /&gt;
			barargs.id = 'l' .. duration&lt;br /&gt;
		elseif args.nDate then&lt;br /&gt;
			dates = {year=tonumber(os.date('%Y', args.nDate)), mon=lang:formatDate('M', os.date('%Y-%m', args.nDate)),&lt;br /&gt;
				l=args.daysToEnd &amp;lt; duration and '-l' .. duration, nDate=args.nDate}&lt;br /&gt;
			barargs.id = (args.multiyear and dates.year or '') .. lang:lc(dates.mon) .. (dates.l or '')&lt;br /&gt;
		else&lt;br /&gt;
			local id, y, m, ey, em = {},&lt;br /&gt;
				tonumber(os.date('%Y', args.nStartDate or args.nAltStartDate)),&lt;br /&gt;
				tonumber(os.date('%m', args.nStartDate or args.nAltStartDate)),&lt;br /&gt;
				tonumber(os.date('%Y', args.nEndDate   or args.nAltEndDate  )),&lt;br /&gt;
				tonumber(os.date('%m', args.nEndDate   or args.nAltEndDate  ))&lt;br /&gt;
			dates = {nStartDate=args.nStartDate, nAltStartDate=args.nAltStartDate, nEndDate=args.nEndDate, nAltEndDate=args.nAltEndDate}&lt;br /&gt;
&lt;br /&gt;
			repeat&lt;br /&gt;
				id[#id+1] = (args.multiyear and y or '') .. lang:lc(monthAbbrs[m])&lt;br /&gt;
				dates[#dates+1] = {year=y, mon=monthAbbrs[m]}&lt;br /&gt;
				y = y + math.floor(m / 12)&lt;br /&gt;
				m = m % 12 + 1&lt;br /&gt;
			until y == ey and m &amp;gt; em or y &amp;gt; ey&lt;br /&gt;
&lt;br /&gt;
			dates.l = args.daysToEnd &amp;lt; duration and '-l' .. duration&lt;br /&gt;
			id = table.concat(id, '-') .. (dates.l or '')&lt;br /&gt;
			barargs.id = id&lt;br /&gt;
			dates.id = id&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		barargs.collapsed = false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return p._customBarStacked(barargs), dates&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p._buildBars(args)&lt;br /&gt;
	local frame = mw.getCurrentFrame()&lt;br /&gt;
	local updatePeriod = 86400 -- temporary implementation only supports daily updates&lt;br /&gt;
&lt;br /&gt;
	local function getUnix(timestamp)&lt;br /&gt;
		return lang:formatDate('U', timestamp)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local rows, prevRow = {}, {}&lt;br /&gt;
	for line in mw.text.gsplit(args.data, '\n') do&lt;br /&gt;
		local i, barargs = 1, {}&lt;br /&gt;
		-- parameter parsing, basic type/missing value handling&lt;br /&gt;
		for parameter in mw.text.gsplit(line, ';') do&lt;br /&gt;
			if parameter:find('^%s*%a') then&lt;br /&gt;
				parameter = mw.text.split(parameter, '=')&lt;br /&gt;
				parameter[1] = mw.text.trim(parameter[1])&lt;br /&gt;
				if parameter[1]:find('^alttot') then&lt;br /&gt;
					parameter[2] = tonumber(frame:callParserFunction('#expr', parameter[2]))&lt;br /&gt;
				else&lt;br /&gt;
					parameter[2] = mw.text.trim(parameter[2])&lt;br /&gt;
					if parameter[1]:find('^firstright') then&lt;br /&gt;
						parameter[2] = yesno(parameter[2])&lt;br /&gt;
					elseif parameter[2] == '' then&lt;br /&gt;
						parameter[2] = nil&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
				barargs[parameter[1]] = parameter[2]&lt;br /&gt;
			else&lt;br /&gt;
				parameter = mw.text.trim(parameter)&lt;br /&gt;
				if parameter ~= '' then&lt;br /&gt;
					if i &amp;gt;= 2 and i &amp;lt;= 6 then&lt;br /&gt;
						parameter = tonumber(frame:callParserFunction('#expr', parameter))&lt;br /&gt;
						if not parameter then&lt;br /&gt;
							error(('Data parameters 2 to 6 must not be formatted. i=%d, line=%s'):format(i, line))&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
					barargs[i] = parameter&lt;br /&gt;
				end&lt;br /&gt;
				i = i + 1&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local bValid, nDateDiff&lt;br /&gt;
		-- get relevant date info based on previous row&lt;br /&gt;
		if barargs[1] then&lt;br /&gt;
			bValid, barargs.nDate = pcall(getUnix, barargs[1])&lt;br /&gt;
			assert(bValid, 'invalid date &amp;quot;' .. barargs[1] .. '&amp;quot;')&lt;br /&gt;
			if prevRow.nDate or prevRow.nEndDate then&lt;br /&gt;
				nDateDiff = barargs.nDate - (prevRow.nDate or prevRow.nEndDate)&lt;br /&gt;
				if nDateDiff &amp;gt; updatePeriod then&lt;br /&gt;
					if nDateDiff == 2 * updatePeriod then&lt;br /&gt;
						prevRow = {nDate=barargs.nDate-updatePeriod}&lt;br /&gt;
						prevRow[1] = os.date('%Y-%m-%d', prevRow.nDate)&lt;br /&gt;
					else&lt;br /&gt;
						prevRow = {nStartDate=(prevRow.nDate or prevRow.nEndDate)+updatePeriod, nEndDate=barargs.nDate-updatePeriod}&lt;br /&gt;
					end&lt;br /&gt;
					rows[#rows+1] = prevRow&lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				prevRow.nEndDate = barargs.nDate - updatePeriod&lt;br /&gt;
				if prevRow.nStartDate == prevRow.nEndDate then&lt;br /&gt;
					prevRow.nDate = prevRow.nEndDate&lt;br /&gt;
					prevRow[1] = os.date('%Y-%m-%d', prevRow.nDate)&lt;br /&gt;
				-- as nAltStartDate assumes a minimal multiday interval, it's possible for it to be greater if a true previous span is 1 day&lt;br /&gt;
				elseif prevRow.nAltStartDate and prevRow.nAltStartDate &amp;gt;= prevRow.nEndDate then&lt;br /&gt;
					error('a row in a consecutive intervals group is 1 day long and misses the date parameter')&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			if barargs.enddate then&lt;br /&gt;
				bValid, barargs.nEndDate = pcall(getUnix, barargs.enddate)&lt;br /&gt;
				assert(bValid, 'invalid enddate &amp;quot;' .. barargs.enddate .. '&amp;quot;')&lt;br /&gt;
			end&lt;br /&gt;
			if prevRow.nDate or prevRow.nEndDate then&lt;br /&gt;
				barargs.nStartDate = (prevRow.nDate or prevRow.nEndDate) + updatePeriod&lt;br /&gt;
				if barargs.nStartDate == barargs.nEndDate then&lt;br /&gt;
					barargs.nDate = barargs.nEndDate&lt;br /&gt;
					barargs[1] = os.date('%Y-%m-%d', barargs.nDate)&lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				prevRow.nAltEndDate = (prevRow.nStartDate or prevRow.nAltStartDate) + updatePeriod&lt;br /&gt;
				barargs.nAltStartDate = prevRow.nAltEndDate + updatePeriod&lt;br /&gt;
				if barargs.nEndDate and barargs.nAltStartDate &amp;gt;= barargs.nEndDate then&lt;br /&gt;
					error('a row in a consecutive intervals group is 1 day long and misses the date parameter')&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local function fillCols(col, change)&lt;br /&gt;
			local data = args['right' .. col .. 'data']&lt;br /&gt;
			local changetype = args['changetype' .. col]&lt;br /&gt;
			local value, num, prevnum&lt;br /&gt;
&lt;br /&gt;
			if data == 'alttot1' then&lt;br /&gt;
				num = barargs.alttot1 or barargs[4]&lt;br /&gt;
				prevnum = prevRow.alttot1 or prevRow[4]&lt;br /&gt;
			elseif data == 'alttot2' then&lt;br /&gt;
				num = barargs.alttot2 or barargs[6]&lt;br /&gt;
				prevnum = prevRow.alttot2 or prevRow[6]&lt;br /&gt;
			elseif data then&lt;br /&gt;
				num = barargs[data+1]&lt;br /&gt;
				prevnum = prevRow[data+1]&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			if data and num then -- nothing in column, source found, and data exists&lt;br /&gt;
				value = changetype == 'o' and '' or lang:formatNum(num) -- set value to num if changetype isn't 'o'&lt;br /&gt;
&lt;br /&gt;
				if not change and not barargs['firstright' .. col] then&lt;br /&gt;
					if prevnum and prevnum ~= 0 then -- data on previous row&lt;br /&gt;
						if num - prevnum ~= 0 then --data has changed since previous row&lt;br /&gt;
							change = num-prevnum&lt;br /&gt;
							if changetype == 'a' then -- change type is &amp;quot;absolute&amp;quot;&lt;br /&gt;
								if change &amp;gt; 0 then&lt;br /&gt;
									change = '+' .. lang:formatNum(change)&lt;br /&gt;
								end&lt;br /&gt;
							else -- change type is &amp;quot;percent&amp;quot;, &amp;quot;only percent&amp;quot; or undefined&lt;br /&gt;
								local percent = 100 * change / prevnum -- calculate percent&lt;br /&gt;
								local rounding = math.abs(percent) &amp;gt;= 10 and '%.0f' or math.abs(percent) &amp;gt;= 1 and '%.1f' or '%.2f'&lt;br /&gt;
								percent = tonumber(rounding:format(percent)) -- round to two sigfigs&lt;br /&gt;
&lt;br /&gt;
								if percent &amp;gt; 0 then&lt;br /&gt;
									change = '+' .. lang:formatNum(percent) .. '%'&lt;br /&gt;
								elseif percent &amp;lt; 0 then&lt;br /&gt;
									change = lang:formatNum(percent) .. '%'&lt;br /&gt;
								else&lt;br /&gt;
									change = '='&lt;br /&gt;
								end&lt;br /&gt;
							end&lt;br /&gt;
						else -- data has not changed since previous row&lt;br /&gt;
							change = '='&lt;br /&gt;
						end&lt;br /&gt;
					else -- no data on previous row&lt;br /&gt;
						barargs['firstright' .. col] = true -- set to (n.a.)&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			return value, change&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		if not barargs[7] then&lt;br /&gt;
			barargs[7], barargs[8] = fillCols(1, barargs[8])&lt;br /&gt;
		end&lt;br /&gt;
		if not barargs[9] then&lt;br /&gt;
			barargs[9], barargs[10] = fillCols(2, barargs[10])&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		rows[#rows+1] = barargs&lt;br /&gt;
		prevRow = barargs&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- calculate and pass repetitive (except daysToEnd) parameters to each row&lt;br /&gt;
	local lastRow = rows[#rows]&lt;br /&gt;
	local total = {lastRow[2] or 0, lastRow[3] or 0, [4]=lastRow[5] or 0}&lt;br /&gt;
	total[3] = lastRow.alttot1 or lastRow[4] and lastRow[4] - total[1] - total[2] or 0&lt;br /&gt;
	total[5] = lastRow.alttot2 or lastRow[6] and lastRow[6] - total[1] - total[2] or 0&lt;br /&gt;
	local divisor = (total[1] + total[2] + total[3] + total[4] + total[5]) / (args.barwidth - 5) --should be -3 if borders didn't go inward&lt;br /&gt;
	local firstDate, lastDate = rows[1].nDate, lastRow.nDate or lastRow.nEndDate&lt;br /&gt;
	local multiyear = os.date('%Y', firstDate) ~= os.date('%Y', lastDate - (args.nooverlap and args.duration * 86400 or 0))&lt;br /&gt;
	if args.collapsible ~= false then&lt;br /&gt;
		args.collapsible = (lastDate - firstDate) / 86400 &amp;gt;= args.duration&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local bars, dateList = {}, {}&lt;br /&gt;
	for i, row in ipairs(rows) do -- build rows&lt;br /&gt;
		row.divisor = divisor&lt;br /&gt;
		row.numwidth = args.numwidth&lt;br /&gt;
		row.collapsible = args.collapsible&lt;br /&gt;
		row.duration = args.duration&lt;br /&gt;
		row.nooverlap = args.nooverlap&lt;br /&gt;
		row.daysToEnd = (lastDate - (row.nDate or row.nEndDate or row.nAltEndDate)) / 86400&lt;br /&gt;
		row.multiyear = multiyear&lt;br /&gt;
&lt;br /&gt;
		bars[#bars+1], dateList[#dateList+1] = p._row(row)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return table.concat(bars, '\n'), dateList&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p._barColors = { -- also in styles.css&lt;br /&gt;
	'#A50026',	--deaths&lt;br /&gt;
	'SkyBlue',	--recoveries&lt;br /&gt;
	'Tomato',	--cases or altlbl1&lt;br /&gt;
	'Gold',		--altlbl2&lt;br /&gt;
	'OrangeRed'	--altlbl3&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function p._legend0(args)&lt;br /&gt;
	return&lt;br /&gt;
		'&amp;lt;span style=&amp;quot;font-size:90%; margin:0px&amp;quot;&amp;gt;' ..&lt;br /&gt;
			'&amp;lt;span style=&amp;quot;background-color:' .. (args[1] or 'none') ..&lt;br /&gt;
			'; border:' .. (args.border or 'none') ..&lt;br /&gt;
			'; color:' .. (args[1] or 'none') .. '&amp;quot;&amp;gt;' ..&lt;br /&gt;
				'&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;' .. '&amp;lt;/span&amp;gt;' ..&lt;br /&gt;
			'&amp;amp;nbsp;' .. (args[2] or '') .. '&amp;lt;/span&amp;gt;'&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p._chart(args)&lt;br /&gt;
	for key, value in pairs(args) do&lt;br /&gt;
		if ({float=1, barwidth=1, numwidth=1, changetype=1})[key:gsub('%d', '')] then&lt;br /&gt;
			args[key] = value:lower()&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local barargs = {}&lt;br /&gt;
&lt;br /&gt;
	barargs.css = 'Module:Medical cases chart/styles.css'&lt;br /&gt;
	barargs.float = args.float or 'right'&lt;br /&gt;
&lt;br /&gt;
	args.barwidth = args.barwidth or 'medium'&lt;br /&gt;
	local barwidth&lt;br /&gt;
	if args.barwidth == 'thin' then&lt;br /&gt;
		barwidth = 120&lt;br /&gt;
	elseif args.barwidth == 'medium' then&lt;br /&gt;
		barwidth = 280&lt;br /&gt;
	elseif args.barwidth == 'wide' then&lt;br /&gt;
		barwidth = 400&lt;br /&gt;
	elseif args.barwidth == 'auto' then&lt;br /&gt;
		barwidth = 'auto'&lt;br /&gt;
	else&lt;br /&gt;
		error('unrecognized barwidth')&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local function _numwidth(i)&lt;br /&gt;
		local nw = args.numwidth:sub(i,i)&lt;br /&gt;
		return assert(numwidth[nw], 'unrecognized numwidth[' .. i .. ']')&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	args.numwidth = args.numwidth or 'mm'&lt;br /&gt;
	if args.numwidth:sub(1,1) == 'n' or args.numwidth:sub(2,2) == 'n' or args.numwidth:sub(4,4) == 'n' then&lt;br /&gt;
		error('&amp;quot;n&amp;quot; is only allowed in numwidth[3]')&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local buffer = 0.3 --until automatic numwidth determination&lt;br /&gt;
	local right1width, right2width = _numwidth(1) + 0.3 + _numwidth(2) + buffer, 0&lt;br /&gt;
	if #args.numwidth == 4 then&lt;br /&gt;
		right2width = _numwidth(3) + _numwidth(4) + buffer&lt;br /&gt;
		if args.numwidth:sub(3,3) ~= 'n' then&lt;br /&gt;
			right2width = right2width + 0.3&lt;br /&gt;
		end&lt;br /&gt;
		if args.right2 then&lt;br /&gt;
			right2width = math.ceil(right2width / 0.88 * 100) / 100 -- from td scale to th&lt;br /&gt;
		else&lt;br /&gt;
			right1width = right1width + 0.8 + right2width&lt;br /&gt;
			right2width = 0&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	right1width = math.ceil(right1width / 0.88 * 100) / 100&lt;br /&gt;
&lt;br /&gt;
	if tonumber(barwidth) then&lt;br /&gt;
		-- transform colswidth from th to td scale, add it with border-spacing, and finally transform to table scale&lt;br /&gt;
		local relwidth = math.ceil(((7.08 + right1width + right2width) * 0.88 + 0.8 * (args.right2 and 5 or 4)) * 88) / 100&lt;br /&gt;
		barargs.width = 'calc(' .. relwidth .. 'em + ' .. barwidth .. 'px)' --why do the bar borders go inward (no +2)?&lt;br /&gt;
		barargs.barwidth = barwidth .. 'px'&lt;br /&gt;
	else&lt;br /&gt;
		barargs.width = 'auto'&lt;br /&gt;
		barargs.barwidth = 'auto'&lt;br /&gt;
	end&lt;br /&gt;
	barargs.lineheight = args.rowheight&lt;br /&gt;
&lt;br /&gt;
	local title = {}&lt;br /&gt;
&lt;br /&gt;
	local function spaces(n)&lt;br /&gt;
		local nbsp = '&amp;amp;nbsp;'&lt;br /&gt;
		return '&amp;lt;span class=&amp;quot;nowrap&amp;quot;&amp;gt;' .. nbsp:rep(n) .. '&amp;lt;/span&amp;gt;'&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local location = lang:ucfirst(mw.ustring.gsub(args.location, i18n.the_, ''))&lt;br /&gt;
	local navbartitle = args.outbreak .. i18n._data .. '/' ..&lt;br /&gt;
		(args.location3 and args.location3 .. '/' or '') ..&lt;br /&gt;
		(args.location2 and args.location2 .. '/' or '') ..&lt;br /&gt;
		location .. i18n._medicalCasesChart&lt;br /&gt;
&lt;br /&gt;
	local navbar = require('Module:Navbar')._navbar&lt;br /&gt;
	title[1] = (args.pretitle and args.pretitle .. ' ' or '') ..&lt;br /&gt;
		args.disease .. ' ' .. i18n.casesIn .. ' ' .. args.location ..&lt;br /&gt;
		(args.location2 and ', ' .. args.location2 or '') ..&lt;br /&gt;
		(args.location3 and ', ' .. args.location3 or '') ..&lt;br /&gt;
		(args.posttitle and ' ' .. args.posttitle or '') .. spaces(2) ..'(' ..&lt;br /&gt;
		navbar({navbartitle, titleArg=':' .. mw.getCurrentFrame():getParent():getTitle(), mini=1, nodiv=1}) ..&lt;br /&gt;
		')&amp;lt;br /&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
	title[2] = p._legend0({p._barColors[1], i18n.deaths})&lt;br /&gt;
	args.recoveries = args.recoveries == nil and true or args.recoveries&lt;br /&gt;
	title[3] = args.recoveries and spaces(3) .. p._legend0({p._barColors[2], args.reclbl or i18n.recoveries}) or ''&lt;br /&gt;
	title[4] = args.altlbl1 ~= 'hide' and spaces(3) .. p._legend0({p._barColors[3], args.altlbl1 or i18n.activeCases}) or ''&lt;br /&gt;
	title[5] = args.altlbl2 and spaces(3) .. p._legend0({p._barColors[4], args.altlbl2}) or ''&lt;br /&gt;
	title[6] = args.altlbl3 and spaces(3) .. p._legend0({p._barColors[5], args.altlbl3}) or ''&lt;br /&gt;
&lt;br /&gt;
	local togglesbar, buildargs = nil, {}&lt;br /&gt;
&lt;br /&gt;
	args.right1 = args.right1 or i18n.noOfCases&lt;br /&gt;
	args.duration = args.duration or 15&lt;br /&gt;
	args.nooverlap = args.nooverlap or false&lt;br /&gt;
&lt;br /&gt;
	buildargs.barwidth = tonumber(barwidth) or 280&lt;br /&gt;
	buildargs.numwidth = args.numwidth:gsub('d', 'm')&lt;br /&gt;
	if args.datapage then&lt;br /&gt;
		local externalData = require('Module:Medical cases chart/data')._externalData&lt;br /&gt;
		buildargs.data = externalData(args)&lt;br /&gt;
	else&lt;br /&gt;
		buildargs.data = args.data&lt;br /&gt;
	end&lt;br /&gt;
	-- if no right1data and right1 title is cases, use 3rd classification&lt;br /&gt;
	buildargs.right1data = args.right1data or args.right1 == i18n.noOfCases and 3&lt;br /&gt;
	-- if no right2data and right2 title is deaths, use 1st classification&lt;br /&gt;
	buildargs.right2data = args.right2data or (args.right2 == i18n.noOfDeaths or args.right2 == i18n.noOfDeaths2) and 1&lt;br /&gt;
	buildargs.changetype1 = (args.changetype1 or args.changetype or ''):sub(1,1) -- 1st letter&lt;br /&gt;
	buildargs.changetype2 = (args.changetype2 or args.changetype or ''):sub(1,1) -- 1st letter&lt;br /&gt;
	buildargs.collapsible = args.collapsible&lt;br /&gt;
	buildargs.duration = args.duration&lt;br /&gt;
	buildargs.nooverlap = args.nooverlap&lt;br /&gt;
&lt;br /&gt;
	local dateList&lt;br /&gt;
	barargs.bars, dateList = p._buildBars(buildargs)&lt;br /&gt;
&lt;br /&gt;
	if buildargs.collapsible then&lt;br /&gt;
		togglesbar = p._buildTogglesBar(dateList, args.duration, args.nooverlap)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	title[7] = togglesbar and '&amp;lt;br /&amp;gt;' .. togglesbar or ''&lt;br /&gt;
	barargs.title = table.concat(title)&lt;br /&gt;
&lt;br /&gt;
	barargs.left1 = '&amp;lt;div style=&amp;quot;width:7.08em&amp;quot;&amp;gt;' .. i18n.date .. '&amp;lt;/div&amp;gt;'&lt;br /&gt;
	barargs.right1 = '&amp;lt;div class=center style=&amp;quot;width:' .. right1width .. 'em&amp;quot;&amp;gt;' .. args.right1 .. '&amp;lt;/div&amp;gt;' --center isn't necessary with proper&lt;br /&gt;
	if args.right2 then																					   --numwidth, but better safe than sorry&lt;br /&gt;
		barargs.right2 = '&amp;lt;div class=center style=&amp;quot;width:' .. right2width ..'em&amp;quot;&amp;gt;' .. args.right2 .. '&amp;lt;/div&amp;gt;'&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	barargs.footer = args.caption&lt;br /&gt;
	local box = BarBox.create(barargs)&lt;br /&gt;
	return tostring(box)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local getArgs = require('Module:Arguments').getArgs&lt;br /&gt;
&lt;br /&gt;
function p.barColors(frame)&lt;br /&gt;
	local args = getArgs(frame)&lt;br /&gt;
	return p._barColors[tonumber(args[1])]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.chart(frame)&lt;br /&gt;
	local args = getArgs(frame, {&lt;br /&gt;
		valueFunc = function (key, value)&lt;br /&gt;
			if value and value ~= '' then&lt;br /&gt;
				key = key:gsub('%d', '')&lt;br /&gt;
				if ({rowheight=1, duration=1, rightdata=1})[key] then -- if key in {...}&lt;br /&gt;
					return tonumber(value) or value&lt;br /&gt;
				end&lt;br /&gt;
				if ({recoveries=1, collapsible=1, nooverlap=1})[key] then&lt;br /&gt;
					return yesno(value)&lt;br /&gt;
				end&lt;br /&gt;
				return value&lt;br /&gt;
			end&lt;br /&gt;
			return nil&lt;br /&gt;
		end&lt;br /&gt;
	})&lt;br /&gt;
	return p._chart(args)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
</feed>