Modul:Chart: Perbedaan antara revisi

Konten dihapus Konten ditambahkan
mw.text was added in last upgrade
NFarras (bicara | kontrib)
kTidak ada ringkasan suntingan
 
(2 revisi perantara oleh 2 pengguna tidak ditampilkan)
Baris 1:
--<source lang=lua>
--[[
keywords are used for languages: they are the names of the actual
parameters of the template
]]
 
local keywords = {
barChart = 'bar chart',
pieChart = 'pie chart',
width = 'width',
height = 'height',
stack = 'stack',
colors = 'colors',
group = 'group',
xlegend = 'x legends',
yticks = 'y tick marks',
tooltip = 'tooltip',
accumulateTooltip = 'tooltip value accumulation',
links = 'links',
defcolor = 'default color',
scalePerGroup = 'scale per group',
unitsPrefix = 'units prefix',
unitsSuffix = 'units suffix',
groupNames = 'group names',
hideGroupLegends = 'hide group legends',
slices = 'slices',
slice = 'slice',
radius = 'radius',
percent = 'percent',
 
} -- here is what you want to translate
 
local defColors = require "Module:Chart/Default colors"
local hideGroupLegends
 
local function nulOrWhitespace( s )
return not s or mw.text.trim( s ) == ''
end
 
local function createGroupList( tab, legends, cols )
if #legends > 1 and not hideGroupLegends then
table.insert( tab, mw.text.tag( 'div' ) )
local list = {}
local spanStyle = "padding:0 1em;background-color:%s;border:1px solid %s;margin-right:1em;-webkit-print-color-adjust:exact;"
for gi = 1, #legends do
local span = mw.text.tag( 'span', { style = string.format( spanStyle, cols[gi], cols[gi] ) }, '&nbsp;' ) .. ' '.. legends[gi]
table.insert( list, mw.text.tag( 'li', {}, span ) )
end
table.insert( tab,
mw.text.tag( 'ul',
{style="width:100%;list-style:none;-webkit-column-width:12em;-moz-column-width:12em;column-width:12em;"},
table.concat( list, '\n' )
)
)
table.insert( tab, '</div>' )
end
end
 
function pieChart( frame )
local res, imslices, args = {}, {}, frame.args
local radius
local values, colors, names, legends, links = {}, {}, {}, {}, {}
local delimiter = args.delimiter or ':'
local lang = mw.getContentLanguage()
 
function getArg( s, def, subst, with )
local result = args[keywords[s]] or def or ''
if subst and with then result = mw.ustring.gsub( result, subst, with ) end
return result
end
 
function analyzeParams()
function addSlice( i, slice )
local value, name, color, link = unpack( mw.text.split( slice, '%s*' .. delimiter .. '%s*' ) )
values[i] = tonumber( lang:parseFormattedNumber( value ) )
or error( string.format( 'Slice %d: "%s", first item("%s") could not be parsed as a number', i, value or '', sliceStr ) )
colors[i] = not nulOrWhitespace( color ) and color or defColors[i * 2]
names[i] = name or ''
links[i] = link
end
radius = getArg( 'radius', 150 )
hideGroupLegends = not nulOrWhitespace( args[keywords.hideGroupLegends] )
local slicesStr = getArg( 'slices' )
local prefix = getArg( 'unitsPrefix', '', '_', ' ' )
local suffix = getArg( 'unitsSuffix', '', '_', ' ' )
local percent = args[keywords.percent]
local sum = 0
local i, value = 0
for slice in mw.ustring.gmatch( slicesStr or '', "%b()" ) do
i = i + 1
addSlice( i, mw.ustring.match( slice, '^%(%s*(.-)%s*%)$' ) )
end
for k, v in pairs(args) do
local ind = mw.ustring.match( k, '^' .. keywords.slice .. '%s+(%d+)$' )
if ind then addSlice( tonumber( ind ), v ) end
end
for _, val in ipairs( values ) do sum = sum + val end
for i, value in ipairs( values ) do
local addprec = percent and string.format( ' (%0.1f%%)', value / sum * 100 ) or ''
legends[i] = mw.ustring.format( '%s: %s%s%s%s', names[i], prefix, lang:formatNum( value ), suffix, addprec )
links[i] = mw.text.trim( links[i] or mw.ustring.format( '[[#noSuchAnchor|%s]]', legends[i] ) )
end
end
 
function addRes( ... )
for _, v in pairs( { ... } ) do
table.insert( res, v )
end
end
 
