<!DOCTYPE html>
|
<html>
|
<head>
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<title>The source code</title>
|
<link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
|
<script type="text/javascript" src="../resources/prettify/prettify.js"></script>
|
<style type="text/css">
|
.highlight { display: block; background-color: #ddd; }
|
</style>
|
<script type="text/javascript">
|
function highlight() {
|
document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
|
}
|
</script>
|
</head>
|
<body onload="prettyPrint(); highlight();">
|
<pre class="prettyprint lang-js"><span id='Ext-chart-series-Bar'>/**
|
</span> * Creates a Bar Chart. A Bar Chart is a useful visualization technique to display quantitative information for
|
* different categories that can show some progression (or regression) in the dataset. As with all other series, the Bar
|
* Series must be appended in the *series* Chart array configuration. See the Chart documentation for more information.
|
* A typical configuration object for the bar series could be:
|
*
|
* @example
|
* var store = Ext.create('Ext.data.JsonStore', {
|
* fields: ['name', 'data'],
|
* data: [
|
* { 'name': 'metric one', 'data':10 },
|
* { 'name': 'metric two', 'data': 7 },
|
* { 'name': 'metric three', 'data': 5 },
|
* { 'name': 'metric four', 'data': 2 },
|
* { 'name': 'metric five', 'data':27 }
|
* ]
|
* });
|
*
|
* Ext.create('Ext.chart.Chart', {
|
* renderTo: Ext.getBody(),
|
* width: 500,
|
* height: 300,
|
* animate: true,
|
* store: store,
|
* axes: [{
|
* type: 'Numeric',
|
* position: 'bottom',
|
* fields: ['data'],
|
* label: {
|
* renderer: Ext.util.Format.numberRenderer('0,0')
|
* },
|
* title: 'Sample Values',
|
* grid: true,
|
* minimum: 0
|
* }, {
|
* type: 'Category',
|
* position: 'left',
|
* fields: ['name'],
|
* title: 'Sample Metrics'
|
* }],
|
* series: [{
|
* type: 'bar',
|
* axis: 'bottom',
|
* highlight: true,
|
* tips: {
|
* trackMouse: true,
|
* width: 140,
|
* height: 28,
|
* renderer: function(storeItem, item) {
|
* this.setTitle(storeItem.get('name') + ': ' + storeItem.get('data') + ' views');
|
* }
|
* },
|
* label: {
|
* display: 'insideEnd',
|
* field: 'data',
|
* renderer: Ext.util.Format.numberRenderer('0'),
|
* orientation: 'horizontal',
|
* color: '#333',
|
* 'text-anchor': 'middle'
|
* },
|
* xField: 'name',
|
* yField: 'data'
|
* }]
|
* });
|
*
|
* In this configuration we set `bar` as the series type, bind the values of the bar to the bottom axis and set the
|
* xField or category field to the `name` parameter of the store. We also set `highlight` to true which enables smooth
|
* animations when bars are hovered. We also set some configuration for the bar labels to be displayed inside the bar,
|
* to display the information found in the `data1` property of each element store, to render a formated text with the
|
* `Ext.util.Format` we pass in, to have an `horizontal` orientation (as opposed to a vertical one) and we also set
|
* other styles like `color`, `text-anchor`, etc.
|
*/
|
Ext.define('Ext.chart.series.Bar', {
|
|
/* Begin Definitions */
|
|
extend: 'Ext.chart.series.Cartesian',
|
|
alternateClassName: ['Ext.chart.BarSeries', 'Ext.chart.BarChart', 'Ext.chart.StackedBarChart'],
|
|
requires: ['Ext.chart.axis.Axis', 'Ext.fx.Anim'],
|
|
<span id='Ext-chart-series-Bar-cfg-type'> /* End Definitions */
|
</span>
|
type: 'bar',
|
|
alias: 'series.bar',
|
<span id='Ext-chart-series-Bar-cfg-column'> /**
|
</span> * @cfg {Boolean} column Whether to set the visualization as column chart or horizontal bar chart.
|
*/
|
column: false,
|
|
<span id='Ext-chart-series-Bar-cfg-style'> /**
|
</span> * @cfg style Style properties that will override the theming series styles.
|
*/
|
style: {},
|
|
<span id='Ext-chart-series-Bar-cfg-gutter'> /**
|
</span> * @cfg {Number} gutter The gutter space between single bars, as a percentage of the bar width
|
*/
|
gutter: 38.2,
|
|
<span id='Ext-chart-series-Bar-cfg-groupGutter'> /**
|
</span> * @cfg {Number} groupGutter The gutter space between groups of bars, as a percentage of the bar width
|
*/
|
groupGutter: 38.2,
|
|
<span id='Ext-chart-series-Bar-cfg-xPadding'> /**
|
</span> * @cfg {Number/Object} xPadding Padding between the left/right axes and the bars.
|
* The possible values are a number (the number of pixels for both left and right padding)
|
* or an object with `{ left, right }` properties.
|
*/
|
xPadding: 0,
|
|
<span id='Ext-chart-series-Bar-cfg-yPadding'> /**
|
</span> * @cfg {Number/Object} yPadding Padding between the top/bottom axes and the bars.
|
* The possible values are a number (the number of pixels for both top and bottom padding)
|
* or an object with `{ top, bottom }` properties.
|
*/
|
yPadding: 10,
|
|
<span id='Ext-chart-series-Bar-cfg-stacked'> /**
|
</span> * @cfg {Boolean} stacked
|
* If set to `true` then bars for multiple `yField` values will be rendered stacked on top of one another.
|
* Otherwise, they will be rendered side-by-side. Defaults to `false`.
|
*/
|
|
constructor: function(config) {
|
this.callParent(arguments);
|
var me = this,
|
surface = me.chart.surface,
|
shadow = me.chart.shadow,
|
i, l;
|
config.highlightCfg = Ext.Object.merge({
|
lineWidth: 3,
|
stroke: '#55c',
|
opacity: 0.8,
|
color: '#f00'
|
}, config.highlightCfg);
|
Ext.apply(me, config, {
|
shadowAttributes: [{
|
"stroke-width": 6,
|
"stroke-opacity": 0.05,
|
stroke: 'rgb(200, 200, 200)',
|
translate: {
|
x: 1.2,
|
y: 1.2
|
}
|
}, {
|
"stroke-width": 4,
|
"stroke-opacity": 0.1,
|
stroke: 'rgb(150, 150, 150)',
|
translate: {
|
x: 0.9,
|
y: 0.9
|
}
|
}, {
|
"stroke-width": 2,
|
"stroke-opacity": 0.15,
|
stroke: 'rgb(100, 100, 100)',
|
translate: {
|
x: 0.6,
|
y: 0.6
|
}
|
}]
|
});
|
|
me.group = surface.getGroup(me.seriesId + '-bars');
|
if (shadow) {
|
for (i = 0, l = me.shadowAttributes.length; i < l; i++) {
|
me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
|
}
|
}
|
},
|
|
<span id='Ext-chart-series-Bar-method-getPadding'> // @private returns the padding.
|
</span> getPadding: function() {
|
var me = this,
|
xPadding = me.xPadding,
|
yPadding = me.yPadding,
|
padding = { };
|
|
if (Ext.isNumber(xPadding)) {
|
padding.left = xPadding;
|
padding.right = xPadding;
|
} else if (Ext.isObject(xPadding)) {
|
padding.left = xPadding.left;
|
padding.right = xPadding.right;
|
} else {
|
padding.left = 0;
|
padding.right = 0;
|
}
|
padding.width = padding.left + padding.right;
|
|
if (Ext.isNumber(yPadding)) {
|
padding.bottom = yPadding;
|
padding.top = yPadding;
|
} else if (Ext.isObject(yPadding)) {
|
padding.bottom = yPadding.bottom;
|
padding.top = yPadding.top;
|
} else {
|
padding.bottom = 0;
|
padding.top = 0;
|
}
|
padding.height = padding.bottom + padding.top;
|
|
return padding;
|
},
|
|
<span id='Ext-chart-series-Bar-method-getBarGirth'> // @private returns the bar girth.
|
</span> getBarGirth: function() {
|
var me = this,
|
store = me.chart.getChartStore(),
|
column = me.column,
|
ln = store.getCount(),
|
gutter = me.gutter / 100,
|
padding,
|
property;
|
|
if (me.style && me.style.width) {
|
return me.style.width;
|
}
|
padding = me.getPadding();
|
property = (column ? 'width' : 'height');
|
return (me.chart.chartBBox[property] - padding[property]) / (ln * (gutter + 1) - gutter);
|
},
|
|
<span id='Ext-chart-series-Bar-method-getGutters'> // @private returns the gutters.
|
</span> getGutters: function() {
|
var me = this,
|
column = me.column,
|
padding = me.getPadding(),
|
halfBarGirth = me.getBarGirth() / 2,
|
lowerGutter = Math.ceil((column ? padding.left : padding.bottom) + halfBarGirth),
|
upperGutter = Math.ceil((column ? padding.right : padding.top) + halfBarGirth);
|
|
return {
|
lower: lowerGutter,
|
upper: upperGutter,
|
verticalAxis: !column
|
};
|
},
|
|
<span id='Ext-chart-series-Bar-method-getBounds'> // @private Get chart and data boundaries
|
</span> getBounds: function() {
|
var me = this,
|
chart = me.chart,
|
store = chart.getChartStore(),
|
data = store.data.items,
|
i, ln, record,
|
bars = [].concat(me.yField),
|
barsLoc,
|
barsLen = bars.length,
|
groupBarsLen = barsLen,
|
groupGutter = me.groupGutter / 100,
|
column = me.column,
|
padding = me.getPadding(),
|
stacked = me.stacked,
|
barWidth = me.getBarGirth(),
|
barWidthProperty = column ? 'width' : 'height',
|
math = Math,
|
mmin = math.min,
|
mmax = math.max,
|
mabs = math.abs,
|
boundAxes = me.getAxesForXAndYFields(),
|
boundYAxis = boundAxes.yAxis,
|
minX, maxX, colsScale, colsZero, gutters,
|
ends, shrunkBarWidth, groupBarWidth, bbox, minY, maxY, axis, out,
|
scale, zero, total, rec, j, plus, minus;
|
|
me.setBBox(true);
|
bbox = me.bbox;
|
|
//Skip excluded series
|
if (me.__excludes) {
|
for (j = 0, total = me.__excludes.length; j < total; j++) {
|
if (me.__excludes[j]) {
|
groupBarsLen--;
|
}
|
}
|
}
|
axis = chart.axes.get(boundYAxis);
|
if (axis) {
|
ends = axis.applyData();
|
minY = ends.from;
|
maxY = ends.to;
|
}
|
|
if (me.yField && !Ext.isNumber(minY)) {
|
out = me.getMinMaxYValues();
|
minY = out[0];
|
maxY = out[1];
|
}
|
|
if (!Ext.isNumber(minY)) {
|
minY = 0;
|
}
|
if (!Ext.isNumber(maxY)) {
|
maxY = 0;
|
}
|
scale = (column ? bbox.height - padding.height : bbox.width - padding.width) / (maxY - minY);
|
shrunkBarWidth = barWidth;
|
groupBarWidth = (barWidth / ((stacked ? 1 : groupBarsLen) * (groupGutter + 1) - groupGutter));
|
|
if (barWidthProperty in me.style) {
|
groupBarWidth = mmin(groupBarWidth, me.style[barWidthProperty]);
|
shrunkBarWidth = groupBarWidth * ((stacked ? 1 : groupBarsLen) * (groupGutter + 1) - groupGutter);
|
}
|
zero = (column) ? bbox.y + bbox.height - padding.bottom : bbox.x + padding.left;
|
|
if (stacked) {
|
total = [[], []];
|
for (i = 0, ln = data.length; i < ln; i++) {
|
record = data[i];
|
total[0][i] = total[0][i] || 0;
|
total[1][i] = total[1][i] || 0;
|
for (j = 0; j < barsLen; j++) {
|
if (me.__excludes && me.__excludes[j]) {
|
continue;
|
}
|
rec = record.get(bars[j]);
|
total[+(rec > 0)][i] += mabs(rec);
|
}
|
}
|
total[+(maxY > 0)].push(mabs(maxY));
|
total[+(minY > 0)].push(mabs(minY));
|
minus = mmax.apply(math, total[0]);
|
plus = mmax.apply(math, total[1]);
|
scale = (column ? bbox.height - padding.height : bbox.width - padding.width) / (plus + minus);
|
zero = zero + minus * scale * (column ? -1 : 1);
|
}
|
else if (minY / maxY < 0) {
|
zero = zero - minY * scale * (column ? -1 : 1);
|
}
|
|
// If the columns are bound to the x-axis, calculate their positions
|
if (me.boundColumn) {
|
axis = chart.axes.get(boundAxes.xAxis);
|
if (axis) {
|
ends = axis.applyData();
|
minX = ends.from;
|
maxX = ends.to;
|
}
|
if (me.xField && !Ext.isNumber(minX)) {
|
out = me.getMinMaxYValues();
|
minX = out[0];
|
maxX = out[1];
|
}
|
if (!Ext.isNumber(minX)) {
|
minX = 0;
|
}
|
if (!Ext.isNumber(maxX)) {
|
maxX = 0;
|
}
|
gutters = me.getGutters();
|
colsScale = (bbox.width - (gutters.lower + gutters.upper)) / ((maxX - minX) || 1);
|
|
colsZero = bbox.x + gutters.lower;
|
|
barsLoc = [];
|
for (i = 0, ln = data.length; i < ln; i++) {
|
record = data[i];
|
rec = record.get(me.xField);
|
barsLoc[i] = colsZero + (rec - minX) * colsScale - (groupBarWidth / 2);
|
}
|
}
|
|
return {
|
bars: bars,
|
barsLoc: barsLoc,
|
bbox: bbox,
|
shrunkBarWidth: shrunkBarWidth,
|
barsLen: barsLen,
|
groupBarsLen: groupBarsLen,
|
barWidth: barWidth,
|
groupBarWidth: groupBarWidth,
|
scale: scale,
|
zero: zero,
|
padding: padding,
|
signed: minY / maxY < 0,
|
minY: minY,
|
maxY: maxY
|
};
|
},
|
|
<span id='Ext-chart-series-Bar-method-getPaths'> // @private Build an array of paths for the chart
|
</span> getPaths: function() {
|
var me = this,
|
chart = me.chart,
|
store = chart.getChartStore(),
|
data = store.data.items,
|
i, total, record,
|
bounds = me.bounds = me.getBounds(),
|
items = me.items = [],
|
yFields = Ext.isArray(me.yField) ? me.yField : [me.yField],
|
gutter = me.gutter / 100,
|
groupGutter = me.groupGutter / 100,
|
animate = chart.animate,
|
column = me.column,
|
group = me.group,
|
enableShadows = chart.shadow,
|
shadowGroups = me.shadowGroups,
|
shadowAttributes = me.shadowAttributes,
|
shadowGroupsLn = shadowGroups.length,
|
bbox = bounds.bbox,
|
barWidth = bounds.barWidth,
|
shrunkBarWidth = bounds.shrunkBarWidth,
|
padding = me.getPadding(),
|
stacked = me.stacked,
|
barsLen = bounds.barsLen,
|
colors = me.colorArrayStyle,
|
colorLength = colors && colors.length || 0,
|
themeIndex = me.themeIdx,
|
math = Math,
|
mmax = math.max,
|
mmin = math.min,
|
mabs = math.abs,
|
j, yValue, height, totalDim, totalNegDim, bottom, top, hasShadow, barAttr, attrs, counter,
|
totalPositiveValues, totalNegativeValues,
|
shadowIndex, shadow, sprite, offset, floorY, idx;
|
|
for (i = 0, total = data.length; i < total; i++) {
|
record = data[i];
|
bottom = bounds.zero;
|
top = bounds.zero;
|
totalDim = 0;
|
totalNegDim = 0;
|
totalPositiveValues = totalNegativeValues = 0;
|
hasShadow = false;
|
for (j = 0, counter = 0; j < barsLen; j++) {
|
// Excluded series
|
if (me.__excludes && me.__excludes[j]) {
|
continue;
|
}
|
yValue = record.get(bounds.bars[j]);
|
if (yValue >= 0) {
|
totalPositiveValues += yValue;
|
}
|
else {
|
totalNegativeValues += yValue;
|
}
|
height = Math.round((yValue - mmax(bounds.minY, 0)) * bounds.scale);
|
idx = themeIndex + (barsLen > 1 ? j : 0);
|
barAttr = {
|
fill: colors[idx % colorLength]
|
};
|
if (column) {
|
Ext.apply(barAttr, {
|
height: height,
|
width: mmax(bounds.groupBarWidth, 0),
|
x: (me.boundColumn ? bounds.barsLoc[i]
|
: (bbox.x + padding.left
|
+ (barWidth - shrunkBarWidth) * 0.5
|
+ i * barWidth * (1 + gutter)
|
+ counter * bounds.groupBarWidth * (1 + groupGutter) * !stacked)),
|
y: bottom - height
|
});
|
}
|
else {
|
// draw in reverse order
|
offset = (total - 1) - i;
|
Ext.apply(barAttr, {
|
height: mmax(bounds.groupBarWidth, 0),
|
width: height + (bottom == bounds.zero),
|
x: bottom + (bottom != bounds.zero),
|
y: (bbox.y + padding.top
|
+ (barWidth - shrunkBarWidth) * 0.5
|
+ offset * barWidth * (1 + gutter)
|
+ counter * bounds.groupBarWidth * (1 + groupGutter) * !stacked + 1)
|
});
|
}
|
if (height < 0) {
|
if (column) {
|
barAttr.y = top;
|
barAttr.height = mabs(height);
|
} else {
|
barAttr.x = top + height;
|
barAttr.width = mabs(height);
|
}
|
}
|
if (stacked) {
|
if (height < 0) {
|
top += height * (column ? -1 : 1);
|
} else {
|
bottom += height * (column ? -1 : 1);
|
}
|
totalDim += mabs(height);
|
if (height < 0) {
|
totalNegDim += mabs(height);
|
}
|
}
|
barAttr.x = Math.floor(barAttr.x) + 1;
|
floorY = Math.floor(barAttr.y);
|
if (Ext.isIE8m && barAttr.y > floorY) {
|
floorY--;
|
}
|
barAttr.y = floorY;
|
barAttr.width = Math.floor(barAttr.width);
|
barAttr.height = Math.floor(barAttr.height);
|
items.push({
|
series: me,
|
yField: yFields[j],
|
storeItem: record,
|
value: [record.get(me.xField), yValue],
|
attr: barAttr,
|
point: column ? [barAttr.x + barAttr.width / 2, yValue >= 0 ? barAttr.y : barAttr.y + barAttr.height] :
|
[yValue >= 0 ? barAttr.x + barAttr.width : barAttr.x, barAttr.y + barAttr.height / 2]
|
});
|
// When resizing, reset before animating
|
if (animate && chart.resizing) {
|
attrs = column ? {
|
x: barAttr.x,
|
y: bounds.zero,
|
width: barAttr.width,
|
height: 0
|
} : {
|
x: bounds.zero,
|
y: barAttr.y,
|
width: 0,
|
height: barAttr.height
|
};
|
if (enableShadows && (stacked && !hasShadow || !stacked)) {
|
hasShadow = true;
|
//update shadows
|
for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) {
|
shadow = shadowGroups[shadowIndex].getAt(stacked ? i : (i * barsLen + j));
|
if (shadow) {
|
shadow.setAttributes(attrs, true);
|
}
|
}
|
}
|
//update sprite position and width/height
|
sprite = group.getAt(i * barsLen + j);
|
if (sprite) {
|
sprite.setAttributes(attrs, true);
|
}
|
}
|
counter++;
|
}
|
if (stacked && items.length) {
|
items[i * counter].totalDim = totalDim;
|
items[i * counter].totalNegDim = totalNegDim;
|
items[i * counter].totalPositiveValues = totalPositiveValues;
|
items[i * counter].totalNegativeValues = totalNegativeValues;
|
}
|
}
|
if (stacked && counter == 0) {
|
// Remove ghost shadow ref: EXTJSIV-5982
|
for (i = 0, total = data.length; i < total; i++) {
|
for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) {
|
shadow = shadowGroups[shadowIndex].getAt(i);
|
if (shadow) {
|
shadow.hide(true);
|
}
|
}
|
}
|
}
|
},
|
|
<span id='Ext-chart-series-Bar-method-renderShadows'> // @private render/setAttributes on the shadows
|
</span> renderShadows: function(i, barAttr, baseAttrs, bounds) {
|
var me = this,
|
chart = me.chart,
|
surface = chart.surface,
|
animate = chart.animate,
|
stacked = me.stacked,
|
shadowGroups = me.shadowGroups,
|
shadowAttributes = me.shadowAttributes,
|
shadowGroupsLn = shadowGroups.length,
|
store = chart.getChartStore(),
|
column = me.column,
|
items = me.items,
|
shadows = [],
|
zero = bounds.zero,
|
shadowIndex, shadowBarAttr, shadow, totalDim, totalNegDim, j, rendererAttributes;
|
|
if ((stacked && (i % bounds.groupBarsLen === 0)) || !stacked) {
|
j = i / bounds.groupBarsLen;
|
//create shadows
|
for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) {
|
shadowBarAttr = Ext.apply({}, shadowAttributes[shadowIndex]);
|
shadow = shadowGroups[shadowIndex].getAt(stacked ? j : i);
|
Ext.copyTo(shadowBarAttr, barAttr, 'x,y,width,height');
|
if (!shadow) {
|
shadow = surface.add(Ext.apply({
|
type: 'rect',
|
group: shadowGroups[shadowIndex]
|
}, Ext.apply({}, baseAttrs, shadowBarAttr)));
|
}
|
if (stacked) {
|
totalDim = items[i].totalDim;
|
totalNegDim = items[i].totalNegDim;
|
if (column) {
|
shadowBarAttr.y = zero + totalNegDim - totalDim - 1;
|
shadowBarAttr.height = totalDim;
|
}
|
else {
|
shadowBarAttr.x = zero - totalNegDim;
|
shadowBarAttr.width = totalDim;
|
}
|
}
|
|
rendererAttributes = me.renderer(shadow, store.getAt(j), shadowBarAttr, i, store);
|
rendererAttributes.hidden = !!barAttr.hidden;
|
if (animate) {
|
me.onAnimate(shadow, { to: rendererAttributes });
|
}
|
else {
|
shadow.setAttributes(rendererAttributes, true);
|
}
|
shadows.push(shadow);
|
}
|
}
|
return shadows;
|
},
|
|
<span id='Ext-chart-series-Bar-method-drawSeries'> /**
|
</span> * Draws the series for the current chart.
|
*/
|
drawSeries: function() {
|
var me = this,
|
chart = me.chart,
|
store = chart.getChartStore(),
|
surface = chart.surface,
|
animate = chart.animate,
|
stacked = me.stacked,
|
column = me.column,
|
chartAxes = chart.axes,
|
boundAxes = me.getAxesForXAndYFields(),
|
boundXAxis = boundAxes.xAxis,
|
boundYAxis = boundAxes.yAxis,
|
enableShadows = chart.shadow,
|
shadowGroups = me.shadowGroups,
|
shadowGroupsLn = shadowGroups.length,
|
group = me.group,
|
seriesStyle = me.seriesStyle,
|
items, ln, i, j, baseAttrs, sprite, rendererAttributes, shadowIndex, shadowGroup,
|
bounds, endSeriesStyle, barAttr, attrs, anim;
|
|
if (!store || !store.getCount() || me.seriesIsHidden) {
|
me.hide();
|
me.items = [];
|
return;
|
}
|
|
//fill colors are taken from the colors array.
|
endSeriesStyle = Ext.apply({}, this.style, seriesStyle);
|
delete endSeriesStyle.fill;
|
delete endSeriesStyle.x;
|
delete endSeriesStyle.y;
|
delete endSeriesStyle.width;
|
delete endSeriesStyle.height;
|
|
me.unHighlightItem();
|
me.cleanHighlights();
|
|
me.boundColumn = (boundXAxis && Ext.Array.contains(me.axis,boundXAxis)
|
&& chartAxes.get(boundXAxis)
|
&& chartAxes.get(boundXAxis).isNumericAxis);
|
|
me.getPaths();
|
bounds = me.bounds;
|
items = me.items;
|
|
baseAttrs = column ? {
|
y: bounds.zero,
|
height: 0
|
} : {
|
x: bounds.zero,
|
width: 0
|
};
|
ln = items.length;
|
|
// Create new or reuse sprites and animate/display
|
for (i = 0; i < ln; i++) {
|
sprite = group.getAt(i);
|
barAttr = items[i].attr;
|
|
if (enableShadows) {
|
items[i].shadows = me.renderShadows(i, barAttr, baseAttrs, bounds);
|
}
|
|
// Create a new sprite if needed (no height)
|
if (!sprite) {
|
attrs = Ext.apply({}, baseAttrs, barAttr);
|
attrs = Ext.apply(attrs, endSeriesStyle || {});
|
sprite = surface.add(Ext.apply({}, {
|
type: 'rect',
|
group: group
|
}, attrs));
|
}
|
if (animate) {
|
rendererAttributes = me.renderer(sprite, store.getAt(i), barAttr, i, store);
|
sprite._to = rendererAttributes;
|
anim = me.onAnimate(sprite, { to: Ext.apply(rendererAttributes, endSeriesStyle) });
|
if (enableShadows && stacked && (i % bounds.barsLen === 0)) {
|
j = i / bounds.barsLen;
|
for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) {
|
anim.on('afteranimate', function() {
|
this.show(true);
|
}, shadowGroups[shadowIndex].getAt(j));
|
}
|
}
|
}
|
else {
|
rendererAttributes = me.renderer(sprite, store.getAt(i), Ext.apply(barAttr, { hidden: false }), i, store);
|
sprite.setAttributes(Ext.apply(rendererAttributes, endSeriesStyle), true);
|
}
|
items[i].sprite = sprite;
|
}
|
|
// Hide unused sprites
|
ln = group.getCount();
|
for (j = i; j < ln; j++) {
|
group.getAt(j).hide(true);
|
}
|
|
if (me.stacked) {
|
// If stacked, we have only store.getCount() shadows.
|
i = store.getCount();
|
}
|
|
// Hide unused shadows
|
if (enableShadows) {
|
for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) {
|
shadowGroup = shadowGroups[shadowIndex];
|
ln = shadowGroup.getCount();
|
for (j = i; j < ln; j++) {
|
shadowGroup.getAt(j).hide(true);
|
}
|
}
|
}
|
me.renderLabels();
|
},
|
|
<span id='Ext-chart-series-Bar-method-onCreateLabel'> // @private called when a label is to be created.
|
</span> onCreateLabel: function(storeItem, item, i, display) {
|
var me = this,
|
surface = me.chart.surface,
|
group = me.labelsGroup,
|
config = me.label,
|
endLabelStyle = Ext.apply({}, config, me.seriesLabelStyle || {}),
|
sprite;
|
|
return surface.add(Ext.apply({
|
type: 'text',
|
group: group
|
}, endLabelStyle || {}));
|
},
|
|
<span id='Ext-chart-series-Bar-method-onPlaceLabel'> // @private called when a label is to be positioned.
|
</span> onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {
|
// Determine the label's final position. Starts with the configured preferred value but
|
// may get flipped from inside to outside or vice-versa depending on space.
|
var me = this,
|
opt = me.bounds,
|
groupBarWidth = opt.groupBarWidth,
|
column = me.column,
|
chart = me.chart,
|
chartBBox = chart.chartBBox,
|
resizing = chart.resizing,
|
xValue = item.value[0],
|
yValue = item.value[1],
|
attr = item.attr,
|
config = me.label,
|
stacked = me.stacked,
|
stackedDisplay = config.stackedDisplay,
|
rotate = (config.orientation == 'vertical'),
|
field = [].concat(config.field),
|
format = config.renderer,
|
text, size, width, height,
|
zero = opt.zero,
|
insideStart = 'insideStart',
|
insideEnd = 'insideEnd',
|
outside = 'outside',
|
over = 'over',
|
under = 'under',
|
labelMarginX = 4, // leave space around the labels (important when saving chart as image)
|
labelMarginY = 2,
|
signed = opt.signed,
|
x, y, finalAttr;
|
|
if (display == insideStart || display == insideEnd || display == outside) {
|
if (stacked && (display == outside)) {
|
// It doesn't make sense to use 'outside' on a stacked chart
|
// unless we only want to display the 'stackedDisplay' labels.
|
label.hide(true);
|
return;
|
}
|
label.setAttributes({
|
// Reset the style in case the label is being reused (for instance, if a series is excluded)
|
// and do it before calling the renderer function.
|
style: undefined
|
});
|
text = (Ext.isNumber(index) ? format(storeItem.get(field[index]), label, storeItem, item, i, display, animate, index) : '');
|
label.setAttributes({
|
// Set the text onto the label.
|
text: text
|
});
|
size = me.getLabelSize(text, label.attr.style);
|
width = size.width;
|
height = size.height;
|
if (column) {
|
//-----------------------------------------
|
// Position the label within a column chart
|
|
// If there is no label to display, or if the corresponding box in a stacked column
|
// isn't tall enough to display the label, then leave.
|
if (!width || !height || (stacked && (attr.height < height))) {
|
label.hide(true);
|
return;
|
}
|
|
// Align horizontally the label in the middle of the column
|
x = attr.x + (rotate ? groupBarWidth/2 : (groupBarWidth - width)/2);
|
|
// If the label is to be displayed outside, make sure there is room for it, otherwise display it inside.
|
if (display == outside) {
|
var free = (yValue >= 0 ? (attr.y - chartBBox.y) : (chartBBox.y + chartBBox.height - attr.y - attr.height));
|
if (free < height + labelMarginY) {
|
display = insideEnd;
|
}
|
}
|
|
// If the label is to be displayed inside a non-stacked chart, make sure it is
|
// not taller than the box, otherwise move it outside.
|
if (!stacked && (display != outside)) {
|
if (height + labelMarginY > attr.height) {
|
display = outside;
|
}
|
}
|
|
// Place the label vertically depending on its config and on whether the value
|
// it represents is positive (above the X-axis) or negative (below the X-axis)
|
if (!y) {
|
y = attr.y;
|
if (yValue >= 0) {
|
switch (display) {
|
case insideStart: y += attr.height + (rotate ? -labelMarginY : -height/2); break;
|
case insideEnd: y += (rotate ? height + labelMarginX : height/2); break;
|
case outside: y += (rotate ? -labelMarginY : -height/2); break;
|
}
|
} else {
|
switch (display) {
|
case insideStart: y += (rotate ? height + labelMarginY : height/2); break;
|
case insideEnd: y += (rotate ? attr.height - labelMarginY : attr.height - height/2); break;
|
case outside: y += (rotate ? attr.height + height + labelMarginY : attr.height + height/2); break;
|
}
|
}
|
}
|
}
|
else {
|
//-----------------------------------------
|
// Position the label within a bar chart
|
|
// If there is no label to display, or if the corresponding box has no width, then leave.
|
if (!width || !height || (stacked && !attr.width)) {
|
label.hide(true);
|
return;
|
}
|
|
// Align vertically the label in the middle of the bar
|
y = attr.y + (rotate ? (groupBarWidth + height)/2 : groupBarWidth/2);
|
|
// If the label is to be displayed outside, make sure there is room for it otherwise display it inside.
|
if (display == outside) {
|
var free = (yValue >= 0 ? (chartBBox.x + chartBBox.width - attr.x - attr.width) : (attr.x - chartBBox.x));
|
if (free < width + labelMarginX) {
|
display = insideEnd;
|
}
|
}
|
|
// If the label is to be displayed inside (and it is not rotated yet), make sure it is
|
// not wider than the box it represents otherwise (for a stacked chart) rotate it vertically
|
// and center it, or (for a non-stacked chart) move it outside.
|
if ((display != outside) && !rotate) {
|
if (width + labelMarginX > attr.width) {
|
if (stacked) {
|
if (height > attr.width) {
|
label.hide(true);
|
return; // Even rotated, there isn't enough room.
|
}
|
x = attr.x + attr.width/2;
|
y = attr.y + attr.height - (attr.height - width)/2;
|
rotate = true;
|
} else {
|
display = outside;
|
}
|
}
|
}
|
|
// Place the label horizontally depending on its config and on whether the value
|
// it represents is positive (above the X-axis) or negative (below the X-axis)
|
if (!x) {
|
x = attr.x;
|
if (yValue >= 0) {
|
switch (display) {
|
case insideStart: x += (rotate ? width/2 : labelMarginX); break;
|
case insideEnd: x += attr.width + (rotate ? -width/2 : -width - labelMarginX); break;
|
case outside: x += attr.width + (rotate ? width/2 : labelMarginX); break;
|
}
|
} else {
|
switch (display) {
|
case insideStart: x += attr.width + (rotate ? -width/2 : -width - labelMarginX); break;
|
case insideEnd: x += (rotate ? width/2 : labelMarginX); break;
|
case outside: x += (rotate ? -width/2 : -width - labelMarginX); break;
|
}
|
}
|
}
|
}
|
} else if (display == over || display == under) {
|
if (stacked && stackedDisplay) {
|
//-----------------------------------------
|
// Position the label on top or at the bottom of a stacked bar/column
|
|
text = label.attr.text;
|
label.setAttributes({
|
// The text is already set onto the label: we just need to set the style
|
// (but don't overwrite any custom style that might have been set by an app override).
|
style: Ext.applyIf((label.attr && label.attr.style) || {},
|
{
|
'font-weight':'bold',
|
'font-size':'14px'
|
}
|
)
|
});
|
|
size = me.getLabelSize(text, label.attr.style);
|
width = size.width;
|
height = size.height;
|
|
switch (display) {
|
case over:
|
if (column) {
|
x = attr.x + (rotate ? groupBarWidth/2 : (groupBarWidth - width)/2);
|
y = zero - (item.totalDim - item.totalNegDim) - height/2 - labelMarginY;
|
} else {
|
x = zero + (item.totalDim - item.totalNegDim) + labelMarginX;
|
y = attr.y + (rotate ? (groupBarWidth + height)/2 : groupBarWidth/2);
|
}
|
break;
|
case under:
|
if (column) {
|
x = attr.x + (rotate ? groupBarWidth/2 : (groupBarWidth - width)/2);
|
y = zero + item.totalNegDim + height/2;
|
} else {
|
x = zero - item.totalNegDim - width - labelMarginX;
|
y = attr.y + (rotate ? (groupBarWidth + height)/2 : groupBarWidth/2);
|
}
|
break;
|
}
|
}
|
}
|
|
if (x == undefined || y == undefined) {
|
// bad configuration: x/y are not set
|
label.hide(true);
|
return;
|
}
|
|
label.isOutside = (display == outside);
|
label.setAttributes({
|
text: text
|
});
|
|
//set position
|
finalAttr = {
|
x: x,
|
y: y
|
};
|
//rotate
|
if (rotate) {
|
finalAttr.rotate = {
|
x: x,
|
y: y,
|
degrees: 270
|
};
|
}
|
//check for resizing
|
if (animate && resizing) {
|
if (column) {
|
x = attr.x + attr.width / 2;
|
y = zero;
|
} else {
|
x = zero;
|
y = attr.y + attr.height / 2;
|
}
|
label.setAttributes({
|
x: x,
|
y: y
|
}, true);
|
if (rotate) {
|
label.setAttributes({
|
rotate: {
|
x: x,
|
y: y,
|
degrees: 270
|
}
|
}, true);
|
}
|
}
|
//handle animation
|
if (animate) {
|
me.onAnimate(label, { to: finalAttr });
|
}
|
else {
|
label.setAttributes(Ext.apply(finalAttr, {
|
hidden: false
|
}), true);
|
}
|
},
|
|
<span id='Ext-chart-series-Bar-method-getLabelSize'> /* @private
|
</span> * Gets the dimensions of a given bar label. Uses a single hidden sprite to avoid
|
* changing visible sprites.
|
* @param value
|
*/
|
getLabelSize: function(value, labelStyle) {
|
var tester = this.testerLabel,
|
config = this.label,
|
endLabelStyle = Ext.apply({}, config, labelStyle, this.seriesLabelStyle || {}),
|
rotated = config.orientation === 'vertical',
|
bbox, w, h,
|
undef;
|
if (!tester) {
|
tester = this.testerLabel = this.chart.surface.add(Ext.apply({
|
type: 'text',
|
opacity: 0
|
}, endLabelStyle));
|
}
|
tester.setAttributes({
|
style: labelStyle,
|
text: value
|
}, true);
|
|
// Flip the width/height if rotated, as getBBox returns the pre-rotated dimensions
|
bbox = tester.getBBox();
|
w = bbox.width;
|
h = bbox.height;
|
return {
|
width: rotated ? h : w,
|
height: rotated ? w : h
|
};
|
},
|
|
<span id='Ext-chart-series-Bar-method-onAnimate'> // @private used to animate label, markers and other sprites.
|
</span> onAnimate: function(sprite, attr) {
|
sprite.show();
|
return this.callParent(arguments);
|
},
|
|
<span id='Ext-chart-series-Bar-method-isItemInPoint'> isItemInPoint: function(x, y, item) {
|
</span> var bbox = item.sprite.getBBox();
|
return bbox.x <= x && bbox.y <= y
|
&& (bbox.x + bbox.width) >= x
|
&& (bbox.y + bbox.height) >= y;
|
},
|
|
<span id='Ext-chart-series-Bar-method-hideAll'> // @private hide all markers
|
</span> hideAll: function(index) {
|
var axes = this.chart.axes,
|
axesItems = axes.items,
|
ln = axesItems.length,
|
i = 0;
|
|
index = (isNaN(this._index) ? index : this._index) || 0;
|
|
if (!this.__excludes) {
|
this.__excludes = [];
|
}
|
|
this.__excludes[index] = true;
|
this.drawSeries();
|
|
for (i; i < ln; i++) {
|
axesItems[i].drawAxis();
|
}
|
},
|
|
<span id='Ext-chart-series-Bar-method-showAll'> // @private show all markers
|
</span> showAll: function(index) {
|
var axes = this.chart.axes,
|
axesItems = axes.items,
|
ln = axesItems.length,
|
i = 0;
|
|
index = (isNaN(this._index) ? index : this._index) || 0;
|
|
if (!this.__excludes) {
|
this.__excludes = [];
|
}
|
|
this.__excludes[index] = false;
|
this.drawSeries();
|
|
for (i; i < ln; i++) {
|
axesItems[i].drawAxis();
|
}
|
},
|
|
<span id='Ext-chart-series-Bar-method-getLegendColor'> /**
|
</span> * Returns a string with the color to be used for the series legend item.
|
* @param index
|
*/
|
getLegendColor: function(index) {
|
var me = this,
|
colorLength = me.colorArrayStyle.length;
|
|
if (me.style && me.style.fill) {
|
return me.style.fill;
|
} else {
|
return me.colorArrayStyle[index % colorLength];
|
}
|
},
|
|
<span id='Ext-chart-series-Bar-method-highlightItem'> highlightItem: function(item) {
|
</span> this.callParent(arguments);
|
this.renderLabels();
|
},
|
|
<span id='Ext-chart-series-Bar-method-unHighlightItem'> unHighlightItem: function() {
|
</span> this.callParent(arguments);
|
this.renderLabels();
|
},
|
|
<span id='Ext-chart-series-Bar-method-cleanHighlights'> cleanHighlights: function() {
|
</span> this.callParent(arguments);
|
this.renderLabels();
|
}
|
});
|
</pre>
|
</body>
|
</html>
|