יחידה:גרפים: הבדלים בין גרסאות בדף

אין תקציר עריכה
עדכון הקוד מהעדכונים שנעשו בויקיפדיה האנגלית
שורה 59: שורה 59:
'#FF5005',
'#FF5005',
}
}
local hideGroupLegends


local function nulOrWhitespace( s )
local function nulOrWhitespace( s )
שורה 65: שורה 67:


local function createGroupList( tab, legends, cols )
local function createGroupList( tab, legends, cols )
     if #legends > 1 then
     if #legends > 1 and not hideGroupLegends then
         table.insert( tab, mw.text.tag( 'div' ) )
         table.insert( tab, mw.text.tag( 'div' ) )
         local list = {}
         local list = {}
         local spanStyle = "padding:0 1em;background-color:%s;box-shadow:2px -1px 4px 0 silver;margin-right:1em;"
         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
         for gi = 1, #legends do
             local span = mw.text.tag( 'span', { style = string.format( spanStyle, cols[gi] ) }, ' ' ) .. ' '..  legends[gi]
             local span = mw.text.tag( 'span', { style = string.format( spanStyle, cols[gi], cols[gi] ) }, ' ' ) .. ' '..  legends[gi]
             table.insert( list, mw.text.tag( 'li', {}, span ) )
             table.insert( list, mw.text.tag( 'li', {}, span ) )
         end
         end
         table.insert( tab,
         table.insert( tab,
             mw.text.tag( 'ul',
             mw.text.tag( 'ul',
-- chrome bug with column display with rtl.                 {style="text-align:right;width:100%;list-style:none;-webkit-column-width:12em;-moz-column-width:12em;column-width:12em"},
                 {style="width:100%;list-style:none;-webkit-column-width:12em;-moz-column-width:12em;column-width:12em;"},
                {style="text-align:right;width:100%;list-style:none;-moz-column-width:12em;column-width:12em"},
                 table.concat( list, '\n' )
                 table.concat( list, '\n' )
             )
             )
שורה 88: שורה 89:
     local radius
     local radius
     local values, colors, names, legends, links = {}, {}, {}, {}, {}
     local values, colors, names, legends, links = {}, {}, {}, {}, {}
     local delimiter = args[keywords.delimiter] or ':'
     local delimiter = args.delimiter or ':'
     local lang = mw.getContentLanguage()
     local lang = mw.getContentLanguage()


שורה 101: שורה 102:
             local value, name, color, link = unpack( mw.text.split( slice, '%s*' .. delimiter .. '%s*' ) )
             local value, name, color, link = unpack( mw.text.split( slice, '%s*' .. delimiter .. '%s*' ) )
             values[i] = tonumber( lang:parseFormattedNumber( value ) )
             values[i] = tonumber( lang:parseFormattedNumber( value ) )
                 or error( string.format( 'Slice %d: \"%s\" could not be parsed as a number', i, value or '' ) )
                 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]
             colors[i] = not nulOrWhitespace( color ) and color or defColors[i * 2]
             names[i] = name or ''
             names[i] = name or ''
             links[i] = link
             links[i] = link
שורה 108: שורה 109:
          
          
         radius = getArg( 'radius', 150 )
         radius = getArg( 'radius', 150 )
        hideGroupLegends = not nulOrWhitespace( args[keywords.hideGroupLegends] )
         local slicesStr = getArg( 'slices' )
         local slicesStr = getArg( 'slices' )
         local prefix = getArg( 'unitsPrefix', '', '_', ' ' )
         local prefix = getArg( 'unitsPrefix', '', '_', ' ' )
שורה 128: שורה 130:
             local addprec = percent and string.format( ' (%0.1f%%)', value / sum * 100 ) or ''
             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 )
             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]]', mw.ustring.gsub( legends[i] or '', '[%[%]]', '' ) ) )
             links[i] = mw.text.trim( links[i] or mw.ustring.format( '[[#noSuchAnchor|%s]]', legends[i] ) )
         end
         end
     end
     end
