Upgrade to ExtJS 3.3.1 - Released 11/30/2010
[extjs.git] / src / ext-core / examples / rating / rating.js
1 /*!
2  * Ext JS Library 3.3.1
3  * Copyright(c) 2006-2010 Sencha Inc.
4  * licensing@sencha.com
5  * http://www.sencha.com/license
6  */
7 Ext.ns('Ext.ux');
8
9 Ext.ux.Rating = Ext.extend(Ext.util.Observable, {
10         // Configuration options
11     starWidth: 16,
12     split: 1,
13     resetValue: '',
14         defaultSelected: -1,    
15     selected: -1,
16     showTitles: true,    
17     
18         // Our class constructor
19     constructor : function(element, config) {
20         Ext.apply(this, config);
21         Ext.ux.Rating.superclass.constructor.call(this);
22         
23         this.addEvents(
24             'change',
25             'reset'
26         );
27         
28         this.el = Ext.get(element);
29         this.init();
30     },
31     
32     init : function() {
33         var me = this;
34         
35                 // Some arrays we are going to store data in
36         this.values = [];
37         this.titles = [];
38         this.stars = [];
39                 
40                 // We create a container to put all our stars into      
41         this.container = this.el.createChild({
42             cls: 'ux-rating-container ux-rating-clearfix'
43         });
44                                 
45         if(this.canReset) {
46             this.resetEl = this.container.createChild({
47                 cls: 'ux-rating-reset',
48                 cn: [{
49                     tag: 'a',
50                     title: this.showTitles ? (this.resetTitle || 'Reset your vote') : '',
51                     html: 'Reset'
52                 }]
53             });
54             this.resetEl.visibilityMode = Ext.Element.DISPLAY;
55             
56             this.resetEl.hover(function() {
57                 Ext.fly(this).addClass('ux-rating-reset-hover');
58             }, function() {
59                 Ext.fly(this).removeClass('ux-rating-reset-hover');
60             });
61             
62             this.resetEl.on('click', this.reset, this);
63         }
64
65                 // We use DomQuery to select the radio buttons
66                 this.radioBoxes = this.el.select('input[type=radio]');
67                 // Then we can loop over the CompositeElement using each
68         this.radioBoxes.each(this.initStar, this);
69         
70                 // We use DomHelper to create our hidden input
71                 this.input = this.container.createChild({
72                         tag: 'input',
73                         type: 'hidden',
74                         name: this.name,
75                         value: this.values[this.defaultSelected] || this.resetValue
76                 });
77                 
78                 // Lets remove all the radio buttons from the DOM
79                 this.radioBoxes.remove();
80                 
81         this.select(this.defaultSelected === undefined ? false : this.defaultSelected)
82
83         if(this.disabled) {
84             this.disable();
85         } else {
86                         // Enable will set up our event listeners
87             this.enable();
88         }
89     },
90
91     initStar : function(item, all, i) {
92         var sw = Math.floor(this.starWidth / this.split);
93         
94                 // We use the name and disabled attributes of the first radio button 
95                 if(i == 0) {
96                 this.name = item.dom.name;
97                 this.disabled = item.dom.disabled;              
98                 }
99                 
100                 // Saving the value and title for this star
101         this.values[i] = item.dom.value;
102         this.titles[i] = item.dom.title;
103
104         if(item.dom.checked) {
105             this.defaultSelected = i;
106         }
107         
108                 // Now actually create the star!
109         var star = this.container.createChild({
110             cls: 'ux-rating-star'
111         });
112         
113         var starLink = star.createChild({
114             tag: 'a',
115             html: this.values[i],
116             title: this.showTitles ? this.titles[i] : ''
117         });
118         
119         // Prepare division settings
120         if(this.split) {
121           var odd = (i % this.split);              
122           star.setWidth(sw);
123           starLink.setStyle('margin-left', '-' + (odd * sw) + 'px');
124         }
125
126                 // Save the reference to this star so we can easily access it later
127         this.stars.push(star.dom);
128     },
129     
130     onStarClick : function(ev, t) {
131         if(!this.disabled) {
132             this.select(this.stars.indexOf(t));
133         }
134     },
135
136     onStarOver : function(ev, t) {
137         if(!this.disabled) {
138             this.fillTo(this.stars.indexOf(t), true);
139         }        
140     },
141     
142     onStarOut : function(ev, t) {
143         if(!this.disabled) {
144             this.fillTo(this.selected, false);            
145         }
146     },
147     
148     reset : function(ev, t) {
149         this.select(-1);
150     },
151     
152     select : function(index) {
153         if(index === false || index === -1) {
154             // remove current selection in el
155             this.value = this.resetValue;
156             this.title = "";
157                         this.input.dom.value = '';    
158             
159             if(this.canReset) {
160                 this.resetEl.setOpacity(0.5);
161             }
162             
163             this.fillNone();
164             
165             if(this.selected !== -1) {
166                 this.fireEvent('change', this, this.values[index], this.stars[index]);              
167             }
168             this.selected = -1;            
169         }
170         else if(index !== this.selected) {
171                         // Update some properties         
172             this.selected = index;
173             this.value = this.values[index];
174             this.title = this.titles[index];
175                         
176                         // Set the value of our hidden input so the rating can be submitted                     
177                         this.input.dom.value = this.value;
178                         
179             if(this.canReset) {
180                 this.resetEl.setOpacity(0.99);
181             }
182             
183                         // the fillTo() method will fill the stars up until the selected one        
184             this.fillTo(index, false);
185             
186                         // Lets also not forget to fire our custom event!
187             this.fireEvent('change', this, this.values[index], this.stars[index]);            
188         }     
189     },
190     
191     fillTo : function(index, hover) {
192         if (index != -1) {
193             var addClass = hover ? 'ux-rating-star-hover' : 'ux-rating-star-on';
194             var removeClass = hover ? 'ux-rating-star-on' : 'ux-rating-star-hover';
195              
196                         // We add a css class to each star up until the selected one   
197             Ext.each(this.stars.slice(0, index+1), function() {
198                 Ext.fly(this).removeClass(removeClass).addClass(addClass);
199             });
200
201                         // And then remove the same class from all the stars after this one
202             Ext.each(this.stars.slice(index+1), function() {
203                 Ext.fly(this).removeClass([removeClass, addClass]);
204             });
205         }
206         else {
207             this.fillNone();
208         }        
209     },
210     
211     fillNone : function() {
212         this.container.select('.ux-rating-star').removeClass(['ux-rating-star-hover', 'ux-rating-star-on']);
213     },
214
215     enable : function() {               
216         if(this.canReset) {
217             this.resetEl.show();
218         }
219         
220         this.input.dom.disabled = null;
221         this.disabled = false;
222         
223         this.container.removeClass('ux-rating-disabled');
224  
225                 // We will be using the technique of event delegation by listening
226         // for bubbled up events on the container       
227         this.container.on({
228             click: this.onStarClick, 
229             mouseover: this.onStarOver,
230             mouseout: this.onStarOut,
231             scope: this,
232             delegate: 'div.ux-rating-star'
233         });        
234     },
235             
236     disable : function() {
237         if(this.canReset) {
238             this.resetEl.hide();
239         }
240         
241         this.input.dom.disabled = true;
242         this.disabled = true;
243         
244         this.container.addClass('ux-rating-disabled');
245
246         this.container.un({
247             click: this.onStarClick, 
248             mouseover: this.onStarOver,
249             mouseout: this.onStarOut,
250             scope: this,
251             delegate: 'div.ux-rating-star'
252         });        
253     },
254     
255     getValue : function() {
256         return this.values[this.selected] || this.resetValue;
257     },
258         
259         destroy : function() {
260                 this.disable();
261                 this.container.remove();
262                 this.radioBoxes.appendTo(this.el);
263                 if(this.selected !== -1) {
264                         this.radioBoxes.elements[this.selected].checked = true;
265                 }
266         }
267 });