function createImageMap()
addRes( '{{#tag:imagemap|', 'Image:Circle frame.svg{{!}}' .. ( radius * 2 ) .. 'px' )
addRes( unpack( imslices ) )
addRes( 'desc none', '}}' )
end
 
function drawSlice( i, q, start )
local color = colors[i]
local angle = start * 2 * math.pi
local sin, cos = math.abs( math.sin( angle ) ), math.abs( math.cos( angle ) )
local wsin, wcos = sin * radius, cos * radius
local s1, s2, w1, w2, w3, w4, width, border
local style
if q == 1 then
border = 'left'
w1, w2, w3, w4 = 0, 0, wsin, wcos
s1, s2 = 'bottom', 'left'
elseif q == 2 then
border = 'bottom'
w1, w2, w3, w4 = 0, wcos, wsin, 0
s1, s2 = 'bottom', 'right'
elseif q == 3 then
border = 'right'
w1, w2, w3, w4 = wsin, wcos, 0, 0
s1, s2 = 'top', 'right'
else
border = 'top'
w1, w2, w3, w4 = wsin, 0, 0, wcos
s1, s2 = 'top', 'left'
end
 
local style = string.format( 'border:solid transparent;position:absolute;%s:%spx;%s:%spx;width:%spx;height:%spx', s1, radius, s2, radius, radius, radius )
if start <= ( q - 1 ) * 0.25 then
style = string.format( '%s;border:0;background-color:%s', style, color )
else
style = string.format( '%s;border-width:%spx %spx %spx %spx;border-%s-color:%s', style, w1, w2, w3, w4, border, color )
end
addRes( mw.text.tag( 'div', { style = style }, '' ) )
end
 
function createSlices()
function coordsOfAngle( angle )
return ( 100 + math.floor( 100 * math.cos( angle ) ) ) .. ' ' .. ( 100 - math.floor( 100 * math.sin( angle ) ) )
end
 
local sum, start = 0, 0
for _, value in ipairs( values ) do sum = sum + value end
for i, value in ipairs(values) do
local poly = { 'poly 100 100' }
local startC, endC = start / sum, ( start + value ) / sum
local startQ, endQ = math.floor( startC * 4 + 1 ), math.floor( endC * 4 + 1 )
for q = startQ, math.min( endQ, 4 ) do drawSlice( i, q, startC ) end
for angle = startC * 2 * math.pi, endC * 2 * math.pi, 0.02 do
table.insert( poly, coordsOfAngle( angle ) )
end
table.insert( poly, coordsOfAngle( endC * 2 * math.pi ) .. ' 100 100 ' .. links[i] )
table.insert( imslices, table.concat( poly, ' ' ) )
start = start + values[i]
end
end
 
analyzeParams()
if #values == 0 then error( "no slices found - can't draw pie chart" ) end
addRes( mw.text.tag( 'div', { class = 'chart noresize', style = string.format( 'margin-top:0.5em;max-width:%spx;', radius * 2 ) } ) )
addRes( mw.text.tag( 'div', { style = string.format( 'position:relative;min-width:%spx;min-height:%spx;max-width:%spx;overflow:hidden;', radius * 2, radius * 2, radius * 2 ) } ) )
createSlices()
addRes( mw.text.tag( 'div', { style = string.format( 'position:absolute;min-width:%spx;min-height:%spx;overflow:hidden;', radius * 2, radius * 2 ) } ) )
createImageMap()
addRes( '</div>' ) -- close "position:relative" div that contains slices and imagemap.
addRes( '</div>' ) -- close "position:relative" div that contains slices and imagemap.
createGroupList( res, legends, colors ) -- legends
addRes( '</div>' ) -- close containing div
return frame:preprocess( table.concat( res, '\n' ) )
end
 
 
function barChart( frame )
local res = {}
local args = frame.args -- can be changed to frame:getParent().args
local values, xlegends, colors, tooltips, yscales = {}, {}, {}, {} ,{}, {}, {}
local groupNames, unitsSuffix, unitsPrefix, links = {}, {}, {}, {}
local width, height, yticks, stack, delimiter = 500, 350, -1, false, args.delimiter or ':'
local chartWidth, chartHeight, defcolor, scalePerGroup, accumulateTooltip
 
local keywords = {
width = 'width',
height = 'height',
stack = 'stack',
colors = 'colors',
group = 'group',
xlegend = 'x legends',
tooltip = 'tooltip',
defcolor = 'default color',
scalePerGroup = 'scale per group',
unitsPrefix = 'units prefix',
unitsSuffix = 'units suffix',
groupNames = 'group names',
} -- here is where you want to translate
 