שורה 201: שורה 203:
     analyzeParams()
     analyzeParams()
     if #values == 0 then error( "no slices found - can't draw pie chart" ) end
     if #values == 0 then error( "no slices found - can't draw pie chart" ) end
     addRes( mw.text.tag( 'div', { style = string.format( "max-width:%spx", radius * 2 ) } ) )
     addRes( mw.text.tag( 'div', { class = 'chart', 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 ) } ) )
     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()
     createSlices()
שורה 219: שורה 221:
     local values, xlegends, colors, tooltips, yscales = {}, {}, {}, {} ,{}, {}, {}
     local values, xlegends, colors, tooltips, yscales = {}, {}, {}, {} ,{}, {}, {}
     local groupNames, unitsSuffix, unitsPrefix, links = {}, {}, {}, {}
     local groupNames, unitsSuffix, unitsPrefix, links = {}, {}, {}, {}
     local width, height, stack, delimiter, xrotation = 500, 350, false, args[keywords.delimiter] or ':'
     local width, height, stack, delimiter = 500, 350, false, args.delimiter or ':'
     local chartWidth, chartHeight, defcolor, scalePerGroup  
     local chartWidth, chartHeight, defcolor, scalePerGroup, accumulateTooltip
    local valueInBar = args[keywords.valueInBar]




שורה 228: שורה 229:


     function validate()
     function validate()
         function asGroups( name, tab, toDuplicate, emptyOK, defGroup )
         function asGroups( name, tab, toDuplicate, emptyOK )
             if #tab == 0 and not emptyOK then
             if #tab == 0 and not emptyOK then
                 error( "must supply values for " .. keywords[name] )
                 error( "must supply values for " .. keywords[name] )
