Bước tới nội dung

Mô đun:Medical cases chart

Bách khoa toàn thư mở Wikipedia
local yesno = require('Module:Yesno')local barBox = require('Module:Bar box')local lang = mw.getLanguage("en")local i18n = require('Module:Medical cases chart/i18n')["vi"]local monthAbbrs = {}for i = 1, 12 do	monthAbbrs[i] = lang:formatDate('M', '2020-' .. ('%02d'):format(i))endlocal function is(v)	return (v or '') ~= ''endlocal 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">' .. p._monthStr(label) .. '</span>' .. 	'<span class="mw-collapsible' .. off .. customtoggles .. '" id="mw-customcollapsible-' .. id .. '">' .. p._monthStr(label)  .. '</span>'	return outStringendfunction p._monthStr (str)	if str == "Jan" then return "Thg 1"	elseif str == "Feb" then return "Thg 2"	elseif str == "Mar" then return "Thg 3"	elseif str == "Apr" then return "Thg 4"	elseif str == "May" then return "Thg 5"	elseif str == "Jun" then return "Thg 6"	elseif str == "Jul" then return "Thg 7"	elseif str == "Aug" then return "Thg 8"	elseif str == "Sep" then return "Thg 9"	elseif str == "Oct" then return "Thg 10"	elseif str == "Nov" then return "Thg 11"	elseif str == "Dec" then return "Thg 12"	end	return strendfunction p._yearToggleButton(year)	return p._toggleButton(year.l, ' mw-customtoggle-' .. year.year, year.year, year.year)endfunction 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)endfunction 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, duration .. '&nbsp;ngày gần nhất')endfunction 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>'endlocal numwidth = {n=0, t=2.45, m=3.5, d=3.5, w=4.55, x=5.6}local bkgClasses = {	'mcc-d',	--tử vong	'mcc-r',	--hồi phục	'mcc-c',	--số ca hoặc altlbl1	'mcc-a2',	--altlbl2	'mcc-a3'	--altlbl3}function p._customBarStacked(args)	local barargs = {}	barargs[1] = args[1]	-- (next update)	-- local function _numwidth(i)	-- 	return args.numwidth:sub(i,i)	-- end	local function _numwidth(nw)		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		return 3.5	end	if args[7] or args[8] then -- is it acceptable to have one and not the other?		barargs[2] =			'<span class=mcc-r' .. _numwidth(1) .. '>' .. (args[7] or '') .. '</span>' ..			'<span class=mcc-l' .. _numwidth(2) .. '>' .. (args[8] or '') .. '</span>'	end	if #args.numwidth == 4 then		barargs.note2 = (args[9] or args[10]) and (args.numwidth:sub(3,3) ~= 'n' and '<span class=mcc-r' .. _numwidth(3) .. '>' .. (args[9] or '') .. '</span>' or '') ..			'<span class=mcc-l' .. _numwidth(4) .. '>' .. (args[10] or '') .. '</span>' or ''					local pad = args.numwidth:sub(3, 3) == 'n' and '0' or '0.3em'	end	width1 = 3.5	width2 = 3.5	if is(args.numwidth) then		width1 = _numwidth(mw.ustring.sub(args.numwidth,1,1))		width2 = _numwidth(mw.ustring.sub(args.numwidth,2,2))		width3 = _numwidth(mw.ustring.sub(args.numwidth,3,3))		width4 = _numwidth(mw.ustring.sub(args.numwidth,4,4))	end	barargs[2] =		'<span class="cbs-ibr" style="padding:0 0.3em 0 0; width:' .. width1 .. 'em">' .. (args[7] or '') .. '</span>' ..		'<span class="cbs-ibl" style="width:' .. width2 .. 'em">' .. (args[8] or '') .. '</span>'		if mw.ustring.len(args.numwidth) == 4 then		local padding = '0.3em'		if mw.ustring.sub(args.numwidth,3,3) == 'n' then			padding = '0'		end		barargs.note2 =			'<span class="cbs-ibr" style="padding:0 ' .. padding .. ' 0 0; width:' .. width3 .. 'em">' .. (args[9] or '') .. '</span>' ..			'<span class="cbs-ibl" style="width:' .. width4 .. 'em">' .. (args[10] or '') .. '</span>'	end	for i=1,5 do		barargs[2*i + 1] = p._barColors[i]		barargs[2*i + 2] = (tonumber(args[i+1]) or 0)/(tonumber(args.divisor) or 1)		barargs['title' .. i] = args[i+1]	end	barargs.align = 'cdcc'	barargs.collapsed = args.collapsed	barargs.id = args.id	barargs.rowstyle = is(tonumber(args.rowheight)) and ('line-height:'..args.rowheight..';') or nil	return barBox._stacked(barargs)endfunction 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 yesno(args['firstright' .. firstright]) == true then			change = '(' .. i18n.na .. ')'		elseif yesno(args['firstright' .. firstright]) == false or not is(args['firstright' .. firstright]) then			if not is(args[1]) and is(args[valuecol]) then				change = '(' .. i18n['='] .. ')'			else				change = is(args[changecol]) and '(' .. args[changecol] .. ')' or ''			end		end		return change	end	barargs[8] = changeArg(1,7,8)	barargs[9] = args[9] or ''	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 = true		else			barargs.collapsed = false		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 = false	end	return p._customBarStacked(barargs), datesendfunction p._buildBars(args)	local frame = mw.getCurrentFrame()	local lang = mw.getContentLanguage()	local updatePeriod = 86400 -- temporary implementation only supports daily updates	local function getUnix(timestamp)		return lang:formatDate('U', timestamp)	end		local rows, prevRow, maxparam = {}, {}, 1	for line in mw.text.gsplit(args.data, '\n') do		local i, barargs = 1, {}		for parameter in mw.text.gsplit(line, ';') do			parameter = mw.text.trim(parameter)			if string.find(parameter, '^%a') then				parameter = mw.text.split(parameter, '=')				if parameter[1] == 'alttot1' or parameter[1] == 'alttot2' then					parameter[2] = tonumber(frame:callParserFunction('#expr', parameter[2]))					if is(parameter[2]) then						maxparam = math.max(maxparam, parameter[2])					end				end				barargs[parameter[1]] = parameter[2]			else				-- parameter = mw.text.trim(parameter)				if is(parameter) then					if i >= 2 and i <= 6 then						parameter = tonumber(frame:callParserFunction('#expr', frame:callParserFunction('formatnum',parameter,'R')))						maxparam = math.max(maxparam, parameter or 1)					end					barargs[i] = parameter					if i == 7 or i == 9 then						parameter = tonumber(mw.ustring.match(frame:callParserFunction('formatnum',parameter,'R'), '^%d*'))						maxparam = math.max(maxparam, parameter or 1)					end				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)				elseif prevRow.nAltStartDate and prevRow.nAltStartDate >= prevRow.nEndDate then					error('Một dòng trong khoảng thời gian liên tiếp dài 1 ngày thiếu một ngày')				end			end		else			if barargs.enddate then				bValid, barargs.nEndDate = pcall(getUnix, barargs.enddate)				assert(bValid, 'giá trị enddate không hợp lệ "' .. 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('Một dòng trong khoảng thời gian liên tiếp dài 1 ngày thiếu một ngày')				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 is(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 yesno(barargs['firstright' .. col] ~= true) 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								percent = tonumber(mw.ustring.format(rounding, 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				barargs.prevDate = prevRow[1]		rows[#rows+1] = barargs		prevRow = barargs	end		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]) / (args.barwidth - 5)	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 = tonumber(args.divisor) and tonumber(args.divisor) or maxparam / (0.92 * args.barwidth)--divisor		row.numwidth = args.numwidth		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, '\n'), dateListend-- func -> objp._barColors = {	'#A50026', --tử vong	'SkyBlue', --hồi phục	'Tomato', --số ca hoặc altlbl1	'Gold', --altlbl2	'OrangeRed' --altlbl3}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>'endfunction 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 = 'Mô đun:Medical cases chart/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('barwidth không tồn tại')	end	local function _numwidth(i)		local nw = args.numwidth:sub(i,i)		return assert(numwidth[nw], 'unrecognized numwidth[' .. i .. ']')	end	local numwidth = 120	local right1 = numwidth - 8 -- -8 because of padding	if args.numwidth then		numwidth = _numwidth(1) + 10 + _numwidth(2)		if mw.ustring.len(args.numwidth) == 4 then			numwidth = numwidth + _numwidth(3) + _numwidth(4)			if mw.ustring.sub(args.numwidth,3,3) == 'n' then				numwidth = numwidth + 6			else				numwidth = numwidth + 10			end		end		right1 = _numwidth(1) + 2 + _numwidth(2)		if not args.right2 and mw.ustring.len(args.numwidth) == 4 then			right1 = right1 + _numwidth(3) + _numwidth(4)			if mw.ustring.sub(args.numwidth,3,3) == 'n' then				numwidth = numwidth + 6			else				numwidth = numwidth + 10			end		end	end		args.numwidth = args.numwidth or 'mm'	if args.numwidth:sub(1,1) == 'n' or args.numwidth:sub(2,2) == 'n' or args.numwidth:sub(4,4) == 'n' then		error('"n" chỉ được cho phép trong numwidth[3]')	end		local buffer = 0.3 --until automatic numwidth determination	local right1width, right2width = _numwidth(1) + 0.3 + _numwidth(2) + buffer, 0		if #args.numwidth == 4 then		right2width = _numwidth(3) + _numwidth(4) + buffer		if args.numwidth:sub(3, 3) ~= 'n' then			right2width = right2width + 0.3		end				if args.right2 then			right2width = math.ceil(right2width / 0.88 * 100) / 100 -- from td scale to th		else			right1width = right1width + 0.8 + right2width			right2width = 0		end	end	right1width = math.ceil(right1width / 0.88 * 100) / 100	local barwidth = 280	if tonumber(barwidth) then		local relwidth = math.ceil(((7.08 + right1width + right2width) * 0.88 + 0.8 * (args.right2 and 5 or 4)) * 88) / 100		barargs.width = 'calc(' .. relwidth .. 'em + ' .. barwidth .. 'px)' --why do the bar borders go inward (no +2)?		barargs.barwidth = barwidth .. 'px'	else		barargs.width = 'auto'		barargs.barwidth = 'auto'	end		barargs.lineheight = args.rowheight	local title = {}		local function spaces(n)		local nbsp = '&nbsp;'		return '<span class="nowrap">' .. nbsp:rep(n) .. '</span>'	end		local location = args.location	location = mw.ustring.upper(mw.ustring.sub(location,1,1)) .. mw.ustring.sub(location,2)	local navbartitle = args.outbreak .. '/' ..		(args.location3 and args.location3 .. '/' or '') ..		(args.location2 and args.location2 .. '/' or '') ..		'Biểu đồ số ca nhiễm tại ' .. location		local navbar = require('Module:Navbar')._navbar		title[1] = (args.pretitle and args.pretitle .. ' ' or '') ..		args.disease .. ' ' .. i18n.casesIn .. ' ' .. args.location ..		(args.location2 and ', ' .. args.location2 or '') ..		(args.location3 and ', ' .. args.location3 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.noOfDeaths})	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:gsub('d', 'm')	if args.datapage then		local externalData = require('Module:Medical cases chart/data')._externalData		buildargs.data = externalData(args)	else		buildargs.data = args.data	end		buildargs.right1data = args.right1data or args.right1 == i18n.noOfCases and 3	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 style="width:7.08em">' .. i18n.date .. '</div>'	barargs.right1 = '<div class=center style="width:' .. right1width .. 'em">' .. args.right1 .. '</div>'		if args.right2 then		barargs.right2 = '<div class=center style="width:' .. right2width ..'em">' .. args.right2 .. '</div>'	end	barargs.caption = args.caption or args.footer	local box = barBox._box(barargs)	return tostring(box)endlocal getArgs = require('Module:Arguments').getArgsfunction p.barColors(frame)	local args = getArgs(frame)	return p._barColors[tonumber(args[1])]endfunction p.chart(frame)	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-- deprecation warningfunction p.monthToggleButton(frame)	return "Bản mẫu này không còn được dùng nữa, bạn có thể xóa nó"endreturn p