local numGroups, numValues
local scaleWidth
 
function nulOrWhitespace( s )
return not s or mw.text.trim( s ) == ''
end
 
function validate()
Baris 38 ⟶ 209:
end
if #tab > 0 and #tab ~= numGroups then
error ( keywords[name] .. ' shouldmust contain the same number of items as the number of groups, but it contains ' .. #tab .. ' items and there are (' .. numGroups .. ') groups')
end
end
Baris 63 ⟶ 234:
if #values[gi] ~= numValues then error( keywords.group .. " " .. gi .. " does not have same number of values as " .. keywords.group .. " 1" ) end
end
if #xlegends ~= numValues then error( 'Illegal number of ' .. keywords.xlegend .. '. Should be exatlyexactly ' .. numValues ) end
end
 
Baris 90 ⟶ 261:
end
elseif k == keywords.stack then stack = true
elseif k == keywords.yticks then yticks = tonumber(v) or -1
elseif k == keywords.scalePerGroup then scalePerGroup = true
elseif k == keywords.defcolor then defcolor = v
elseif k == keywords.accumulateTooltip then accumulateTooltip = not nulOrWhitespace( v )
elseif k == keywords.hideGroupLegends then hideGroupLegends = not nulOrWhitespace( v )
else
for keyword, tab in pairs( {
Baris 101 ⟶ 275:
unitsSuffix = unitsSuffix,
groupNames = groupNames,
links = links,
} ) do
if testone( keywords[keyword], k, v, tab )
Baris 117 ⟶ 292:
end
 
function calcHeightLimits() -- if limits were passed by user, use ithemthem, otherwise calculate. for "stack" there's only one limet.
if stack then
local sums = {}
Baris 123 ⟶ 298:
for i, val in ipairs( group ) do sums[i] = ( sums[i] or 0 ) + val end
end
local sum = roundup( math.max( unpack( sums ) ) )
for i = 1, #values do yscales[i] = sum end
else
for i, group in ipairs( values ) do yscales[i] = math.max( unpack( group ) ) end
end
for i, scale in ipairs( yscales ) do yscales[i] = roundup( scale * 0.9999 ) end
if not scalePerGroup then for i = 1, #values do yscales[i] = math.max( unpack( yscales ) ) end end
end
 
function tooltip( gi, i, val )
if tooltips and tooltips[gi] and not nulOrWhitespace( tooltips[gi][i] ) then return tooltips[gi][i], true end
local groupName = not nulOrWhitespace( groupNames[gi] ) and groupNames[gi] .. ': ' or ''
local prefix = unitsPrefix[gi] or unitsPrefix[1] or ''
local suffix = unitsSuffix[gi] or unitsSuffix[1] or ''
return mw.ustring.gsub(groupName .. prefix .. mw.getContentLanguage():formatNum( tonumber( val ) or 0 ) .. suffix, '_', ' '), false
end
 
function calcHeights( gi, i, val )
local barHeight = math.floor( val / yscales[gi] * chartHeight + 0.5 ) -- add half to make it "round" instead of "trunc"
local top, base = chartHeight - barHeight, 0
if stack then
Baris 159 ⟶ 334:
function calcx( gi, i )
local setOffset, setWidth = groupBounds( i )
setWidthif =stack 0.85or numGroups == *1 setWidththen
if stack then
local barWidth = math.min( 38, math.floor( 0.8 * setWidth ) )
return setOffset + (setWidth - barWidth) / 2, barWidth
end
setWidth = 0.85 * setWidth
local barWidth = math.floor( 0.75 * setWidth / numGroups )
local left = setOffset + math.floor( ( gi - 1 ) / numGroups * setWidth )
Baris 169 ⟶ 344:
end
 
function drawbar( gi, i, val, ttval )
if val == '0' then return end -- do not show single line (borders....) if value is 0, or rather, '0'. see talkpage
local color, tooltip = colors[gi] or defcolor or 'blue', tooltip( gi, i, val )
 
local color, tooltip, custom = colors[gi] or defcolor or 'blue', tooltip( gi, i, ttval or val )
local left, barWidth = calcx( gi, i )
local barHeight, top = calcHeights( gi, i, val )
local style = string.format("position:absolute;left:%spx;top:%spx;height:%spx;min-width:%spx;max-width:%spx;background-color:%s;box-shadow:4px -3px 3px 1px grey;",
-- borders so it shows up when printing
left, top, barHeight, barWidth, barWidth, color)
local style = string.format("position:absolute;left:%spx;top:%spx;height:%spx;min-width:%spx;max-width:%spx;background-color:%s;-webkit-print-color-adjust:exact;border:1px solid %s;border-bottom:none;overflow:hidden;",
table.insert( res, mw.text.tag( 'div', { style = style, title = tooltip, }, "" ) )
left, top, barHeight-1, barWidth-2, barWidth-2, color, color)
local link = links[gi] and links[gi][i] or ''
local img = not nulOrWhitespace( link ) and mw.ustring.format( '[[File:Transparent.png|1000px|link=%s|%s]]', link, custom and tooltip or '' ) or ''
table.insert( res, mw.text.tag( 'div', { style = style, title = tooltip, }, img ) )
end
 
 
function drawYScale()
function drawSingle( gi, color, width, yticks, single )
local yscale = yscales[gi]
local _, top, ordermag = roundup( yscale * 0.999 )
local numnotches = topyticks <>= 1.50 and topyticks * 4or
or (top <= 4 1.5 and top * 24
or top < 4 and top * or top2
or top)
local valStyleStr =
single and 'position:absolute;height=20px;text-align:right;vertical-align:middle;width:%spx;top:%spx;padding:0 2px'
Baris 207 ⟶ 389:
local color = colors[gi] or defcolor
table.insert( res, mw.text.tag( 'div', { style = string.format( colStyle, chartHeight, colWidth, left, color, color ) } ) )
drawSingle( gi, color, colWidth, yticks )
table.insert( res, '</div>' )
end
else
drawSingle( 1, 'black', scaleWidth, yticks, true )
end
end
Baris 217 ⟶ 399:
function drawXlegends()
local setOffset, setWidth
local legendDivStyleFormat = "position:absolute;left:%spx;top:10px;min-width:%spx;max-width:%spx;text-align:center;veriticalvertical-align:top;"
local tickDivstyleFormat = "position:absolute;left:%spx;height:10px;width:1px;border-left:1px solid black;"
for i = 1, numValues do
if not nulOrWhitespace( xlegends[i] ) then
setOffset, setWidth = groupBounds( i )
-- setWidth = 0.85 * setWidth
table.insert( res, mw.text.tag( 'div', { style = string.format( legendDivStyleFormat, setOffset -+ 5, setWidth - 10, setWidth - 10 ) }, xlegends[i] or '' ) )
table.insert( res, mw.text.tag( 'div', { style = string.format( tickDivstyleFormat, setOffset + setWidth / 2 ) }, '' ) )
end
end
end
 
function printGroupList()
if #groupNames > 1 then
local list = {}
local spanStyle = "padding:0 1em;background-color:%s"
for gi = 1, #groupNames do
local span = mw.text.tag( 'span', { style = string.format( spanStyle, colors[gi] ) }, '&nbsp;' ) .. ' '.. groupNames[gi]
table.insert( list, mw.text.tag( 'li', {}, span ) )
end
table.insert( res, mw.text.tag( 'ul', {}, table.concat( list, '\n' ) ) )
end
end
 
function drawChart()
table.insert( res, mw.text.tag( 'div', { class = 'chart noresize', style = string.format( 'margin-top:1em;max-width:%spx;', width ) } ) )
table.insert( res, mw.text.tag( 'div', { style = string.format("position:relative;min-height:%spx;min-width:%spx;max-width:%spx;", height, width, width ) } ) )
 
table.insert( res, mw.text.tag( 'div', { style = string.format("float:right;position:relative;min-height:%spx;min-width:%spx;max-width:%spx;border-left:1px black solid;border-bottom:1px black solid;", chartHeight, chartWidth, chartWidth ) } ) )
local acum = stack and accumulateTooltip and {}
 
for gi, group in pairs( values ) do
for i, val in ipairs( group ) do
drawbar(if gi,acum then acum[i,] val= ( acum[i] or 0 ) + val end
drawbar( gi, i, val, acum and acum[i] )
end
end
 
table.insert( res, '</div>' )
table.insert( res, mw.text.tag( 'div', { style = string.format("position:absolute;height:%spx;min-width:%spx;max-width:%spx;", chartHeight, scaleWidth, scaleWidth, scaleWidth ) } ) )
Baris 261 ⟶ 431:
table.insert( res, '</div>' )
table.insert( res, '</div>' )
createGroupList( res, groupNames, colors )
printGroupList()
table.insert( res, '</div>' )
end
Baris 272 ⟶ 442:
end
 
return {
['bar-chart'] = barChart },
[keywords.barChart] = barChart,
[keywords.pieChart] = pieChart,
}
--</source>