/**
* @class Ext.chart.LegendItem
* A single item of a legend (marker plus label)
*/
Ext.define('Ext.chart.LegendItem', {
/* Begin Definitions */
extend: 'Ext.draw.CompositeSprite',
requires: ['Ext.chart.Shape'],
/* End Definitions */
// Controls Series visibility
hiddenSeries: false,
// These are cached for quick lookups
label: undefined,
// Position of the item, relative to the upper-left corner of the legend box
x: 0,
y: 0,
zIndex: 500,
// checks to make sure that a unit size follows the bold keyword in the font style value
boldRe: /bold\s\d{1,}.*/i,
constructor: function(config) {
this.callParent(arguments);
this.createLegend(config);
},
/**
* Creates all the individual sprites for this legend item
*/
createLegend: function(config) {
var me = this,
series = me.series,
index = config.yFieldIndex;
me.label = me.createLabel(config);
me.createSeriesMarkers(config);
me.setAttributes({
hidden: false
}, true);
me.yFieldIndex = index;
// Add event listeners
me.on('mouseover', me.onMouseOver, me);
me.on('mouseout', me.onMouseOut, me);
me.on('mousedown', me.onMouseDown, me);
if (!series.visibleInLegend(index)) {
me.hiddenSeries = true;
me.label.setAttributes({
opacity: 0.5
}, true);
};
// Relative to 0,0 at first so that the bbox is calculated correctly
me.updatePosition({ x: 0, y: 0 });
},
/**
* @private Retrieves text to be displayed as item label.
*/
getLabelText: function() {
var me = this,
series = me.series,
idx = me.yFieldIndex;
function getSeriesProp(name) {
var val = series[name];
return (Ext.isArray(val) ? val[idx] : val);
}
return getSeriesProp('title') || getSeriesProp('yField');
},
/**
* @private Creates label sprite.
*/
createLabel: function(config) {
var me = this,
legend = me.legend;
return me.add('label', me.surface.add({
type: 'text',
x: 20,
y: 0,
zIndex: (me.zIndex || 0) + 2,
fill: legend.labelColor,
font: legend.labelFont,
text: me.getLabelText(),
style: {
cursor: 'pointer'
}
}));
},
/**
* @private Creates Series marker Sprites.
*/
createSeriesMarkers: function(config) {
var me = this,
index = config.yFieldIndex,
series = me.series,
seriesType = series.type,
surface = me.surface,
z = me.zIndex;
// Line series - display as short line with optional marker in the middle
if (seriesType === 'line' || seriesType === 'scatter') {
if(seriesType === 'line') {
var seriesStyle = Ext.apply(series.seriesStyle, series.style);
me.drawLine(0.5, 0.5, 16.5, 0.5, z, seriesStyle, index);
};
if (series.showMarkers || seriesType === 'scatter') {
var markerConfig = Ext.apply(series.markerStyle, series.markerConfig || {}, {
fill: series.getLegendColor(index)
});
me.drawMarker(8.5, 0.5, z, markerConfig);
}
}
// All other series types - display as filled box
else {
me.drawFilledBox(12, 12, z, index);
}
},
/**
* @private Creates line sprite for Line series.
*/
drawLine: function(fromX, fromY, toX, toY, z, seriesStyle, index) {
var me = this,
surface = me.surface,
series = me.series;
return me.add('line', surface.add({
type: 'path',
path: 'M' + fromX + ',' + fromY + 'L' + toX + ',' + toY,
zIndex: (z || 0) + 2,
"stroke-width": series.lineWidth,
"stroke-linejoin": "round",
"stroke-dasharray": series.dash,
stroke: seriesStyle.stroke || series.getLegendColor(index) || '#000',
style: {
cursor: 'pointer'
}
}));
},
/**
* @private Creates series-shaped marker for Line and Scatter series.
*/
drawMarker: function(x, y, z, markerConfig) {
var me = this,
surface = me.surface,
series = me.series;
return me.add('marker', Ext.chart.Shape[markerConfig.type](surface, {
fill: markerConfig.fill,
x: x,
y: y,
zIndex: (z || 0) + 2,
radius: markerConfig.radius || markerConfig.size,
style: {
cursor: 'pointer'
}
}));
},
/**
* @private Creates box-shaped marker for all series but Line and Scatter.
*/
drawFilledBox: function(width, height, z, index) {
var me = this,
surface = me.surface,
series = me.series;
return me.add('box', surface.add({
type: 'rect',
zIndex: (z || 0) + 2,
x: 0,
y: 0,
width: width,
height: height,
fill: series.getLegendColor(index),
style: {
cursor: 'pointer'
}
}));
},
/**
* @private Draws label in bold when mouse cursor is over the item.
*/
onMouseOver: function() {
var me = this;
me.label.setStyle({
'font-weight': 'bold'
});
me.series._index = me.yFieldIndex;
me.series.highlightItem();
},
/**
* @private Draws label in normal when mouse cursor leaves the item.
*/
onMouseOut: function() {
var me = this,
legend = me.legend,
boldRe = me.boldRe;
me.label.setStyle({
'font-weight': legend.labelFont && boldRe.test(legend.labelFont) ? 'bold' : 'normal'
});
me.series._index = me.yFieldIndex;
me.series.unHighlightItem();
},
/**
* @private Toggles Series visibility upon mouse click on the item.
*/
onMouseDown: function() {
var me = this,
index = me.yFieldIndex;
if (!me.hiddenSeries) {
me.series.hideAll(index);
me.label.setAttributes({
opacity: 0.5
}, true);
} else {
me.series.showAll(index);
me.label.setAttributes({
opacity: 1
}, true);
}
me.hiddenSeries = !me.hiddenSeries;
me.legend.chart.redraw();
},
/**
* Update the positions of all this item's sprites to match the root position
* of the legend box.
* @param {Object} relativeTo (optional) If specified, this object's 'x' and 'y' values will be used
* as the reference point for the relative positioning. Defaults to the Legend.
*/
updatePosition: function(relativeTo) {
var me = this,
items = me.items,
ln = items.length,
i = 0,
item;
if (!relativeTo) {
relativeTo = me.legend;
}
for (; i < ln; i++) {
item = items[i];
switch (item.type) {
case 'text':
item.setAttributes({
x: 20 + relativeTo.x + me.x,
y: relativeTo.y + me.y
}, true);
break;
case 'rect':
item.setAttributes({
translate: {
x: relativeTo.x + me.x,
y: relativeTo.y + me.y - 6
}
}, true);
break;
default:
item.setAttributes({
translate: {
x: relativeTo.x + me.x,
y: relativeTo.y + me.y
}
}, true);
}
}
}
});