Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / docs / source / Axis.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="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../resources/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-axis-Axis'>/**
19 </span> * @class Ext.chart.axis.Axis
20  * @extends Ext.chart.axis.Abstract
21  *
22  * Defines axis for charts. The axis position, type, style can be configured.
23  * The axes are defined in an axes array of configuration objects where the type,
24  * field, grid and other configuration options can be set. To know more about how
25  * to create a Chart please check the Chart class documentation. Here's an example for the axes part:
26  * An example of axis for a series (in this case for an area chart that has multiple layers of yFields) could be:
27  *
28  *     axes: [{
29  *         type: 'Numeric',
30  *         grid: true,
31  *         position: 'left',
32  *         fields: ['data1', 'data2', 'data3'],
33  *         title: 'Number of Hits',
34  *         grid: {
35  *             odd: {
36  *                 opacity: 1,
37  *                 fill: '#ddd',
38  *                 stroke: '#bbb',
39  *                 'stroke-width': 1
40  *             }
41  *         },
42  *         minimum: 0
43  *     }, {
44  *         type: 'Category',
45  *         position: 'bottom',
46  *         fields: ['name'],
47  *         title: 'Month of the Year',
48  *         grid: true,
49  *         label: {
50  *             rotate: {
51  *                 degrees: 315
52  *             }
53  *         }
54  *     }]
55  *
56  * In this case we use a `Numeric` axis for displaying the values of the Area series and a `Category` axis for displaying the names of
57  * the store elements. The numeric axis is placed on the left of the screen, while the category axis is placed at the bottom of the chart.
58  * Both the category and numeric axes have `grid` set, which means that horizontal and vertical lines will cover the chart background. In the
59  * category axis the labels will be rotated so they can fit the space better.
60  */
61 Ext.define('Ext.chart.axis.Axis', {
62
63     /* Begin Definitions */
64
65     extend: 'Ext.chart.axis.Abstract',
66
67     alternateClassName: 'Ext.chart.Axis',
68
69     requires: ['Ext.draw.Draw'],
70
71     /* End Definitions */
72
73 <span id='Ext-chart-axis-Axis-cfg-grid'>    /**
74 </span>     * @cfg {Boolean/Object} grid
75      * The grid configuration enables you to set a background grid for an axis.
76      * If set to *true* on a vertical axis, vertical lines will be drawn.
77      * If set to *true* on a horizontal axis, horizontal lines will be drawn.
78      * If both are set, a proper grid with horizontal and vertical lines will be drawn.
79      *
80      * You can set specific options for the grid configuration for odd and/or even lines/rows.
81      * Since the rows being drawn are rectangle sprites, you can set to an odd or even property
82      * all styles that apply to {@link Ext.draw.Sprite}. For more information on all the style
83      * properties you can set please take a look at {@link Ext.draw.Sprite}. Some useful style properties are `opacity`, `fill`, `stroke`, `stroke-width`, etc.
84      *
85      * The possible values for a grid option are then *true*, *false*, or an object with `{ odd, even }` properties
86      * where each property contains a sprite style descriptor object that is defined in {@link Ext.draw.Sprite}.
87      *
88      * For example:
89      *
90      *     axes: [{
91      *         type: 'Numeric',
92      *         grid: true,
93      *         position: 'left',
94      *         fields: ['data1', 'data2', 'data3'],
95      *         title: 'Number of Hits',
96      *         grid: {
97      *             odd: {
98      *                 opacity: 1,
99      *                 fill: '#ddd',
100      *                 stroke: '#bbb',
101      *                 'stroke-width': 1
102      *             }
103      *         }
104      *     }, {
105      *         type: 'Category',
106      *         position: 'bottom',
107      *         fields: ['name'],
108      *         title: 'Month of the Year',
109      *         grid: true
110      *     }]
111      *
112      */
113
114 <span id='Ext-chart-axis-Axis-cfg-majorTickSteps'>    /**
115 </span>     * @cfg {Number} majorTickSteps
116      * If `minimum` and `maximum` are specified it forces the number of major ticks to the specified value.
117      */
118
119 <span id='Ext-chart-axis-Axis-cfg-minorTickSteps'>    /**
120 </span>     * @cfg {Number} minorTickSteps
121      * The number of small ticks between two major ticks. Default is zero.
122      */
123
124 <span id='Ext-chart-axis-Axis-cfg-title'>    /**
125 </span>     * @cfg {String} title
126      * The title for the Axis
127      */
128
129     //@private force min/max values from store
130     forceMinMax: false,
131
132 <span id='Ext-chart-axis-Axis-cfg-dashSize'>    /**
133 </span>     * @cfg {Number} dashSize
134      * The size of the dash marker. Default's 3.
135      */
136     dashSize: 3,
137
138 <span id='Ext-chart-axis-Axis-cfg-position'>    /**
139 </span>     * @cfg {String} position
140      * Where to set the axis. Available options are `left`, `bottom`, `right`, `top`. Default's `bottom`.
141      */
142     position: 'bottom',
143
144     // @private
145     skipFirst: false,
146
147 <span id='Ext-chart-axis-Axis-cfg-length'>    /**
148 </span>     * @cfg {Number} length
149      * Offset axis position. Default's 0.
150      */
151     length: 0,
152
153 <span id='Ext-chart-axis-Axis-cfg-width'>    /**
154 </span>     * @cfg {Number} width
155      * Offset axis width. Default's 0.
156      */
157     width: 0,
158
159     majorTickSteps: false,
160
161     // @private
162     applyData: Ext.emptyFn,
163
164     getRange: function () {
165         var me = this,
166             store = me.chart.getChartStore(),
167             fields = me.fields,
168             ln = fields.length,
169             math = Math,
170             mmax = math.max,
171             mmin = math.min,
172             aggregate = false,
173             min = isNaN(me.minimum) ? Infinity : me.minimum,
174             max = isNaN(me.maximum) ? -Infinity : me.maximum,
175             total = 0, i, l, value, values, rec,
176             excludes = [],
177             series = me.chart.series.items;
178
179         //if one series is stacked I have to aggregate the values
180         //for the scale.
181         // TODO(zhangbei): the code below does not support series that stack on 1 side but non-stacked axis
182         // listed in axis config. For example, a Area series whose axis : ['left', 'bottom'].
183         // Assuming only stack on y-axis.
184         for (i = 0, l = series.length; !aggregate &amp;&amp; i &lt; l; i++) {
185             aggregate = aggregate || (me.position == 'left' || me.position == 'right') &amp;&amp; series[i].stacked;
186             excludes = series[i].__excludes || excludes;
187         }
188         store.each(function(record) {
189             if (aggregate) {
190                 if (!isFinite(min)) {
191                     min = 0;
192                 }
193                 for (values = [0, 0], i = 0; i &lt; ln; i++) {
194                     if (excludes[i]) {
195                         continue;
196                     }
197                     rec = record.get(fields[i]);
198                     values[+(rec &gt; 0)] += math.abs(rec);
199                 }
200                 max = mmax(max, -values[0], +values[1]);
201                 min = mmin(min, -values[0], +values[1]);
202             }
203             else {
204                 for (i = 0; i &lt; ln; i++) {
205                     if (excludes[i]) {
206                         continue;
207                     }
208                     value = record.get(fields[i]);
209                     max = mmax(max, +value);
210                     min = mmin(min, +value);
211                 }
212             }
213         });
214         if (!isFinite(max)) {
215             max = me.prevMax || 0;
216         }
217         if (!isFinite(min)) {
218             min = me.prevMin || 0;
219         }
220         //normalize min max for snapEnds.
221         if (min != max &amp;&amp; (max != Math.floor(max))) {
222             max = Math.floor(max) + 1;
223         }
224
225         if (!isNaN(me.minimum)) {
226             min = me.minimum;
227         }
228         
229         if (!isNaN(me.maximum)) {
230             max = me.maximum;
231         }
232
233         return {min: min, max: max};
234     },
235
236     // @private creates a structure with start, end and step points.
237     calcEnds: function() {
238         var me = this,
239             fields = me.fields,
240             range = me.getRange(),
241             min = range.min,
242             max = range.max,
243             outfrom, outto, out;
244
245         out = Ext.draw.Draw.snapEnds(min, max, me.majorTickSteps !== false ?  (me.majorTickSteps +1) : me.steps);
246         outfrom = out.from;
247         outto = out.to;
248         if (me.forceMinMax) {
249             if (!isNaN(max)) {
250                 out.to = max;
251             }
252             if (!isNaN(min)) {
253                 out.from = min;
254             }
255         }
256         if (!isNaN(me.maximum)) {
257             //TODO(nico) users are responsible for their own minimum/maximum values set.
258             //Clipping should be added to remove lines in the chart which are below the axis.
259             out.to = me.maximum;
260         }
261         if (!isNaN(me.minimum)) {
262             //TODO(nico) users are responsible for their own minimum/maximum values set.
263             //Clipping should be added to remove lines in the chart which are below the axis.
264             out.from = me.minimum;
265         }
266
267         //Adjust after adjusting minimum and maximum
268         out.step = (out.to - out.from) / (outto - outfrom) * out.step;
269
270         if (me.adjustMaximumByMajorUnit) {
271             out.to += out.step;
272         }
273         if (me.adjustMinimumByMajorUnit) {
274             out.from -= out.step;
275         }
276         me.prevMin = min == max? 0 : min;
277         me.prevMax = max;
278         return out;
279     },
280
281 <span id='Ext-chart-axis-Axis-method-drawAxis'>    /**
282 </span>     * Renders the axis into the screen and updates its position.
283      */
284     drawAxis: function (init) {
285         var me = this,
286             i, j,
287             x = me.x,
288             y = me.y,
289             gutterX = me.chart.maxGutter[0],
290             gutterY = me.chart.maxGutter[1],
291             dashSize = me.dashSize,
292             subDashesX = me.minorTickSteps || 0,
293             subDashesY = me.minorTickSteps || 0,
294             length = me.length,
295             position = me.position,
296             inflections = [],
297             calcLabels = false,
298             stepCalcs = me.applyData(),
299             step = stepCalcs.step,
300             steps = stepCalcs.steps,
301             from = stepCalcs.from,
302             to = stepCalcs.to,
303             trueLength,
304             currentX,
305             currentY,
306             path,
307             prev,
308             dashesX,
309             dashesY,
310             delta;
311
312         //If no steps are specified
313         //then don't draw the axis. This generally happens
314         //when an empty store.
315         if (me.hidden || isNaN(step) || (from == to)) {
316             return;
317         }
318
319         me.from = stepCalcs.from;
320         me.to = stepCalcs.to;
321         if (position == 'left' || position == 'right') {
322             currentX = Math.floor(x) + 0.5;
323             path = [&quot;M&quot;, currentX, y, &quot;l&quot;, 0, -length];
324             trueLength = length - (gutterY * 2);
325         }
326         else {
327             currentY = Math.floor(y) + 0.5;
328             path = [&quot;M&quot;, x, currentY, &quot;l&quot;, length, 0];
329             trueLength = length - (gutterX * 2);
330         }
331
332         delta = trueLength / (steps || 1);
333         dashesX = Math.max(subDashesX +1, 0);
334         dashesY = Math.max(subDashesY +1, 0);
335         if (me.type == 'Numeric' || me.type == 'Time') {
336             calcLabels = true;
337             me.labels = [stepCalcs.from];
338         }
339         if (position == 'right' || position == 'left') {
340             currentY = y - gutterY;
341             currentX = x - ((position == 'left') * dashSize * 2);
342             while (currentY &gt;= y - gutterY - trueLength) {
343                 path.push(&quot;M&quot;, currentX, Math.floor(currentY) + 0.5, &quot;l&quot;, dashSize * 2 + 1, 0);
344                 if (currentY != y - gutterY) {
345                     for (i = 1; i &lt; dashesY; i++) {
346                         path.push(&quot;M&quot;, currentX + dashSize, Math.floor(currentY + delta * i / dashesY) + 0.5, &quot;l&quot;, dashSize + 1, 0);
347                     }
348                 }
349                 inflections.push([ Math.floor(x), Math.floor(currentY) ]);
350                 currentY -= delta;
351                 if (calcLabels) {
352                     me.labels.push(me.labels[me.labels.length -1] + step);
353                 }
354                 if (delta === 0) {
355                     break;
356                 }
357             }
358             if (Math.round(currentY + delta - (y - gutterY - trueLength))) {
359                 path.push(&quot;M&quot;, currentX, Math.floor(y - length + gutterY) + 0.5, &quot;l&quot;, dashSize * 2 + 1, 0);
360                 for (i = 1; i &lt; dashesY; i++) {
361                     path.push(&quot;M&quot;, currentX + dashSize, Math.floor(y - length + gutterY + delta * i / dashesY) + 0.5, &quot;l&quot;, dashSize + 1, 0);
362                 }
363                 inflections.push([ Math.floor(x), Math.floor(currentY) ]);
364                 if (calcLabels) {
365                     me.labels.push(me.labels[me.labels.length -1] + step);
366                 }
367             }
368         } else {
369             currentX = x + gutterX;
370             currentY = y - ((position == 'top') * dashSize * 2);
371             while (currentX &lt;= x + gutterX + trueLength) {
372                 path.push(&quot;M&quot;, Math.floor(currentX) + 0.5, currentY, &quot;l&quot;, 0, dashSize * 2 + 1);
373                 if (currentX != x + gutterX) {
374                     for (i = 1; i &lt; dashesX; i++) {
375                         path.push(&quot;M&quot;, Math.floor(currentX - delta * i / dashesX) + 0.5, currentY, &quot;l&quot;, 0, dashSize + 1);
376                     }
377                 }
378                 inflections.push([ Math.floor(currentX), Math.floor(y) ]);
379                 currentX += delta;
380                 if (calcLabels) {
381                     me.labels.push(me.labels[me.labels.length -1] + step);
382                 }
383                 if (delta === 0) {
384                     break;
385                 }
386             }
387             if (Math.round(currentX - delta - (x + gutterX + trueLength))) {
388                 path.push(&quot;M&quot;, Math.floor(x + length - gutterX) + 0.5, currentY, &quot;l&quot;, 0, dashSize * 2 + 1);
389                 for (i = 1; i &lt; dashesX; i++) {
390                     path.push(&quot;M&quot;, Math.floor(x + length - gutterX - delta * i / dashesX) + 0.5, currentY, &quot;l&quot;, 0, dashSize + 1);
391                 }
392                 inflections.push([ Math.floor(currentX), Math.floor(y) ]);
393                 if (calcLabels) {
394                     me.labels.push(me.labels[me.labels.length -1] + step);
395                 }
396             }
397         }
398         if (!me.axis) {
399             me.axis = me.chart.surface.add(Ext.apply({
400                 type: 'path',
401                 path: path
402             }, me.axisStyle));
403         }
404         me.axis.setAttributes({
405             path: path
406         }, true);
407         me.inflections = inflections;
408         if (!init &amp;&amp; me.grid) {
409             me.drawGrid();
410         }
411         me.axisBBox = me.axis.getBBox();
412         me.drawLabel();
413     },
414
415 <span id='Ext-chart-axis-Axis-method-drawGrid'>    /**
416 </span>     * Renders an horizontal and/or vertical grid into the Surface.
417      */
418     drawGrid: function() {
419         var me = this,
420             surface = me.chart.surface,
421             grid = me.grid,
422             odd = grid.odd,
423             even = grid.even,
424             inflections = me.inflections,
425             ln = inflections.length - ((odd || even)? 0 : 1),
426             position = me.position,
427             gutter = me.chart.maxGutter,
428             width = me.width - 2,
429             vert = false,
430             point, prevPoint,
431             i = 1,
432             path = [], styles, lineWidth, dlineWidth,
433             oddPath = [], evenPath = [];
434
435         if ((gutter[1] !== 0 &amp;&amp; (position == 'left' || position == 'right')) ||
436             (gutter[0] !== 0 &amp;&amp; (position == 'top' || position == 'bottom'))) {
437             i = 0;
438             ln++;
439         }
440         for (; i &lt; ln; i++) {
441             point = inflections[i];
442             prevPoint = inflections[i - 1];
443             if (odd || even) {
444                 path = (i % 2)? oddPath : evenPath;
445                 styles = ((i % 2)? odd : even) || {};
446                 lineWidth = (styles.lineWidth || styles['stroke-width'] || 0) / 2;
447                 dlineWidth = 2 * lineWidth;
448                 if (position == 'left') {
449                     path.push(&quot;M&quot;, prevPoint[0] + 1 + lineWidth, prevPoint[1] + 0.5 - lineWidth,
450                               &quot;L&quot;, prevPoint[0] + 1 + width - lineWidth, prevPoint[1] + 0.5 - lineWidth,
451                               &quot;L&quot;, point[0] + 1 + width - lineWidth, point[1] + 0.5 + lineWidth,
452                               &quot;L&quot;, point[0] + 1 + lineWidth, point[1] + 0.5 + lineWidth, &quot;Z&quot;);
453                 }
454                 else if (position == 'right') {
455                     path.push(&quot;M&quot;, prevPoint[0] - lineWidth, prevPoint[1] + 0.5 - lineWidth,
456                               &quot;L&quot;, prevPoint[0] - width + lineWidth, prevPoint[1] + 0.5 - lineWidth,
457                               &quot;L&quot;, point[0] - width + lineWidth, point[1] + 0.5 + lineWidth,
458                               &quot;L&quot;, point[0] - lineWidth, point[1] + 0.5 + lineWidth, &quot;Z&quot;);
459                 }
460                 else if (position == 'top') {
461                     path.push(&quot;M&quot;, prevPoint[0] + 0.5 + lineWidth, prevPoint[1] + 1 + lineWidth,
462                               &quot;L&quot;, prevPoint[0] + 0.5 + lineWidth, prevPoint[1] + 1 + width - lineWidth,
463                               &quot;L&quot;, point[0] + 0.5 - lineWidth, point[1] + 1 + width - lineWidth,
464                               &quot;L&quot;, point[0] + 0.5 - lineWidth, point[1] + 1 + lineWidth, &quot;Z&quot;);
465                 }
466                 else {
467                     path.push(&quot;M&quot;, prevPoint[0] + 0.5 + lineWidth, prevPoint[1] - lineWidth,
468                             &quot;L&quot;, prevPoint[0] + 0.5 + lineWidth, prevPoint[1] - width + lineWidth,
469                             &quot;L&quot;, point[0] + 0.5 - lineWidth, point[1] - width + lineWidth,
470                             &quot;L&quot;, point[0] + 0.5 - lineWidth, point[1] - lineWidth, &quot;Z&quot;);
471                 }
472             } else {
473                 if (position == 'left') {
474                     path = path.concat([&quot;M&quot;, point[0] + 0.5, point[1] + 0.5, &quot;l&quot;, width, 0]);
475                 }
476                 else if (position == 'right') {
477                     path = path.concat([&quot;M&quot;, point[0] - 0.5, point[1] + 0.5, &quot;l&quot;, -width, 0]);
478                 }
479                 else if (position == 'top') {
480                     path = path.concat([&quot;M&quot;, point[0] + 0.5, point[1] + 0.5, &quot;l&quot;, 0, width]);
481                 }
482                 else {
483                     path = path.concat([&quot;M&quot;, point[0] + 0.5, point[1] - 0.5, &quot;l&quot;, 0, -width]);
484                 }
485             }
486         }
487         if (odd || even) {
488             if (oddPath.length) {
489                 if (!me.gridOdd &amp;&amp; oddPath.length) {
490                     me.gridOdd = surface.add({
491                         type: 'path',
492                         path: oddPath
493                     });
494                 }
495                 me.gridOdd.setAttributes(Ext.apply({
496                     path: oddPath,
497                     hidden: false
498                 }, odd || {}), true);
499             }
500             if (evenPath.length) {
501                 if (!me.gridEven) {
502                     me.gridEven = surface.add({
503                         type: 'path',
504                         path: evenPath
505                     });
506                 }
507                 me.gridEven.setAttributes(Ext.apply({
508                     path: evenPath,
509                     hidden: false
510                 }, even || {}), true);
511             }
512         }
513         else {
514             if (path.length) {
515                 if (!me.gridLines) {
516                     me.gridLines = me.chart.surface.add({
517                         type: 'path',
518                         path: path,
519                         &quot;stroke-width&quot;: me.lineWidth || 1,
520                         stroke: me.gridColor || '#ccc'
521                     });
522                 }
523                 me.gridLines.setAttributes({
524                     hidden: false,
525                     path: path
526                 }, true);
527             }
528             else if (me.gridLines) {
529                 me.gridLines.hide(true);
530             }
531         }
532     },
533
534     //@private
535     getOrCreateLabel: function(i, text) {
536         var me = this,
537             labelGroup = me.labelGroup,
538             textLabel = labelGroup.getAt(i),
539             surface = me.chart.surface;
540         if (textLabel) {
541             if (text != textLabel.attr.text) {
542                 textLabel.setAttributes(Ext.apply({
543                     text: text
544                 }, me.label), true);
545                 textLabel._bbox = textLabel.getBBox();
546             }
547         }
548         else {
549             textLabel = surface.add(Ext.apply({
550                 group: labelGroup,
551                 type: 'text',
552                 x: 0,
553                 y: 0,
554                 text: text
555             }, me.label));
556             surface.renderItem(textLabel);
557             textLabel._bbox = textLabel.getBBox();
558         }
559         //get untransformed bounding box
560         if (me.label.rotation) {
561             textLabel.setAttributes({
562                 rotation: {
563                     degrees: 0
564                 }
565             }, true);
566             textLabel._ubbox = textLabel.getBBox();
567             textLabel.setAttributes(me.label, true);
568         } else {
569             textLabel._ubbox = textLabel._bbox;
570         }
571         return textLabel;
572     },
573
574     rect2pointArray: function(sprite) {
575         var surface = this.chart.surface,
576             rect = surface.getBBox(sprite, true),
577             p1 = [rect.x, rect.y],
578             p1p = p1.slice(),
579             p2 = [rect.x + rect.width, rect.y],
580             p2p = p2.slice(),
581             p3 = [rect.x + rect.width, rect.y + rect.height],
582             p3p = p3.slice(),
583             p4 = [rect.x, rect.y + rect.height],
584             p4p = p4.slice(),
585             matrix = sprite.matrix;
586         //transform the points
587         p1[0] = matrix.x.apply(matrix, p1p);
588         p1[1] = matrix.y.apply(matrix, p1p);
589
590         p2[0] = matrix.x.apply(matrix, p2p);
591         p2[1] = matrix.y.apply(matrix, p2p);
592
593         p3[0] = matrix.x.apply(matrix, p3p);
594         p3[1] = matrix.y.apply(matrix, p3p);
595
596         p4[0] = matrix.x.apply(matrix, p4p);
597         p4[1] = matrix.y.apply(matrix, p4p);
598         return [p1, p2, p3, p4];
599     },
600
601     intersect: function(l1, l2) {
602         var r1 = this.rect2pointArray(l1),
603             r2 = this.rect2pointArray(l2);
604         return !!Ext.draw.Draw.intersect(r1, r2).length;
605     },
606
607     drawHorizontalLabels: function() {
608        var  me = this,
609             labelConf = me.label,
610             floor = Math.floor,
611             max = Math.max,
612             axes = me.chart.axes,
613             position = me.position,
614             inflections = me.inflections,
615             ln = inflections.length,
616             labels = me.labels,
617             labelGroup = me.labelGroup,
618             maxHeight = 0,
619             ratio,
620             gutterY = me.chart.maxGutter[1],
621             ubbox, bbox, point, prevX, prevLabel,
622             projectedWidth = 0,
623             textLabel, attr, textRight, text,
624             label, last, x, y, i, firstLabel;
625
626         last = ln - 1;
627         //get a reference to the first text label dimensions
628         point = inflections[0];
629         firstLabel = me.getOrCreateLabel(0, me.label.renderer(labels[0]));
630         ratio = Math.floor(Math.abs(Math.sin(labelConf.rotate &amp;&amp; (labelConf.rotate.degrees * Math.PI / 180) || 0)));
631
632         for (i = 0; i &lt; ln; i++) {
633             point = inflections[i];
634             text = me.label.renderer(labels[i]);
635             textLabel = me.getOrCreateLabel(i, text);
636             bbox = textLabel._bbox;
637             maxHeight = max(maxHeight, bbox.height + me.dashSize + me.label.padding);
638             x = floor(point[0] - (ratio? bbox.height : bbox.width) / 2);
639             if (me.chart.maxGutter[0] == 0) {
640                 if (i == 0 &amp;&amp; axes.findIndex('position', 'left') == -1) {
641                     x = point[0];
642                 }
643                 else if (i == last &amp;&amp; axes.findIndex('position', 'right') == -1) {
644                     x = point[0] - bbox.width;
645                 }
646             }
647             if (position == 'top') {
648                 y = point[1] - (me.dashSize * 2) - me.label.padding - (bbox.height / 2);
649             }
650             else {
651                 y = point[1] + (me.dashSize * 2) + me.label.padding + (bbox.height / 2);
652             }
653
654             textLabel.setAttributes({
655                 hidden: false,
656                 x: x,
657                 y: y
658             }, true);
659
660             // Skip label if there isn't available minimum space
661             if (i != 0 &amp;&amp; (me.intersect(textLabel, prevLabel)
662                 || me.intersect(textLabel, firstLabel))) {
663                 textLabel.hide(true);
664                 continue;
665             }
666
667             prevLabel = textLabel;
668         }
669
670         return maxHeight;
671     },
672
673     drawVerticalLabels: function() {
674         var me = this,
675             inflections = me.inflections,
676             position = me.position,
677             ln = inflections.length,
678             labels = me.labels,
679             maxWidth = 0,
680             max = Math.max,
681             floor = Math.floor,
682             ceil = Math.ceil,
683             axes = me.chart.axes,
684             gutterY = me.chart.maxGutter[1],
685             ubbox, bbox, point, prevLabel,
686             projectedWidth = 0,
687             textLabel, attr, textRight, text,
688             label, last, x, y, i;
689
690         last = ln;
691         for (i = 0; i &lt; last; i++) {
692             point = inflections[i];
693             text = me.label.renderer(labels[i]);
694             textLabel = me.getOrCreateLabel(i, text);
695             bbox = textLabel._bbox;
696
697             maxWidth = max(maxWidth, bbox.width + me.dashSize + me.label.padding);
698             y = point[1];
699             if (gutterY &lt; bbox.height / 2) {
700                 if (i == last - 1 &amp;&amp; axes.findIndex('position', 'top') == -1) {
701                     y = me.y - me.length + ceil(bbox.height / 2);
702                 }
703                 else if (i == 0 &amp;&amp; axes.findIndex('position', 'bottom') == -1) {
704                     y = me.y - floor(bbox.height / 2);
705                 }
706             }
707             if (position == 'left') {
708                 x = point[0] - bbox.width - me.dashSize - me.label.padding - 2;
709             }
710             else {
711                 x = point[0] + me.dashSize + me.label.padding + 2;
712             }
713             textLabel.setAttributes(Ext.apply({
714                 hidden: false,
715                 x: x,
716                 y: y
717             }, me.label), true);
718             // Skip label if there isn't available minimum space
719             if (i != 0 &amp;&amp; me.intersect(textLabel, prevLabel)) {
720                 textLabel.hide(true);
721                 continue;
722             }
723             prevLabel = textLabel;
724         }
725
726         return maxWidth;
727     },
728
729 <span id='Ext-chart-axis-Axis-method-drawLabel'>    /**
730 </span>     * Renders the labels in the axes.
731      */
732     drawLabel: function() {
733         var me = this,
734             position = me.position,
735             labelGroup = me.labelGroup,
736             inflections = me.inflections,
737             maxWidth = 0,
738             maxHeight = 0,
739             ln, i;
740
741         if (position == 'left' || position == 'right') {
742             maxWidth = me.drawVerticalLabels();
743         } else {
744             maxHeight = me.drawHorizontalLabels();
745         }
746
747         // Hide unused bars
748         ln = labelGroup.getCount();
749         i = inflections.length;
750         for (; i &lt; ln; i++) {
751             labelGroup.getAt(i).hide(true);
752         }
753
754         me.bbox = {};
755         Ext.apply(me.bbox, me.axisBBox);
756         me.bbox.height = maxHeight;
757         me.bbox.width = maxWidth;
758         if (Ext.isString(me.title)) {
759             me.drawTitle(maxWidth, maxHeight);
760         }
761     },
762
763     // @private creates the elipsis for the text.
764     elipsis: function(sprite, text, desiredWidth, minWidth, center) {
765         var bbox,
766             x;
767
768         if (desiredWidth &lt; minWidth) {
769             sprite.hide(true);
770             return false;
771         }
772         while (text.length &gt; 4) {
773             text = text.substr(0, text.length - 4) + &quot;...&quot;;
774             sprite.setAttributes({
775                 text: text
776             }, true);
777             bbox = sprite.getBBox();
778             if (bbox.width &lt; desiredWidth) {
779                 if (typeof center == 'number') {
780                     sprite.setAttributes({
781                         x: Math.floor(center - (bbox.width / 2))
782                     }, true);
783                 }
784                 break;
785             }
786         }
787         return true;
788     },
789
790 <span id='Ext-chart-axis-Axis-method-setTitle'>    /**
791 </span>     * Updates the {@link #title} of this axis.
792      * @param {String} title
793      */
794     setTitle: function(title) {
795         this.title = title;
796         this.drawLabel();
797     },
798
799     // @private draws the title for the axis.
800     drawTitle: function(maxWidth, maxHeight) {
801         var me = this,
802             position = me.position,
803             surface = me.chart.surface,
804             displaySprite = me.displaySprite,
805             title = me.title,
806             rotate = (position == 'left' || position == 'right'),
807             x = me.x,
808             y = me.y,
809             base, bbox, pad;
810
811         if (displaySprite) {
812             displaySprite.setAttributes({text: title}, true);
813         } else {
814             base = {
815                 type: 'text',
816                 x: 0,
817                 y: 0,
818                 text: title
819             };
820             displaySprite = me.displaySprite = surface.add(Ext.apply(base, me.axisTitleStyle, me.labelTitle));
821             surface.renderItem(displaySprite);
822         }
823         bbox = displaySprite.getBBox();
824         pad = me.dashSize + me.label.padding;
825
826         if (rotate) {
827             y -= ((me.length / 2) - (bbox.height / 2));
828             if (position == 'left') {
829                 x -= (maxWidth + pad + (bbox.width / 2));
830             }
831             else {
832                 x += (maxWidth + pad + bbox.width - (bbox.width / 2));
833             }
834             me.bbox.width += bbox.width + 10;
835         }
836         else {
837             x += (me.length / 2) - (bbox.width * 0.5);
838             if (position == 'top') {
839                 y -= (maxHeight + pad + (bbox.height * 0.3));
840             }
841             else {
842                 y += (maxHeight + pad + (bbox.height * 0.8));
843             }
844             me.bbox.height += bbox.height + 10;
845         }
846         displaySprite.setAttributes({
847             translate: {
848                 x: x,
849                 y: y
850             }
851         }, true);
852     }
853 });
854 </pre>
855 </body>
856 </html>