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