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 edit
Series edit
The 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 edit
You 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 edit
By 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 edit
You 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 edit
Progress 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