Upgrade to ExtJS 4.0.7 - Released 10/19/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 to the Series class. Labels methods are implemented
19  * in each of the Series (Pie, Bar, etc) for label creation and 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 {Object} label
51      * Object with the following properties:
52      *
53      * - **display** : String
54      *
55      *   Specifies the presence and position of labels for each pie slice. Either "rotate", "middle", "insideStart",
56      *   "insideEnd", "outside", "over", "under", or "none" to prevent label rendering.
57      *   Default value: 'none'.
58      *
59      * - **color** : String
60      *
61      *   The color of the label text.
62      *   Default value: '#000' (black).
63      *
64      * - **contrast** : Boolean
65      *
66      *   True to render the label in contrasting color with the backround.
67      *   Default value: false.
68      *
69      * - **field** : String
70      *
71      *   The name of the field to be displayed in the label.
72      *   Default value: 'name'.
73      *
74      * - **minMargin** : Number
75      *
76      *   Specifies the minimum distance from a label to the origin of the visualization.
77      *   This parameter is useful when using PieSeries width variable pie slice lengths.
78      *   Default value: 50.
79      *
80      * - **font** : String
81      *
82      *   The font used for the labels.
83      *   Default value: "11px Helvetica, sans-serif".
84      *
85      * - **orientation** : String
86      *
87      *   Either "horizontal" or "vertical".
88      *   Dafault value: "horizontal".
89      *
90      * - **renderer** : Function
91      *
92      *   Optional function for formatting the label into a displayable value.
93      *   Default value: function(v) { return v; }
94      */
95
96     //@private a regex to parse url type colors.
97     colorStringRe: /url\s*\(\s*#([^\/)]+)\s*\)/,
98
99     //@private the mixin constructor. Used internally by Series.
100     constructor: function(config) {
101         var me = this;
102         me.label = Ext.applyIf(me.label || {},
103         {
104             display: "none",
105             color: "#000",
106             field: "name",
107             minMargin: 50,
108             font: "11px Helvetica, sans-serif",
109             orientation: "horizontal",
110             renderer: function(v) {
111                 return v;
112             }
113         });
114
115         if (me.label.display !== 'none') {
116             me.labelsGroup = me.chart.surface.getGroup(me.seriesId + '-labels');
117         }
118     },
119
120     //@private a method to render all labels in the labelGroup
121     renderLabels: function() {
122         var me = this,
123             chart = me.chart,
124             gradients = chart.gradients,
125             items = me.items,
126             animate = chart.animate,
127             config = me.label,
128             display = config.display,
129             color = config.color,
130             field = [].concat(config.field),
131             group = me.labelsGroup,
132             groupLength = (group || 0) && group.length,
133             store = me.chart.store,
134             len = store.getCount(),
135             itemLength = (items || 0) && items.length,
136             ratio = itemLength / len,
137             gradientsCount = (gradients || 0) && gradients.length,
138             Color = Ext.draw.Color,
139             hides = [],
140             gradient, i, count, groupIndex, index, j, k, colorStopTotal, colorStopIndex, colorStop, item, label,
141             storeItem, sprite, spriteColor, spriteBrightness, labelColor, colorString;
142
143         if (display == 'none') {
144             return;
145         }
146         // no items displayed, hide all labels
147         if(itemLength == 0){
148             while(groupLength--)
149                 hides.push(groupLength);
150         }else{
151             for (i = 0, count = 0, groupIndex = 0; i < len; i++) {
152                 index = 0;
153                 for (j = 0; j < ratio; j++) {
154                     item = items[count];
155                     label = group.getAt(groupIndex);
156                     storeItem = store.getAt(i);
157                     //check the excludes
158                     while(this.__excludes && this.__excludes[index] && ratio > 1) {
159                         if(field[j]){
160                             hides.push(groupIndex);
161                         }
162                         index++;
163
164                     }
165
166                     if (!item && label) {
167                         label.hide(true);
168                         groupIndex++;
169                     }
170
171                     if (item && field[j]) {
172                         if (!label) {
173                             label = me.onCreateLabel(storeItem, item, i, display, j, index);
174                         }
175                         me.onPlaceLabel(label, storeItem, item, i, display, animate, j, index);
176                         groupIndex++;
177
178                         //set contrast
179                         if (config.contrast && item.sprite) {
180                             sprite = item.sprite;
181                             //set the color string to the color to be set.
182                             if (sprite._endStyle) {
183                                 colorString = sprite._endStyle.fill;
184                             }
185                             else if (sprite._to) {
186                                 colorString = sprite._to.fill;
187                             }
188                             else {
189                                 colorString = sprite.attr.fill;
190                             }
191                             colorString = colorString || sprite.attr.fill;
192
193                             spriteColor = Color.fromString(colorString);
194                             //color wasn't parsed property maybe because it's a gradient id
195                             if (colorString && !spriteColor) {
196                                 colorString = colorString.match(me.colorStringRe)[1];
197                                 for (k = 0; k < gradientsCount; k++) {
198                                     gradient = gradients[k];
199                                     if (gradient.id == colorString) {
200                                         //avg color stops
201                                         colorStop = 0; colorStopTotal = 0;
202                                         for (colorStopIndex in gradient.stops) {
203                                             colorStop++;
204                                             colorStopTotal += Color.fromString(gradient.stops[colorStopIndex].color).getGrayscale();
205                                         }
206                                         spriteBrightness = (colorStopTotal / colorStop) / 255;
207                                         break;
208                                     }
209                                 }
210                             }
211                             else {
212                                 spriteBrightness = spriteColor.getGrayscale() / 255;
213                             }
214                             if (label.isOutside) {
215                                 spriteBrightness = 1;
216                             }
217                             labelColor = Color.fromString(label.attr.color || label.attr.fill).getHSL();
218                             labelColor[2] = spriteBrightness > 0.5 ? 0.2 : 0.8;
219                             label.setAttributes({
220                                 fill: String(Color.fromHSL.apply({}, labelColor))
221                             }, true);
222                         }
223
224                     }
225                     count++;
226                     index++;
227                 }
228             }
229         }
230         me.hideLabels(hides);
231     },
232     hideLabels: function(hides){
233         var labelsGroup = this.labelsGroup,
234             hlen = hides.length;
235         while(hlen--)
236             labelsGroup.getAt(hides[hlen]).hide(true);
237     }
238 });