שורה 235: שורה 236:
                 for i = 2, numGroups do tab[i] = tab[1] end
                 for i = 2, numGroups do tab[i] = tab[1] end
             end
             end
            if defGroup then for i = #tab + 1, numGroups do tab[i] = defGroup[i] end end
             if #tab > 0 and #tab ~= numGroups then
             if #tab > 0 and #tab ~= numGroups then
                 error ( keywords[name] .. ' should contain the same number of items as the number of groups (' .. numGroups .. '), but it has ' .. #tab .. ' items')
                 error ( keywords[name] .. ' must contain the same number of items as the number of groups, but it contains ' .. #tab .. ' items and there are ' .. numGroups .. ' groups')
             end
             end
         end
         end
שורה 249: שורה 249:
         numValues = #values[1]
         numValues = #values[1]
         defcolor = defcolor or 'blue'
         defcolor = defcolor or 'blue'
         colors[1] = colors[1] or defColors[1]
         colors[1] = colors[1] or defcolor
         scaleWidth = scalePerGroup and 80 * numGroups or 100
         scaleWidth = scalePerGroup and 80 * numGroups or 100
         chartWidth = width -scaleWidth
         chartWidth = width -scaleWidth
         asGroups( 'unitsPrefix', unitsPrefix, true, true )
         asGroups( 'unitsPrefix', unitsPrefix, true, true )
         asGroups( 'unitsSuffix', unitsSuffix, true, true )
         asGroups( 'unitsSuffix', unitsSuffix, true, true )
         asGroups( 'colors', colors, false, true, defColors )
         asGroups( 'colors', colors, true, true )
         asGroups( 'groupNames', groupNames, false, false )
         asGroups( 'groupNames', groupNames, false, false )
         if stack and scalePerGroup then
         if stack and scalePerGroup then
שורה 262: שורה 262:
             if #values[gi] ~= numValues then error( keywords.group .. " " .. gi .. " does not have same number of values as " .. keywords.group .. " 1" ) end
             if #values[gi] ~= numValues then error( keywords.group .. " " .. gi .. " does not have same number of values as " .. keywords.group .. " 1" ) end
         end
         end
         if #xlegends ~= numValues then error( 'Illegal number of ' .. keywords.xlegend .. '. Should be exactly ' .. numValues ..
         if #xlegends ~= numValues then error( 'Illegal number of ' .. keywords.xlegend .. '. Should be exactly ' .. numValues ) end
                '\n' .. '(param="' .. args[keywords.xlegend] .. '")') end
     end
     end


שורה 292: שורה 291:
             elseif k == keywords.scalePerGroup then scalePerGroup = true
             elseif k == keywords.scalePerGroup then scalePerGroup = true
             elseif k == keywords.defcolor then defcolor = v
             elseif k == keywords.defcolor then defcolor = v
             elseif k == keywords.xrotation then xrotation = true
             elseif k == keywords.accumulateTooltip then accumulateTooltip = not nulOrWhitespace( v )
            elseif k == keywords.hideGroupLegends then hideGroupLegends = not nulOrWhitespace( v )
             else
             else
                 for keyword, tab in pairs( {
                 for keyword, tab in pairs( {
שורה 315: שורה 315:
         local ordermag = 10 ^ math.floor( math.log10( x ) )
         local ordermag = 10 ^ math.floor( math.log10( x ) )
         local normalized = x /  ordermag
         local normalized = x /  ordermag
         local top = normalized >= 2.5 and ( math.floor( normalized + 1 ) )
         local top = normalized >= 1.5 and ( math.floor( normalized + 1 ) ) or 1.5
            or normalized >= 2 and 2.5
            or normalized >= 1.5 and 2
            or 1.5
         return ordermag * top, top, ordermag
         return ordermag * top, top, ordermag
     end
     end


     function calcHeightLimits() -- if limits were passed by user, use ithem, otherwise calculate. for "stack" there's only one limet.
     function calcHeightLimits() -- if limits were passed by user, use them, otherwise calculate. for "stack" there's only one limet.
         if stack then
         if stack then
             local sums = {}
             local sums = {}
שורה 333: שורה 330:
             for i, group in ipairs( values ) do yscales[i] = math.max( unpack( group ) ) end
             for i, group in ipairs( values ) do yscales[i] = math.max( unpack( group ) ) end
         end
         end
         for i, scale in ipairs( yscales ) do yscales[i] = roundup( scale ) 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
         if not scalePerGroup then for i = 1, #values do yscales[i] = math.max( unpack( yscales ) ) end end
     end
     end
שורה 346: שורה 343:


     function calcHeights( gi, i, val )
     function calcHeights( gi, i, val )
         local barHeight = math.floor( val / yscales[gi] * chartHeight + 0.5 ) -- add half to make it "round" insstead of "trunc"
         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
         local top, base = chartHeight - barHeight, 0
         if stack then
         if stack then
שורה 364: שורה 361:
     function calcx( gi, i )
     function calcx( gi, i )
         local setOffset, setWidth = groupBounds( i )
         local setOffset, setWidth = groupBounds( i )
         if stack then
         if stack or numGroups == 1 then
             local barWidth = math.min( 38, math.floor( 0.8 * setWidth ) )
             local barWidth = math.min( 38, math.floor( 0.8 * setWidth ) )
             return setOffset + (setWidth - barWidth) / 2, barWidth
             return setOffset + (setWidth - barWidth) / 2, barWidth
שורה 374: שורה 371:
     end
     end


     function drawbar( gi, i, val )
     function drawbar( gi, i, val, ttval )
         local color, tooltip, custom = 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 left, barWidth = calcx( gi, i )
         local barHeight, top = calcHeights( gi, i, val )
         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:2px -1px 4px 0 silver;overflow:hidden;text-align:center;line-height:%spx",
        -- borders so it shows up when printing
                         left, top, barHeight, barWidth, barWidth, color, barHeight)
         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;",
                         left, top, barHeight-1, barWidth-2, barWidth-2, color, color)
         local link = links[gi] and links[gi][i] or ''
         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 ''
         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 .. (valueInBar and val or '' ) ) )
         table.insert( res, mw.text.tag( 'div', { style = style, title = tooltip, }, img ) )
     end
     end


שורה 391: שורה 389:
             local _, top, ordermag = roundup( yscale * 0.999 )
             local _, top, ordermag = roundup( yscale * 0.999 )
             local numnotches = top <= 1.5 and top * 4
             local numnotches = top <= 1.5 and top * 4
                    or top == 2.5 and 10
                     or top < 4  and top * 2
                     or top <= 4  and top * 2
                     or top
                     or top
             local valStyleStr =
             local valStyleStr =
שורה 425: שורה 422:
     function drawXlegends()
     function drawXlegends()
         local setOffset, setWidth
         local setOffset, setWidth
         local legendDivStyleFormat = "position:absolute;left:%spx;top:20px;min-width:%spx;max-width:%spx;text-align:center;veritical-align:top;padding:0 0.3em;"
         local legendDivStyleFormat = "position:absolute;left:%spx;top:10px;min-width:%spx;max-width:%spx;text-align:center;vertical-align:top;"
         local tickDivstyleFormat = "position:absolute;left:%spx;height:10px;width:1px;border-left:1px solid black;"
         local tickDivstyleFormat = "position:absolute;left:%spx;height:10px;width:1px;border-left:1px solid black;"
         for i = 1, numValues do
         for i = 1, numValues do
             if not nulOrWhitespace( xlegends[i] ) then
             if not nulOrWhitespace( xlegends[i] ) then
                 setOffset, setWidth = groupBounds( i )
                 setOffset, setWidth = groupBounds( i )
                 table.insert( res, mw.text.tag( 'div',  
                -- setWidth = 0.85 * setWidth
                    {  
                 table.insert( res, mw.text.tag( 'div', { style = string.format( legendDivStyleFormat, setOffset + 5, setWidth - 10, setWidth - 10 ) }, xlegends[i] or '' ) )
                        class = xrotation and 'rot90', -- assigning null to a key means "do not use this key"
                        style = string.format( legendDivStyleFormat, setOffset, setWidth, setWidth )  
                    }, xlegends[i] or '' )
                )
                 table.insert( res, mw.text.tag( 'div', { style = string.format( tickDivstyleFormat, setOffset + setWidth / 2 ) }, '' ) )
                 table.insert( res, mw.text.tag( 'div', { style = string.format( tickDivstyleFormat, setOffset + setWidth / 2 ) }, '' ) )
             end
             end
שורה 442: שורה 435:


     function drawChart()
     function drawChart()
         table.insert( res, mw.text.tag( 'div', { style = string.format( 'max-width:%spx;', width ) } ) )
         table.insert( res, mw.text.tag( 'div', { class = 'chart', 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("position:relative;min-height:%spx;min-width:%spx;max-width:%spx;", height, width, width ) } ) )
       
 
         table.insert( res, mw.text.tag( 'div', { style = string.format("position:relative;top:0;left:0;min-height:%spx;min-width:%spx;max-width:%spx;border-left:1px black solid;border-bottom:1px black solid;", chartHeight, chartWidth, chartWidth ) } ) )
         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 gi, group in pairs( values ) do
             for i, val in ipairs( group ) do
             for i, val in ipairs( group ) do
                 drawbar( gi, i, val )
                if acum then acum[i] = ( acum[i] or 0 ) + val end
                 drawbar( gi, i, val, acum and acum[i] )
             end
             end
         end
         end
         table.insert( res, '</div>' )
         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 ) } ) )
         table.insert( res, mw.text.tag( 'div', { style = string.format("position:absolute;top:0;left:-4px;height:%spx;min-width:%spx;max-width:%spx;", chartHeight, scaleWidth, scaleWidth, scaleWidth ) } ) )
         drawYScale()
         drawYScale()
         table.insert( res, '</div>' )
         table.insert( res, '</div>' )
         table.insert( res, mw.text.tag( 'div', { style = string.format( "position:absolute;top:%spx;left:%spx;width:%spx;", chartHeight, scaleWidth, chartWidth ) } ) )
         table.insert( res, mw.text.tag( 'div', { style = string.format( "position:absolute;top:%spx;left:%spx;width:%spx;", chartHeight, scaleWidth, chartWidth ) } ) )
         drawXlegends()
         drawXlegends()
שורה 477: שורה 470:
     [keywords.pieChart] = pieChart,
     [keywords.pieChart] = pieChart,
}
}
--</syntaxhighlight>
--</source>