Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / docs / source / Chart.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5   <title>The source code</title>
6   <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../prettify/prettify.js"></script>
8   <style type="text/css">
9     .highlight { display: block; background-color: #ddd; }
10   </style>
11   <script type="text/javascript">
12     function highlight() {
13       document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14     }
15   </script>
16 </head>
17 <body onload="prettyPrint(); highlight();">
18   <pre class="prettyprint lang-js"><span id='Ext-chart-Chart'>/**
19 </span> * @class Ext.chart.Chart
20  * @extends Ext.draw.Component
21  *
22  * The Ext.chart package provides the capability to visualize data.
23  * Each chart binds directly to an Ext.data.Store enabling automatic updates of the chart.
24  * A chart configuration object has some overall styling options as well as an array of axes
25  * and series. A chart instance example could look like:
26  *
27   &lt;pre&gt;&lt;code&gt;
28     Ext.create('Ext.chart.Chart', {
29         renderTo: Ext.getBody(),
30         width: 800,
31         height: 600,
32         animate: true,
33         store: store1,
34         shadow: true,
35         theme: 'Category1',
36         legend: {
37             position: 'right'
38         },
39         axes: [ ...some axes options... ],
40         series: [ ...some series options... ]
41     });
42   &lt;/code&gt;&lt;/pre&gt;
43  *
44  * In this example we set the `width` and `height` of the chart, we decide whether our series are
45  * animated or not and we select a store to be bound to the chart. We also turn on shadows for all series,
46  * select a color theme `Category1` for coloring the series, set the legend to the right part of the chart and
47  * then tell the chart to render itself in the body element of the document. For more information about the axes and
48  * series configurations please check the documentation of each series (Line, Bar, Pie, etc).
49  */
50 Ext.define('Ext.chart.Chart', {
51
52     /* Begin Definitions */
53
54     alias: 'widget.chart',
55
56     extend: 'Ext.draw.Component',
57     
58     mixins: {
59         themeManager: 'Ext.chart.theme.Theme',
60         mask: 'Ext.chart.Mask',
61         navigation: 'Ext.chart.Navigation'
62     },
63
64     requires: [
65         'Ext.util.MixedCollection',
66         'Ext.data.StoreManager',
67         'Ext.chart.Legend',
68         'Ext.util.DelayedTask'
69     ],
70
71     /* End Definitions */
72
73     // @private
74     viewBox: false,
75
76 <span id='Ext-chart-Chart-cfg-theme'>    /**
77 </span>     * @cfg {String} theme (optional) The name of the theme to be used. A theme defines the colors and
78      * other visual displays of tick marks on axis, text, title text, line colors, marker colors and styles, etc.
79      * Possible theme values are 'Base', 'Green', 'Sky', 'Red', 'Purple', 'Blue', 'Yellow' and also six category themes
80      * 'Category1' to 'Category6'. Default value is 'Base'.
81      */
82
83 <span id='Ext-chart-Chart-cfg-animate'>    /**
84 </span>     * @cfg {Boolean/Object} animate (optional) true for the default animation (easing: 'ease' and duration: 500)
85      * or a standard animation config object to be used for default chart animations. Defaults to false.
86      */
87     animate: false,
88
89 <span id='Ext-chart-Chart-cfg-legend'>    /**
90 </span>     * @cfg {Boolean/Object} legend (optional) true for the default legend display or a legend config object. Defaults to false.
91      */
92     legend: false,
93
94 <span id='Ext-chart-Chart-cfg-insetPadding'>    /**
95 </span>     * @cfg {integer} insetPadding (optional) Set the amount of inset padding in pixels for the chart. Defaults to 10.
96      */
97     insetPadding: 10,
98
99 <span id='Ext-chart-Chart-cfg-enginePriority'>    /**
100 </span>     * @cfg {Array} enginePriority
101      * Defines the priority order for which Surface implementation to use. The first
102      * one supported by the current environment will be used.
103      */
104     enginePriority: ['Svg', 'Vml'],
105
106 <span id='Ext-chart-Chart-cfg-background'>    /**
107 </span>     * @cfg {Object|Boolean} background (optional) Set the chart background. This can be a gradient object, image, or color.
108      * Defaults to false for no background.
109      *
110      * For example, if `background` were to be a color we could set the object as
111      *
112      &lt;pre&gt;&lt;code&gt;
113         background: {
114             //color string
115             fill: '#ccc'
116         }
117      &lt;/code&gt;&lt;/pre&gt;
118
119      You can specify an image by using:
120
121      &lt;pre&gt;&lt;code&gt;
122         background: {
123             image: 'http://path.to.image/'
124         }
125      &lt;/code&gt;&lt;/pre&gt;
126
127      Also you can specify a gradient by using the gradient object syntax:
128
129      &lt;pre&gt;&lt;code&gt;
130         background: {
131             gradient: {
132                 id: 'gradientId',
133                 angle: 45,
134                 stops: {
135                     0: {
136                         color: '#555'
137                     }
138                     100: {
139                         color: '#ddd'
140                     }
141                 }
142             }
143         }
144      &lt;/code&gt;&lt;/pre&gt;
145      */
146     background: false,
147
148 <span id='Ext-chart-Chart-cfg-gradients'>    /**
149 </span>     * @cfg {Array} gradients (optional) Define a set of gradients that can be used as `fill` property in sprites.
150      * The gradients array is an array of objects with the following properties:
151      *
152      * &lt;ul&gt;
153      * &lt;li&gt;&lt;strong&gt;id&lt;/strong&gt; - string - The unique name of the gradient.&lt;/li&gt;
154      * &lt;li&gt;&lt;strong&gt;angle&lt;/strong&gt; - number, optional - The angle of the gradient in degrees.&lt;/li&gt;
155      * &lt;li&gt;&lt;strong&gt;stops&lt;/strong&gt; - object - An object with numbers as keys (from 0 to 100) and style objects
156      * as values&lt;/li&gt;
157      * &lt;/ul&gt;
158      *
159
160      For example:
161
162      &lt;pre&gt;&lt;code&gt;
163         gradients: [{
164             id: 'gradientId',
165             angle: 45,
166             stops: {
167                 0: {
168                     color: '#555'
169                 },
170                 100: {
171                     color: '#ddd'
172                 }
173             }
174         },  {
175             id: 'gradientId2',
176             angle: 0,
177             stops: {
178                 0: {
179                     color: '#590'
180                 },
181                 20: {
182                     color: '#599'
183                 },
184                 100: {
185                     color: '#ddd'
186                 }
187             }
188         }]
189      &lt;/code&gt;&lt;/pre&gt;
190
191      Then the sprites can use `gradientId` and `gradientId2` by setting the fill attributes to those ids, for example:
192
193      &lt;pre&gt;&lt;code&gt;
194         sprite.setAttributes({
195             fill: 'url(#gradientId)'
196         }, true);
197      &lt;/code&gt;&lt;/pre&gt;
198
199      */
200
201
202     constructor: function(config) {
203         var me = this,
204             defaultAnim;
205         me.initTheme(config.theme || me.theme);
206         if (me.gradients) {
207             Ext.apply(config, { gradients: me.gradients });
208         }
209         if (me.background) {
210             Ext.apply(config, { background: me.background });
211         }
212         if (config.animate) {
213             defaultAnim = {
214                 easing: 'ease',
215                 duration: 500
216             };
217             if (Ext.isObject(config.animate)) {
218                 config.animate = Ext.applyIf(config.animate, defaultAnim);
219             }
220             else {
221                 config.animate = defaultAnim;
222             }
223         }
224         me.mixins.mask.constructor.call(me, config);
225         me.mixins.navigation.constructor.call(me, config);
226         me.callParent([config]);
227     },
228
229     initComponent: function() {
230         var me = this,
231             axes,
232             series;
233         me.callParent();
234         me.addEvents(
235             'itemmousedown',
236             'itemmouseup',
237             'itemmouseover',
238             'itemmouseout',
239             'itemclick',
240             'itemdoubleclick',
241             'itemdragstart',
242             'itemdrag',
243             'itemdragend',
244 <span id='Ext-chart-Chart-event-beforerefresh'>            /**
245 </span>                 * @event beforerefresh
246                  * Fires before a refresh to the chart data is called.  If the beforerefresh handler returns
247                  * &lt;tt&gt;false&lt;/tt&gt; the {@link #refresh} action will be cancelled.
248                  * @param {Chart} this
249                  */
250             'beforerefresh',
251 <span id='Ext-chart-Chart-event-refresh'>            /**
252 </span>                 * @event refresh
253                  * Fires after the chart data has been refreshed.
254                  * @param {Chart} this
255                  */
256             'refresh'
257         );
258         Ext.applyIf(me, {
259             zoom: {
260                 width: 1,
261                 height: 1,
262                 x: 0,
263                 y: 0
264             }
265         });
266         me.maxGutter = [0, 0];
267         me.store = Ext.data.StoreManager.lookup(me.store);
268         axes = me.axes;
269         me.axes = Ext.create('Ext.util.MixedCollection', false, function(a) { return a.position; });
270         if (axes) {
271             me.axes.addAll(axes);
272         }
273         series = me.series;
274         me.series = Ext.create('Ext.util.MixedCollection', false, function(a) { return a.seriesId || (a.seriesId = Ext.id(null, 'ext-chart-series-')); });
275         if (series) {
276             me.series.addAll(series);
277         }
278         if (me.legend !== false) {
279             me.legend = Ext.create('Ext.chart.Legend', Ext.applyIf({chart:me}, me.legend));
280         }
281
282         me.on({
283             mousemove: me.onMouseMove,
284             mouseleave: me.onMouseLeave,
285             mousedown: me.onMouseDown,
286             mouseup: me.onMouseUp,
287             scope: me
288         });
289     },
290
291     // @private overrides the component method to set the correct dimensions to the chart.
292     afterComponentLayout: function(width, height) {
293         var me = this;
294         if (Ext.isNumber(width) &amp;&amp; Ext.isNumber(height)) {
295             me.curWidth = width;
296             me.curHeight = height;
297             me.redraw(true);
298         }
299         this.callParent(arguments);
300     },
301
302 <span id='Ext-chart-Chart-cfg-resize'>    /**
303 </span>     * Redraw the chart. If animations are set this will animate the chart too.
304      * @cfg {boolean} resize Optional flag which changes the default origin points of the chart for animations.
305      */
306     redraw: function(resize) {
307         var me = this,
308             chartBBox = me.chartBBox = {
309                 x: 0,
310                 y: 0,
311                 height: me.curHeight,
312                 width: me.curWidth
313             },
314             legend = me.legend;
315         me.surface.setSize(chartBBox.width, chartBBox.height);
316         // Instantiate Series and Axes
317         me.series.each(me.initializeSeries, me);
318         me.axes.each(me.initializeAxis, me);
319         //process all views (aggregated data etc) on stores
320         //before rendering.
321         me.axes.each(function(axis) {
322             axis.processView();
323         });
324         me.axes.each(function(axis) {
325             axis.drawAxis(true);
326         });
327
328         // Create legend if not already created
329         if (legend !== false) {
330             legend.create();
331         }
332
333         // Place axes properly, including influence from each other
334         me.alignAxes();
335
336         // Reposition legend based on new axis alignment
337         if (me.legend !== false) {
338             legend.updatePosition();
339         }
340
341         // Find the max gutter
342         me.getMaxGutter();
343
344         // Draw axes and series
345         me.resizing = !!resize;
346
347         me.axes.each(me.drawAxis, me);
348         me.series.each(me.drawCharts, me);
349         me.resizing = false;
350     },
351
352     // @private set the store after rendering the chart.
353     afterRender: function() {
354         var ref,
355             me = this;
356         this.callParent();
357
358         if (me.categoryNames) {
359             me.setCategoryNames(me.categoryNames);
360         }
361
362         if (me.tipRenderer) {
363             ref = me.getFunctionRef(me.tipRenderer);
364             me.setTipRenderer(ref.fn, ref.scope);
365         }
366         me.bindStore(me.store, true);
367         me.refresh();
368     },
369
370     // @private get x and y position of the mouse cursor.
371     getEventXY: function(e) {
372         var me = this,
373             box = this.surface.getRegion(),
374             pageXY = e.getXY(),
375             x = pageXY[0] - box.left,
376             y = pageXY[1] - box.top;
377         return [x, y];
378     },
379
380     // @private wrap the mouse down position to delegate the event to the series.
381     onClick: function(e) {
382         var me = this,
383             position = me.getEventXY(e),
384             item;
385
386         // Ask each series if it has an item corresponding to (not necessarily exactly
387         // on top of) the current mouse coords. Fire itemclick event.
388         me.series.each(function(series) {
389             if (Ext.draw.Draw.withinBox(position[0], position[1], series.bbox)) {
390                 if (series.getItemForPoint) {
391                     item = series.getItemForPoint(position[0], position[1]);
392                     if (item) {
393                         series.fireEvent('itemclick', item);
394                     }
395                 }
396             }
397         }, me);
398     },
399
400     // @private wrap the mouse down position to delegate the event to the series.
401     onMouseDown: function(e) {
402         var me = this,
403             position = me.getEventXY(e),
404             item;
405
406         if (me.mask) {
407             me.mixins.mask.onMouseDown.call(me, e);
408         }
409         // Ask each series if it has an item corresponding to (not necessarily exactly
410         // on top of) the current mouse coords. Fire mousedown event.
411         me.series.each(function(series) {
412             if (Ext.draw.Draw.withinBox(position[0], position[1], series.bbox)) {
413                 if (series.getItemForPoint) {
414                     item = series.getItemForPoint(position[0], position[1]);
415                     if (item) {
416                         series.fireEvent('itemmousedown', item);
417                     }
418                 }
419             }
420         }, me);
421     },
422
423     // @private wrap the mouse up event to delegate it to the series.
424     onMouseUp: function(e) {
425         var me = this,
426             position = me.getEventXY(e),
427             item;
428
429         if (me.mask) {
430             me.mixins.mask.onMouseUp.call(me, e);
431         }
432         // Ask each series if it has an item corresponding to (not necessarily exactly
433         // on top of) the current mouse coords. Fire mousedown event.
434         me.series.each(function(series) {
435             if (Ext.draw.Draw.withinBox(position[0], position[1], series.bbox)) {
436                 if (series.getItemForPoint) {
437                     item = series.getItemForPoint(position[0], position[1]);
438                     if (item) {
439                         series.fireEvent('itemmouseup', item);
440                     }
441                 }
442             }
443         }, me);
444     },
445
446     // @private wrap the mouse move event so it can be delegated to the series.
447     onMouseMove: function(e) {
448         var me = this,
449             position = me.getEventXY(e),
450             item, last, storeItem, storeField;
451
452         if (me.mask) {
453             me.mixins.mask.onMouseMove.call(me, e);
454         }
455         // Ask each series if it has an item corresponding to (not necessarily exactly
456         // on top of) the current mouse coords. Fire itemmouseover/out events.
457         me.series.each(function(series) {
458             if (Ext.draw.Draw.withinBox(position[0], position[1], series.bbox)) {
459                 if (series.getItemForPoint) {
460                     item = series.getItemForPoint(position[0], position[1]);
461                     last = series._lastItemForPoint;
462                     storeItem = series._lastStoreItem;
463                     storeField = series._lastStoreField;
464
465
466                     if (item !== last || item &amp;&amp; (item.storeItem != storeItem || item.storeField != storeField)) {
467                         if (last) {
468                             series.fireEvent('itemmouseout', last);
469                             delete series._lastItemForPoint;
470                             delete series._lastStoreField;
471                             delete series._lastStoreItem;
472                         }
473                         if (item) {
474                             series.fireEvent('itemmouseover', item);
475                             series._lastItemForPoint = item;
476                             series._lastStoreItem = item.storeItem;
477                             series._lastStoreField = item.storeField;
478                         }
479                     }
480                 }
481             } else {
482                 last = series._lastItemForPoint;
483                 if (last) {
484                     series.fireEvent('itemmouseout', last);
485                     delete series._lastItemForPoint;
486                     delete series._lastStoreField;
487                     delete series._lastStoreItem;
488                 }
489             }
490         }, me);
491     },
492
493     // @private handle mouse leave event.
494     onMouseLeave: function(e) {
495         var me = this;
496         if (me.mask) {
497             me.mixins.mask.onMouseLeave.call(me, e);
498         }
499         me.series.each(function(series) {
500             delete series._lastItemForPoint;
501         });
502     },
503
504     // @private buffered refresh for when we update the store
505     delayRefresh: function() {
506         var me = this;
507         if (!me.refreshTask) {
508             me.refreshTask = Ext.create('Ext.util.DelayedTask', me.refresh, me);
509         }
510         me.refreshTask.delay(me.refreshBuffer);
511     },
512
513     // @private
514     refresh: function() {
515         var me = this;
516         if (me.rendered &amp;&amp; me.curWidth != undefined &amp;&amp; me.curHeight != undefined) {
517             if (me.fireEvent('beforerefresh', me) !== false) {
518                 me.redraw();
519                 me.fireEvent('refresh', me);
520             }
521         }
522     },
523
524 <span id='Ext-chart-Chart-method-bindStore'>    /**
525 </span>     * Changes the data store bound to this chart and refreshes it.
526      * @param {Store} store The store to bind to this chart
527      */
528     bindStore: function(store, initial) {
529         var me = this;
530         if (!initial &amp;&amp; me.store) {
531             if (store !== me.store &amp;&amp; me.store.autoDestroy) {
532                 me.store.destroy();
533             }
534             else {
535                 me.store.un('datachanged', me.refresh, me);
536                 me.store.un('add', me.delayRefresh, me);
537                 me.store.un('remove', me.delayRefresh, me);
538                 me.store.un('update', me.delayRefresh, me);
539                 me.store.un('clear', me.refresh, me);
540             }
541         }
542         if (store) {
543             store = Ext.data.StoreManager.lookup(store);
544             store.on({
545                 scope: me,
546                 datachanged: me.refresh,
547                 add: me.delayRefresh,
548                 remove: me.delayRefresh,
549                 update: me.delayRefresh,
550                 clear: me.refresh
551             });
552         }
553         me.store = store;
554         if (store &amp;&amp; !initial) {
555             me.refresh();
556         }
557     },
558
559     // @private Create Axis
560     initializeAxis: function(axis) {
561         var me = this,
562             chartBBox = me.chartBBox,
563             w = chartBBox.width,
564             h = chartBBox.height,
565             x = chartBBox.x,
566             y = chartBBox.y,
567             themeAttrs = me.themeAttrs,
568             config = {
569                 chart: me
570             };
571         if (themeAttrs) {
572             config.axisStyle = Ext.apply({}, themeAttrs.axis);
573             config.axisLabelLeftStyle = Ext.apply({}, themeAttrs.axisLabelLeft);
574             config.axisLabelRightStyle = Ext.apply({}, themeAttrs.axisLabelRight);
575             config.axisLabelTopStyle = Ext.apply({}, themeAttrs.axisLabelTop);
576             config.axisLabelBottomStyle = Ext.apply({}, themeAttrs.axisLabelBottom);
577             config.axisTitleLeftStyle = Ext.apply({}, themeAttrs.axisTitleLeft);
578             config.axisTitleRightStyle = Ext.apply({}, themeAttrs.axisTitleRight);
579             config.axisTitleTopStyle = Ext.apply({}, themeAttrs.axisTitleTop);
580             config.axisTitleBottomStyle = Ext.apply({}, themeAttrs.axisTitleBottom);
581         }
582         switch (axis.position) {
583             case 'top':
584                 Ext.apply(config, {
585                     length: w,
586                     width: h,
587                     x: x,
588                     y: y
589                 });
590             break;
591             case 'bottom':
592                 Ext.apply(config, {
593                     length: w,
594                     width: h,
595                     x: x,
596                     y: h
597                 });
598             break;
599             case 'left':
600                 Ext.apply(config, {
601                     length: h,
602                     width: w,
603                     x: x,
604                     y: h
605                 });
606             break;
607             case 'right':
608                 Ext.apply(config, {
609                     length: h,
610                     width: w,
611                     x: w,
612                     y: h
613                 });
614             break;
615         }
616         if (!axis.chart) {
617             Ext.apply(config, axis);
618             axis = me.axes.replace(Ext.createByAlias('axis.' + axis.type.toLowerCase(), config));
619         }
620         else {
621             Ext.apply(axis, config);
622         }
623     },
624
625
626 <span id='Ext-chart-Chart-method-alignAxes'>    /**
627 </span>     * @private Adjust the dimensions and positions of each axis and the chart body area after accounting
628      * for the space taken up on each side by the axes and legend.
629      */
630     alignAxes: function() {
631         var me = this,
632             axes = me.axes,
633             legend = me.legend,
634             edges = ['top', 'right', 'bottom', 'left'],
635             chartBBox,
636             insetPadding = me.insetPadding,
637             insets = {
638                 top: insetPadding,
639                 right: insetPadding,
640                 bottom: insetPadding,
641                 left: insetPadding
642             };
643
644         function getAxis(edge) {
645             var i = axes.findIndex('position', edge);
646             return (i &lt; 0) ? null : axes.getAt(i);
647         }
648
649         // Find the space needed by axes and legend as a positive inset from each edge
650         Ext.each(edges, function(edge) {
651             var isVertical = (edge === 'left' || edge === 'right'),
652                 axis = getAxis(edge),
653                 bbox;
654
655             // Add legend size if it's on this edge
656             if (legend !== false) {
657                 if (legend.position === edge) {
658                     bbox = legend.getBBox();
659                     insets[edge] += (isVertical ? bbox.width : bbox.height) + insets[edge];
660                 }
661             }
662
663             // Add axis size if there's one on this edge only if it has been
664             //drawn before.
665             if (axis &amp;&amp; axis.bbox) {
666                 bbox = axis.bbox;
667                 insets[edge] += (isVertical ? bbox.width : bbox.height);
668             }
669         });
670         // Build the chart bbox based on the collected inset values
671         chartBBox = {
672             x: insets.left,
673             y: insets.top,
674             width: me.curWidth - insets.left - insets.right,
675             height: me.curHeight - insets.top - insets.bottom
676         };
677         me.chartBBox = chartBBox;
678
679         // Go back through each axis and set its length and position based on the
680         // corresponding edge of the chartBBox
681         axes.each(function(axis) {
682             var pos = axis.position,
683                 isVertical = (pos === 'left' || pos === 'right');
684
685             axis.x = (pos === 'right' ? chartBBox.x + chartBBox.width : chartBBox.x);
686             axis.y = (pos === 'top' ? chartBBox.y : chartBBox.y + chartBBox.height);
687             axis.width = (isVertical ? chartBBox.width : chartBBox.height);
688             axis.length = (isVertical ? chartBBox.height : chartBBox.width);
689         });
690     },
691
692     // @private initialize the series.
693     initializeSeries: function(series, idx) {
694         var me = this,
695             themeAttrs = me.themeAttrs,
696             seriesObj, markerObj, seriesThemes, st,
697             markerThemes, colorArrayStyle = [],
698             i = 0, l,
699             config = {
700                 chart: me,
701                 seriesId: series.seriesId
702             };
703         if (themeAttrs) {
704             seriesThemes = themeAttrs.seriesThemes;
705             markerThemes = themeAttrs.markerThemes;
706             seriesObj = Ext.apply({}, themeAttrs.series);
707             markerObj = Ext.apply({}, themeAttrs.marker);
708             config.seriesStyle = Ext.apply(seriesObj, seriesThemes[idx % seriesThemes.length]);
709             config.seriesLabelStyle = Ext.apply({}, themeAttrs.seriesLabel);
710             config.markerStyle = Ext.apply(markerObj, markerThemes[idx % markerThemes.length]);
711             if (themeAttrs.colors) {
712                 config.colorArrayStyle = themeAttrs.colors;
713             } else {
714                 colorArrayStyle = [];
715                 for (l = seriesThemes.length; i &lt; l; i++) {
716                     st = seriesThemes[i];
717                     if (st.fill || st.stroke) {
718                         colorArrayStyle.push(st.fill || st.stroke);
719                     }
720                 }
721                 if (colorArrayStyle.length) {
722                     config.colorArrayStyle = colorArrayStyle;
723                 }
724             }
725             config.seriesIdx = idx;
726         }
727         if (series instanceof Ext.chart.series.Series) {
728             Ext.apply(series, config);
729         } else {
730             Ext.applyIf(config, series);
731             series = me.series.replace(Ext.createByAlias('series.' + series.type.toLowerCase(), config));
732         }
733         if (series.initialize) {
734             series.initialize();
735         }
736     },
737
738     // @private
739     getMaxGutter: function() {
740         var me = this,
741             maxGutter = [0, 0];
742         me.series.each(function(s) {
743             var gutter = s.getGutters &amp;&amp; s.getGutters() || [0, 0];
744             maxGutter[0] = Math.max(maxGutter[0], gutter[0]);
745             maxGutter[1] = Math.max(maxGutter[1], gutter[1]);
746         });
747         me.maxGutter = maxGutter;
748     },
749
750     // @private draw axis.
751     drawAxis: function(axis) {
752         axis.drawAxis();
753     },
754
755     // @private draw series.
756     drawCharts: function(series) {
757         series.triggerafterrender = false;
758         series.drawSeries();
759         if (!this.animate) {
760             series.fireEvent('afterrender');
761         }
762     },
763
764     // @private remove gently.
765     destroy: function() {
766         this.surface.destroy();
767         this.bindStore(null);
768         this.callParent(arguments);
769     }
770 });
771 </pre>
772 </body>
773 </html>