3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @class Ext.chart.series.Series
18 * Series is the abstract class containing the common logic to all chart series. Series includes
19 * methods from Labels, Highlights, Tips and Callouts mixins. This class implements the logic of handling
20 * mouse events, animating, hiding, showing all elements and returning the color of the series to be used as a legend item.
24 * The series class supports listeners via the Observable syntax. Some of these listeners are:
26 * - `itemmouseup` When the user interacts with a marker.
27 * - `itemmousedown` When the user interacts with a marker.
28 * - `itemmousemove` When the user iteracts with a marker.
29 * - `afterrender` Will be triggered when the animation ends or when the series has been rendered completely.
37 * 'afterrender': function() {
38 * console('afterrender');
45 Ext.define('Ext.chart.series.Series', {
47 /* Begin Definitions */
50 observable: 'Ext.util.Observable',
51 labels: 'Ext.chart.Label',
52 highlights: 'Ext.chart.Highlight',
53 tips: 'Ext.chart.Tip',
54 callouts: 'Ext.chart.Callout'
60 * @cfg {Boolean/Object} highlight
61 * If set to `true` it will highlight the markers or the series when hovering
62 * with the mouse. This parameter can also be an object with the same style
63 * properties you would apply to a {@link Ext.draw.Sprite} to apply custom
64 * styles to markers and series.
69 * Add tooltips to the visualization's markers. The options for the tips are the
70 * same configuration used with {@link Ext.tip.ToolTip}. For example:
76 * renderer: function(storeItem, item) {
77 * this.setTitle(storeItem.get('name') + ': ' + storeItem.get('data1') + ' views');
84 * The type of series. Set in subclasses.
90 * The human-readable name of the series.
95 * @cfg {Boolean} showInLegend
96 * Whether to show this series in the legend.
101 * @cfg {Function} renderer
102 * A function that can be overridden to set custom styling properties to each rendered element.
103 * Passes in (sprite, record, attributes, index, store) to the function.
105 renderer: function(sprite, record, attributes, index, store) {
110 * @cfg {Array} shadowAttributes
111 * An array with shadow attributes
113 shadowAttributes: null,
115 //@private triggerdrawlistener flag
116 triggerAfterDraw: false,
119 * @cfg {Object} listeners
120 * An (optional) object with event callbacks. All event callbacks get the target *item* as first parameter. The callback functions are:
128 constructor: function(config) {
131 Ext.apply(me, config);
134 me.shadowGroups = [];
136 me.mixins.labels.constructor.call(me, config);
137 me.mixins.highlights.constructor.call(me, config);
138 me.mixins.tips.constructor.call(me, config);
139 me.mixins.callouts.constructor.call(me, config);
152 * Fires when the series title is changed via {@link #setTitle}.
153 * @param {String} title The new title value
154 * @param {Number} index The index in the collection of titles
159 me.mixins.observable.constructor.call(me, config);
163 itemmouseover: me.onItemMouseOver,
164 itemmouseout: me.onItemMouseOut,
165 mouseleave: me.onMouseLeave
170 * Iterate over each of the records for this series. The default implementation simply iterates
171 * through the entire data store, but individual series implementations can override this to
172 * provide custom handling, e.g. adding/removing records.
173 * @param {Function} fn The function to execute for each record.
174 * @param {Object} scope Scope for the fn.
176 eachRecord: function(fn, scope) {
177 var chart = this.chart;
178 (chart.substore || chart.store).each(fn, scope);
182 * Return the number of records being displayed in this series. Defaults to the number of
183 * records in the store; individual series implementations can override to provide custom handling.
185 getRecordCount: function() {
186 var chart = this.chart,
187 store = chart.substore || chart.store;
188 return store ? store.getCount() : 0;
192 * Determines whether the series item at the given index has been excluded, i.e. toggled off in the legend.
195 isExcluded: function(index) {
196 var excludes = this.__excludes;
197 return !!(excludes && excludes[index]);
200 // @private set the bbox and clipBox for the series
201 setBBox: function(noGutter) {
204 chartBBox = chart.chartBBox,
205 gutterX = noGutter ? 0 : chart.maxGutter[0],
206 gutterY = noGutter ? 0 : chart.maxGutter[1],
212 width: chartBBox.width,
213 height: chartBBox.height
215 me.clipBox = clipBox;
218 x: (clipBox.x + gutterX) - (chart.zoom.x * chart.zoom.width),
219 y: (clipBox.y + gutterY) - (chart.zoom.y * chart.zoom.height),
220 width: (clipBox.width - (gutterX * 2)) * chart.zoom.width,
221 height: (clipBox.height - (gutterY * 2)) * chart.zoom.height
226 // @private set the animation for the sprite
227 onAnimate: function(sprite, attr) {
229 sprite.stopAnimation();
230 if (me.triggerAfterDraw) {
231 return sprite.animate(Ext.applyIf(attr, me.chart.animate));
233 me.triggerAfterDraw = true;
234 return sprite.animate(Ext.apply(Ext.applyIf(attr, me.chart.animate), {
236 'afteranimate': function() {
237 me.triggerAfterDraw = false;
238 me.fireEvent('afterrender');
245 // @private return the gutter.
246 getGutters: function() {
250 // @private wrapper for the itemmouseover event.
251 onItemMouseOver: function(item) {
253 if (item.series === me) {
255 me.highlightItem(item);
263 // @private wrapper for the itemmouseout event.
264 onItemMouseOut: function(item) {
266 if (item.series === me) {
267 me.unHighlightItem();
274 // @private wrapper for the mouseleave event.
275 onMouseLeave: function() {
277 me.unHighlightItem();
284 * For a given x/y point relative to the Surface, find a corresponding item from this
288 * @return {Object} An object describing the item, or null if there is no matching item.
289 * The exact contents of this object will vary by series type, but should always contain the following:
290 * @return {Ext.chart.series.Series} return.series the Series object to which the item belongs
291 * @return {Object} return.value the value(s) of the item's data point
292 * @return {Array} return.point the x/y coordinates relative to the chart box of a single point
293 * for this data item, which can be used as e.g. a tooltip anchor point.
294 * @return {Ext.draw.Sprite} return.sprite the item's rendering Sprite.
296 getItemForPoint: function(x, y) {
297 //if there are no items to query just return null.
298 if (!this.items || !this.items.length || this.seriesIsHidden) {
306 if (!Ext.draw.Draw.withinBox(x, y, bbox)) {
309 for (i = 0, ln = items.length; i < ln; i++) {
310 if (items[i] && this.isItemInPoint(x, y, items[i], i)) {
318 isItemInPoint: function(x, y, item, i) {
323 * Hides all the elements in the series.
325 hideAll: function() {
328 item, len, i, j, l, sprite, shadows;
330 me.seriesIsHidden = true;
331 me._prevShowMarkers = me.showMarkers;
333 me.showMarkers = false;
337 for (i = 0, len = items.length; i < len; i++) {
339 sprite = item.sprite;
341 sprite.setAttributes({
346 if (sprite && sprite.shadows) {
347 shadows = sprite.shadows;
348 for (j = 0, l = shadows.length; j < l; ++j) {
349 shadows[j].setAttributes({
358 * Shows all the elements in the series.
360 showAll: function() {
362 prevAnimate = me.chart.animate;
363 me.chart.animate = false;
364 me.seriesIsHidden = false;
365 me.showMarkers = me._prevShowMarkers;
367 me.chart.animate = prevAnimate;
371 * Returns a string with the color to be used for the series legend item.
373 getLegendColor: function(index) {
374 var me = this, fill, stroke;
375 if (me.seriesStyle) {
376 fill = me.seriesStyle.fill;
377 stroke = me.seriesStyle.stroke;
378 if (fill && fill != 'none') {
387 * Checks whether the data field should be visible in the legend
389 * @param {Number} index The index of the current item
391 visibleInLegend: function(index){
392 var excludes = this.__excludes;
394 return !excludes[index];
396 return !this.seriesIsHidden;
400 * Changes the value of the {@link #title} for the series.
401 * Arguments can take two forms:
403 * <li>A single String value: this will be used as the new single title for the series (applies
404 * to series with only one yField)</li>
405 * <li>A numeric index and a String value: this will set the title for a single indexed yField.</li>
407 * @param {Number} index
408 * @param {String} title
410 setTitle: function(index, title) {
414 if (Ext.isString(index)) {
419 if (Ext.isArray(oldTitle)) {
420 oldTitle[index] = title;
425 me.fireEvent('titlechange', title, index);