Upgrade to ExtJS 3.2.1 - Released 04/27/2010
[extjs.git] / src / ext-core / examples / carousel / carousel.js
1 /*!
2  * Ext JS Library 3.2.1
3  * Copyright(c) 2006-2010 Ext JS, Inc.
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 Ext.ns('Ext.ux');
8
9 Ext.ux.Carousel = Ext.extend(Ext.util.Observable, {
10     interval: 3,
11     transitionDuration: 1,
12     transitionType: 'carousel',
13     transitionEasing: 'easeOut',
14     itemSelector: 'img',
15     activeSlide: 0,
16     autoPlay: false,
17     showPlayButton: false,
18     pauseOnNavigate: false,
19     wrap: false,
20     freezeOnHover: false,
21     navigationOnHover: false,
22     hideNavigation: false,
23     width: null,
24     height: null,
25
26     constructor: function(elId, config) {
27         config = config || {};
28         Ext.apply(this, config);
29
30         Ext.ux.Carousel.superclass.constructor.call(this, config);
31
32         this.addEvents(
33             'beforeprev',
34             'prev',
35             'beforenext',
36             'next',
37             'change',
38             'play',
39             'pause',
40             'freeze',
41             'unfreeze'
42         );
43
44         this.el = Ext.get(elId);
45         this.slides = this.els = [];
46         
47         if(this.autoPlay || this.showPlayButton) {
48             this.wrap = true;
49         };
50
51         if(this.autoPlay && typeof config.showPlayButton === 'undefined') {
52             this.showPlayButton = true;
53         }
54
55         this.initMarkup();
56         this.initEvents();
57
58         if(this.carouselSize > 0) {
59             this.refresh();
60         }
61     },
62
63     initMarkup: function() {
64         var dh = Ext.DomHelper;
65         
66         this.carouselSize = 0;
67         var items = this.el.select(this.itemSelector);
68         this.els.container = dh.append(this.el, {cls: 'ux-carousel-container'}, true);
69         this.els.slidesWrap = dh.append(this.els.container, {cls: 'ux-carousel-slides-wrap'}, true);
70
71         this.els.navigation = dh.append(this.els.container, {cls: 'ux-carousel-nav'}, true).hide();
72         this.els.caption = dh.append(this.els.navigation, {tag: 'h2', cls: 'ux-carousel-caption'}, true);
73         this.els.navNext = dh.append(this.els.navigation, {tag: 'a', href: '#', cls: 'ux-carousel-nav-next'}, true);
74         if(this.showPlayButton) {
75             this.els.navPlay = dh.append(this.els.navigation, {tag: 'a', href: '#', cls: 'ux-carousel-nav-play'}, true)
76         }
77         this.els.navPrev = dh.append(this.els.navigation, {tag: 'a', href: '#', cls: 'ux-carousel-nav-prev'}, true);
78
79         // set the dimensions of the container
80         this.slideWidth = this.width || this.el.getWidth(true);
81         this.slideHeight = this.height || this.el.getHeight(true);
82         this.els.container.setStyle({
83             width: this.slideWidth + 'px',
84             height: this.slideHeight + 'px'
85         });
86
87         this.els.caption.setWidth((this.slideWidth - (this.els.navNext.getWidth()*2) - (this.showPlayButton ? this.els.navPlay.getWidth() : 0) - 20) + 'px')
88         
89         items.appendTo(this.els.slidesWrap).each(function(item) {
90             item = item.wrap({cls: 'ux-carousel-slide'});
91             this.slides.push(item);
92             item.setWidth(this.slideWidth + 'px').setHeight(this.slideHeight + 'px');
93         }, this);
94         this.carouselSize = this.slides.length;
95         if(this.navigationOnHover) {
96             this.els.navigation.setStyle('top', (-1*this.els.navigation.getHeight()) + 'px');
97         }
98         this.el.clip();
99     },
100
101     initEvents: function() {
102         this.els.navPrev.on('click', function(ev) {
103             ev.preventDefault();
104             var target = ev.getTarget();
105             target.blur();            
106             if(Ext.fly(target).hasClass('ux-carousel-nav-disabled')) return;
107             this.prev();
108         }, this);
109
110         this.els.navNext.on('click', function(ev) {
111             ev.preventDefault();
112             var target = ev.getTarget();
113             target.blur();
114             if(Ext.fly(target).hasClass('ux-carousel-nav-disabled')) return;
115             this.next();
116         }, this);
117
118         if(this.showPlayButton) {
119             this.els.navPlay.on('click', function(ev){
120                 ev.preventDefault();
121                 ev.getTarget().blur();
122                 if(this.playing) {
123                     this.pause();
124                 }
125                 else {
126                     this.play();
127                 }
128             }, this);
129         };
130
131         if(this.freezeOnHover) {
132             this.els.container.on('mouseenter', function(){
133                 if(this.playing) {
134                     this.fireEvent('freeze', this.slides[this.activeSlide]);
135                     Ext.TaskMgr.stop(this.playTask);
136                 }
137             }, this);
138             this.els.container.on('mouseleave', function(){
139                 if(this.playing) {
140                     this.fireEvent('unfreeze', this.slides[this.activeSlide]);
141                     Ext.TaskMgr.start(this.playTask);
142                 }
143             }, this, {buffer: (this.interval/2)*1000});
144         };
145
146         if(this.navigationOnHover) {
147             this.els.container.on('mouseenter', function(){
148                 if(!this.navigationShown) {
149                     this.navigationShown = true;
150                     this.els.navigation.stopFx(false).shift({
151                         y: this.els.container.getY(),
152                         duration: this.transitionDuration
153                     })
154                 }
155             }, this);
156
157             this.els.container.on('mouseleave', function(){
158                 if(this.navigationShown) {
159                     this.navigationShown = false;
160                     this.els.navigation.stopFx(false).shift({
161                         y: this.els.navigation.getHeight() - this.els.container.getY(),
162                         duration: this.transitionDuration
163                     })
164                 }
165             }, this);
166         }
167
168         if(this.interval && this.autoPlay) {
169             this.play();
170         };
171     },
172
173     prev: function() {
174         if (this.fireEvent('beforeprev') === false) {
175             return;
176         }
177         if(this.pauseOnNavigate) {
178             this.pause();
179         }
180         this.setSlide(this.activeSlide - 1);
181
182         this.fireEvent('prev', this.activeSlide);        
183         return this; 
184     },
185     
186     next: function() {
187         if(this.fireEvent('beforenext') === false) {
188             return;
189         }
190         if(this.pauseOnNavigate) {
191             this.pause();
192         }
193         this.setSlide(this.activeSlide + 1);
194
195         this.fireEvent('next', this.activeSlide);        
196         return this;         
197     },
198
199     play: function() {
200         if(!this.playing) {
201             this.playTask = this.playTask || {
202                 run: function() {
203                     this.playing = true;
204                     this.setSlide(this.activeSlide+1);
205                 },
206                 interval: this.interval*1000,
207                 scope: this
208             };
209             
210             this.playTaskBuffer = this.playTaskBuffer || new Ext.util.DelayedTask(function() {
211                 Ext.TaskMgr.start(this.playTask);
212             }, this);
213
214             this.playTaskBuffer.delay(this.interval*1000);
215             this.playing = true;
216             if(this.showPlayButton) {
217                 this.els.navPlay.addClass('ux-carousel-playing');
218             }
219             this.fireEvent('play');
220         }        
221         return this;
222     },
223
224     pause: function() {
225         if(this.playing) {
226             Ext.TaskMgr.stop(this.playTask);
227             this.playTaskBuffer.cancel();
228             this.playing = false;
229             if(this.showPlayButton) {
230                 this.els.navPlay.removeClass('ux-carousel-playing');
231             }
232             this.fireEvent('pause');
233         }        
234         return this;
235     },
236         
237     clear: function() {
238         this.els.slidesWrap.update('');
239         this.slides = [];
240         this.carouselSize = 0;
241         this.pause();
242         return this;
243     },
244     
245     add: function(el, refresh) {
246         var item = Ext.fly(el).appendTo(this.els.slidesWrap).wrap({cls: 'ux-carousel-slide'});
247         item.setWidth(this.slideWidth + 'px').setHeight(this.slideHeight + 'px');
248         this.slides.push(item);                        
249         if(refresh) {
250             this.refresh();
251         }        
252         return this;
253     },
254     
255     refresh: function() {
256         this.carouselSize = this.slides.length;
257         this.els.slidesWrap.setWidth((this.slideWidth * this.carouselSize) + 'px');
258         if(this.carouselSize > 0) {
259             if(!this.hideNavigation) this.els.navigation.show();
260             this.activeSlide = 0;
261             this.setSlide(0, true);
262         }                
263         return this;        
264     },
265     
266     setSlide: function(index, initial) {
267         if(!this.wrap && !this.slides[index]) {
268             return;
269         }
270         else if(this.wrap) {
271             if(index < 0) {
272                 index = this.carouselSize-1;
273             }
274             else if(index > this.carouselSize-1) {
275                 index = 0;
276             }
277         }
278         if(!this.slides[index]) {
279             return;
280         }
281
282         this.els.caption.update(this.slides[index].child(':first-child', true).title || '');
283         var offset = index * this.slideWidth;
284         if (!initial) {
285             switch (this.transitionType) {
286                 case 'fade':
287                     this.slides[index].setOpacity(0);
288                     this.slides[this.activeSlide].stopFx(false).fadeOut({
289                         duration: this.transitionDuration / 2,
290                         callback: function(){
291                             this.els.slidesWrap.setStyle('left', (-1 * offset) + 'px');
292                             this.slides[this.activeSlide].setOpacity(1);
293                             this.slides[index].fadeIn({
294                                 duration: this.transitionDuration / 2
295                             });
296                         },
297                         scope: this
298                     })
299                     break;
300
301                 default:
302                     var xNew = (-1 * offset) + this.els.container.getX();
303                     this.els.slidesWrap.stopFx(false);
304                     this.els.slidesWrap.shift({
305                         duration: this.transitionDuration,
306                         x: xNew,
307                         easing: this.transitionEasing
308                     });
309                     break;
310             }
311         }
312         else {
313             this.els.slidesWrap.setStyle('left', '0');
314         }
315
316         this.activeSlide = index;
317         this.updateNav();
318         this.fireEvent('change', this.slides[index], index);
319     },
320
321     updateNav: function() {
322         this.els.navPrev.removeClass('ux-carousel-nav-disabled');
323         this.els.navNext.removeClass('ux-carousel-nav-disabled');
324         if(!this.wrap) {
325             if(this.activeSlide === 0) {
326                 this.els.navPrev.addClass('ux-carousel-nav-disabled');
327             }
328             if(this.activeSlide === this.carouselSize-1) {
329                 this.els.navNext.addClass('ux-carousel-nav-disabled');
330             }
331         }
332     }
333 });