Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / src / chart / Label.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @class Ext.chart.Label
17  *
18  * Labels is a mixin whose methods are appended onto the Series class. Labels is an interface with methods implemented
19  * in each of the Series (Pie, Bar, etc) for label creation and label placement.
20  *
21  * The methods implemented by the Series are:
22  *  
23  * - **`onCreateLabel(storeItem, item, i, display)`** Called each time a new label is created.
24  *   The arguments of the method are:
25  *   - *`storeItem`* The element of the store that is related to the label sprite.
26  *   - *`item`* The item related to the label sprite. An item is an object containing the position of the shape
27  *     used to describe the visualization and also pointing to the actual shape (circle, rectangle, path, etc).
28  *   - *`i`* The index of the element created (i.e the first created label, second created label, etc)
29  *   - *`display`* The display type. May be <b>false</b> if the label is hidden
30  *
31  *  - **`onPlaceLabel(label, storeItem, item, i, display, animate)`** Called for updating the position of the label.
32  *    The arguments of the method are:
33  *    - *`label`* The sprite label.</li>
34  *    - *`storeItem`* The element of the store that is related to the label sprite</li>
35  *    - *`item`* The item related to the label sprite. An item is an object containing the position of the shape
36  *      used to describe the visualization and also pointing to the actual shape (circle, rectangle, path, etc).
37  *    - *`i`* The index of the element to be updated (i.e. whether it is the first, second, third from the labelGroup)
38  *    - *`display`* The display type. May be <b>false</b> if the label is hidden.
39  *    - *`animate`* A boolean value to set or unset animations for the labels.
40  */
41 Ext.define('Ext.chart.Label', {
42
43     /* Begin Definitions */
44
45     requires: ['Ext.draw.Color'],
46     
47     /* End Definitions */
48
49     /**
50      * @cfg {String} display
51      * Specifies the presence and position of labels for each pie slice. Either "rotate", "middle", "insideStart",
52      * "insideEnd", "outside", "over", "under", or "none" to prevent label rendering.
53      * Default value: 'none'.
54      */
55
56     /**
57      * @cfg {String} color
58      * The color of the label text.
59      * Default value: '#000' (black).
60      */
61
62     /**
63      * @cfg {String} field
64      * The name of the field to be displayed in the label.
65      * Default value: 'name'.
66      */
67
68     /**
69      * @cfg {Number} minMargin
70      * Specifies the minimum distance from a label to the origin of the visualization.
71      * This parameter is useful when using PieSeries width variable pie slice lengths.
72      * Default value: 50.
73      */
74
75     /**
76      * @cfg {String} font
77      * The font used for the labels.
78      * Defautl value: "11px Helvetica, sans-serif".
79      */
80
81     /**
82      * @cfg {String} orientation
83      * Either "horizontal" or "vertical".
84      * Dafault value: "horizontal".
85      */
86
87     /**
88      * @cfg {Function} renderer
89      * Optional function for formatting the label into a displayable value.
90      * Default value: function(v) { return v; }
91      * @param v
92      */
93
94     //@private a regex to parse url type colors.
95     colorStringRe: /url\s*\(\s*#([^\/)]+)\s*\)/,
96     
97     //@private the mixin constructor. Used internally by Series.
98     constructor: function(config) {
99         var me = this;
100         me.label = Ext.applyIf(me.label || {},
101         {
102             display: "none",
103             color: "#000",
104             field: "name",
105             minMargin: 50,
106             font: "11px Helvetica, sans-serif",
107             orientation: "horizontal",
108             renderer: function(v) {
109                 return v;
110             }
111         });
112
113         if (me.label.display !== 'none') {
114             me.labelsGroup = me.chart.surface.getGroup(me.seriesId + '-labels');
115         }
116     },
117
118     //@private a method to render all labels in the labelGroup
119     renderLabels: function() {
120         var me = this,
121             chart = me.chart,
122             gradients = chart.gradients,
123             items = me.items,
124             animate = chart.animate,
125             config = me.label,
126             display = config.display,
127             color = config.color,
128             field = [].concat(config.field),
129             group = me.labelsGroup,
130             store = me.chart.store,
131             len = store.getCount(),
132             itemLength = (items || 0) && items.length,
133             ratio = itemLength / len,
134             gradientsCount = (gradients || 0) && gradients.length,
135             Color = Ext.draw.Color,
136             gradient, i, count, index, j, k, colorStopTotal, colorStopIndex, colorStop, item, label,
137             storeItem, sprite, spriteColor, spriteBrightness, labelColor, colorString;
138
139         if (display == 'none') {
140             return;
141         }
142
143         for (i = 0, count = 0; i < len; i++) {
144             index = 0;
145             for (j = 0; j < ratio; j++) {
146                 item = items[count];
147                 label = group.getAt(count);
148                 storeItem = store.getAt(i);
149                 
150                 //check the excludes
151                 while(this.__excludes && this.__excludes[index]) {
152                     index++;
153                 }
154
155                 if (!item && label) {
156                     label.hide(true);
157                 }
158
159                 if (item && field[j]) {
160                     if (!label) {
161                         label = me.onCreateLabel(storeItem, item, i, display, j, index);
162                     }
163                     me.onPlaceLabel(label, storeItem, item, i, display, animate, j, index);
164
165                     //set contrast
166                     if (config.contrast && item.sprite) {
167                         sprite = item.sprite;
168                         //set the color string to the color to be set.
169                         if (sprite._endStyle) {
170                             colorString = sprite._endStyle.fill;
171                         }
172                         else if (sprite._to) {
173                             colorString = sprite._to.fill;
174                         }
175                         else {
176                             colorString = sprite.attr.fill;
177                         }
178                         colorString = colorString || sprite.attr.fill;
179                         
180                         spriteColor = Color.fromString(colorString);
181                         //color wasn't parsed property maybe because it's a gradient id
182                         if (colorString && !spriteColor) {
183                             colorString = colorString.match(me.colorStringRe)[1];
184                             for (k = 0; k < gradientsCount; k++) {
185                                 gradient = gradients[k];
186                                 if (gradient.id == colorString) {
187                                     //avg color stops
188                                     colorStop = 0; colorStopTotal = 0;
189                                     for (colorStopIndex in gradient.stops) {
190                                         colorStop++;
191                                         colorStopTotal += Color.fromString(gradient.stops[colorStopIndex].color).getGrayscale();
192                                     }
193                                     spriteBrightness = (colorStopTotal / colorStop) / 255;
194                                     break;
195                                 }
196                             }
197                         }
198                         else {
199                             spriteBrightness = spriteColor.getGrayscale() / 255;
200                         }
201                         if (label.isOutside) {
202                             spriteBrightness = 1;
203                         }
204                         labelColor = Color.fromString(label.attr.color || label.attr.fill).getHSL();
205                         labelColor[2] = spriteBrightness > 0.5 ? 0.2 : 0.8;
206                         label.setAttributes({
207                             fill: String(Color.fromHSL.apply({}, labelColor))
208                         }, true);
209                     }
210                 }
211                 count++;
212                 index++;
213             }
214         }
215         me.hideLabels(count);
216     },
217
218     //@private a method to hide labels.
219     hideLabels: function(index) {
220         var labelsGroup = this.labelsGroup, len;
221         if (labelsGroup) {
222             len = labelsGroup.getCount();
223             while (len-->index) {
224                 labelsGroup.getAt(len).hide(true);
225             }
226         }
227     }
228 });