Module:Bar
Module:Bar generates a coloured bar for bar charts or progress bars with any number of data series. This is intended for use in more general templates.
Usage
editSeries
editThe bar is invoked with a series of values that represent data series. Each series specifies two or three arguments: the value, the colour, and an optional tooltip title. For example:
{{#invoke:bar|format|4,green,done|2,gray,pending|4,#FCC,not done}}
Total
editYou can explicitly specify the total number of values in the bar. If you do and the series add up to a smaller value, an equivalent empty space will be included at the end:
{| | {{#invoke:bar|format|7,green,done|total=10}} |- | {{#invoke:bar|format|4,green,done|total=10}} |- | {{#invoke:bar|format|2,green,done|total=10}} |}
Width
editBy default the bar will be set to 100% width, but you can specify any valid CSS width instead:
{| | {{#invoke:bar|format|7,green,done|width=5em}} |- | {{#invoke:bar|format|7,green,done|width=15em}} |}
Bar CSS
editYou can customize the appearance of the bar with CSS:
{{#invoke:bar|format|7,green,done|total=10|width=30em|barCSS=border:1px solid #CCC}}
Examples
editProgress bar
edit{| | {{#invoke:bar|format|7,green,done|total=10|width=30em|barCSS=border:1px solid #CCC}} | {{#expr:7 / 10 * 100}}% |}
70% |
Stacked bar chart
edit{| |+ Expenses vs profits |- | 2011 | {{#invoke:bar|format|100,red,expenses|50,green,profits|total=170|width=30em}} |- | 2012 | {{#invoke:bar|format|75,red,expenses|90,green,profits|total=170|width=30em}} |}
2011 | ||||
2012 |
Grouped bar chart
edit{| |+ Expenses vs revenue |- | 2011 | {{#invoke:bar|format|150,green,revenue|total=160|width=15em}} {{#invoke:bar|format|100,red,expenses|total=160|width=15em}} |- | 2012 | {{#invoke:bar|format|160,green,revenue|total=160|width=15em}} {{#invoke:bar|format|75,red,expenses|total=160|width=15em}} |}
2011 | |||||
2012 |
local p = {}
local inner = {}
--##########
--## Public functions
--##########
--- Render a bar chart.
-- @param frame The arguments passed to the script. See docs on renderFromLua.
function p.format( frame )
-- extract args
local width = frame.args['width']
local barCSS = frame.args['barCSS']
local zeroWidth = frame.args['zeroWidth']
local total = frame.args['total']
-- extract bar series from arguments like 'value,color,title'
local series = {}
for key, spec in ipairs(frame.args) do
spec = mw.text.split(spec, ',')
local data = {value = tonumber(spec[1] or 0), color = spec[2] or '#CCC', title = spec[3] or ''}
if data['value'] > 0 then
table.insert(series, data)
end
end
return p.renderFromLua(series, total, width, barCSS, zeroWidth)
end
--- Render a bar chart from Lua.
-- @param series A table representing the bars to render, consisting of a sequence of tables like {value = 14, color = '#CCC', title = 'tooltip text'}.
-- @param total (optional) The total number of values represented by all bar series.
-- @param width (optional) The CSS width of the bar.
-- @param barCss (optional) Additional CSS to apply to the rendered bar table.
-- @param zeroWidth (optional)
function p.renderFromLua(series, total, width, barCSS, zeroWidth)
-- parse arguments
width = width or '100%'
total = tonumber(total or 0)
zeroWidth = zeroWidth or '1px'
-- calculate total
local seriesTotal = 0
for k,v in ipairs(series) do
seriesTotal = seriesTotal + v['value']
end
if total < seriesTotal then
total = seriesTotal
end
-- inject empty series for uncharted values
if(seriesTotal < total) then
table.insert(series, {value = total - seriesTotal, color = 'transparent', title = ''})
end
-- inject ratios
for k,v in ipairs(series) do
v['total'] = total
v['ratio'] = inner.getRatio(v['value'], total)
if v['ratio'] == 0 then
v['width'] = zeroWidth
end
end
-- render
result = mw.html.create 'table'
:attr('role', 'presentation')
:css('width', width)
:cssText(barCSS)
:css('border-spacing', '0')
:tag 'tr'
for k, v in pairs(series) do
result:node(inner.renderSeries(v))
end
return result:allDone()
end
--##########
--## Private functions
--##########
--- Render an individual bar series.
-- @param series The bar series to render.
function inner.renderSeries(series)
-- ignore empty series
if not series.value or series.value == 0 then
return ''
end
-- set width
local width = series.width
if not(width) then
width = series.ratio .. '%'
end
-- format
return mw.html.create 'td'
:attr('title', series.title)
:css('width', width)
:css('background', series.color)
:attr('data-value', series.value)
:css('height', '1em')
:css('padding', '0')
end
--- Get the percentage ratio of two numbers as a decimal value.
-- @param value The number of items in the subset.
-- @param total The total number of items in the set.
function inner.getRatio(value, total)
if(total == 0) then
error('the total for a series cannot be zero')
end
return math.floor(value / total * 10000) / 100
end
return p