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