Upgrade to ExtJS 3.3.1 - Released 11/30/2010
[extjs.git] / src / ext-core / examples / lightbox / lightbox.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.Lightbox = (function(){
10     var els = {},
11         images = [],
12         activeImage,
13         initialized = false,
14         selectors = [];
15
16     return {
17         overlayOpacity: 0.85,
18         animate: true,
19         resizeSpeed: 8,
20         borderSize: 10,
21         labelImage: "Image",
22         labelOf: "of",
23
24         init: function() {
25             this.resizeDuration = this.animate ? ((11 - this.resizeSpeed) * 0.15) : 0;
26             this.overlayDuration = this.animate ? 0.2 : 0;
27
28             if(!initialized) {
29                 Ext.apply(this, Ext.util.Observable.prototype);
30                 Ext.util.Observable.constructor.call(this);
31                 this.addEvents('open', 'close');
32                 this.initMarkup();
33                 this.initEvents();
34                 initialized = true;
35             }
36         },
37
38         initMarkup: function() {
39             els.shim = Ext.DomHelper.append(document.body, {
40                 tag: 'iframe',
41                 id: 'ux-lightbox-shim'
42             }, true);
43             els.overlay = Ext.DomHelper.append(document.body, {
44                 id: 'ux-lightbox-overlay'
45             }, true);
46             
47             var lightboxTpl = new Ext.Template(this.getTemplate());
48             els.lightbox = lightboxTpl.append(document.body, {}, true);
49
50             var ids =
51                 ['outerImageContainer', 'imageContainer', 'image', 'hoverNav', 'navPrev', 'navNext', 'loading', 'loadingLink',
52                 'outerDataContainer', 'dataContainer', 'data', 'details', 'caption', 'imageNumber', 'bottomNav', 'navClose'];
53
54             Ext.each(ids, function(id){
55                 els[id] = Ext.get('ux-lightbox-' + id);
56             });
57
58             Ext.each([els.overlay, els.lightbox, els.shim], function(el){
59                 el.setVisibilityMode(Ext.Element.DISPLAY)
60                 el.hide();
61             });
62
63             var size = (this.animate ? 250 : 1) + 'px';
64             els.outerImageContainer.setStyle({
65                 width: size,
66                 height: size
67             });
68         },
69
70         getTemplate : function() {
71             return [
72                 '<div id="ux-lightbox">',
73                     '<div id="ux-lightbox-outerImageContainer">',
74                         '<div id="ux-lightbox-imageContainer">',
75                             '<img id="ux-lightbox-image">',
76                             '<div id="ux-lightbox-hoverNav">',
77                                 '<a href="#" id="ux-lightbox-navPrev"></a>',
78                                 '<a href="#" id="ux-lightbox-navNext"></a>',
79                             '</div>',
80                             '<div id="ux-lightbox-loading">',
81                                 '<a id="ux-lightbox-loadingLink"></a>',
82                             '</div>',
83                         '</div>',
84                     '</div>',
85                     '<div id="ux-lightbox-outerDataContainer">',
86                         '<div id="ux-lightbox-dataContainer">',
87                             '<div id="ux-lightbox-data">',
88                                 '<div id="ux-lightbox-details">',
89                                     '<span id="ux-lightbox-caption"></span>',
90                                     '<span id="ux-lightbox-imageNumber"></span>',
91                                 '</div>',
92                                 '<div id="ux-lightbox-bottomNav">',
93                                     '<a href="#" id="ux-lightbox-navClose"></a>',
94                                 '</div>',
95                             '</div>',
96                         '</div>',
97                     '</div>',
98                 '</div>'
99             ];
100         },
101
102         initEvents: function() {
103             var close = function(ev) {
104                 ev.preventDefault();
105                 this.close();
106             };
107
108             els.overlay.on('click', close, this);
109             els.loadingLink.on('click', close, this);
110             els.navClose.on('click', close, this);
111
112             els.lightbox.on('click', function(ev) {
113                 if(ev.getTarget().id == 'ux-lightbox') {
114                     this.close();
115                 }
116             }, this);
117
118             els.navPrev.on('click', function(ev) {
119                 ev.preventDefault();
120                 this.setImage(activeImage - 1);
121             }, this);
122
123             els.navNext.on('click', function(ev) {
124                 ev.preventDefault();
125                 this.setImage(activeImage + 1);
126             }, this);
127         },
128
129         register: function(sel, group) {
130             if(selectors.indexOf(sel) === -1) {
131                 selectors.push(sel);
132
133                 Ext.fly(document).on('click', function(ev){
134                     var target = ev.getTarget(sel);
135
136                     if (target) {
137                         ev.preventDefault();
138                         this.open(target, sel, group);
139                     }
140                 }, this);
141             }
142         },
143
144         open: function(image, sel, group) {
145             group = group || false;
146             this.setViewSize();
147             els.overlay.fadeIn({
148                 duration: this.overlayDuration,
149                 endOpacity: this.overlayOpacity,
150                 callback: function() {
151                     images = [];
152
153                     var index = 0;
154                     if(!group) {
155                         images.push([image.href, image.title]);
156                     }
157                     else {
158                         var setItems = Ext.query(sel);
159                         Ext.each(setItems, function(item) {
160                             if(item.href) {
161                                 images.push([item.href, item.title]);
162                             }
163                         });
164
165                         while (images[index][0] != image.href) {
166                             index++;
167                         }
168                     }
169
170                     // calculate top and left offset for the lightbox
171                     var pageScroll = Ext.fly(document).getScroll();
172
173                     var lightboxTop = pageScroll.top + (Ext.lib.Dom.getViewportHeight() / 10);
174                     var lightboxLeft = pageScroll.left;
175                     els.lightbox.setStyle({
176                         top: lightboxTop + 'px',
177                         left: lightboxLeft + 'px'
178                     }).show();
179
180                     this.setImage(index);
181                     
182                     this.fireEvent('open', images[index]);                                        
183                 },
184                 scope: this
185             });
186         },
187         
188         setViewSize: function(){
189             var viewSize = this.getViewSize();
190             els.overlay.setStyle({
191                 width: viewSize[0] + 'px',
192                 height: viewSize[1] + 'px'
193             });
194             els.shim.setStyle({
195                 width: viewSize[0] + 'px',
196                 height: viewSize[1] + 'px'
197             }).show();
198         },
199
200         setImage: function(index){
201             activeImage = index;
202                       
203             this.disableKeyNav();            
204             if (this.animate) {
205                 els.loading.show();
206             }
207
208             els.image.hide();
209             els.hoverNav.hide();
210             els.navPrev.hide();
211             els.navNext.hide();
212             els.dataContainer.setOpacity(0.0001);
213             els.imageNumber.hide();
214
215             var preload = new Image();
216             preload.onload = (function(){
217                 els.image.dom.src = images[activeImage][0];
218                 this.resizeImage(preload.width, preload.height);
219             }).createDelegate(this);
220             preload.src = images[activeImage][0];
221         },
222
223         resizeImage: function(w, h){
224             var wCur = els.outerImageContainer.getWidth();
225             var hCur = els.outerImageContainer.getHeight();
226
227             var wNew = (w + this.borderSize * 2);
228             var hNew = (h + this.borderSize * 2);
229
230             var wDiff = wCur - wNew;
231             var hDiff = hCur - hNew;
232
233             var afterResize = function(){
234                 els.hoverNav.setWidth(els.imageContainer.getWidth() + 'px');
235
236                 els.navPrev.setHeight(h + 'px');
237                 els.navNext.setHeight(h + 'px');
238
239                 els.outerDataContainer.setWidth(wNew + 'px');
240
241                 this.showImage();
242             };
243             
244             if (hDiff != 0 || wDiff != 0) {
245                 els.outerImageContainer.shift({
246                     height: hNew,
247                     width: wNew,
248                     duration: this.resizeDuration,
249                     scope: this,
250                     callback: afterResize,
251                     delay: 50
252                 });
253             }
254             else {
255                 afterResize.call(this);
256             }
257         },
258
259         showImage: function(){
260             els.loading.hide();
261             els.image.fadeIn({
262                 duration: this.resizeDuration,
263                 scope: this,
264                 callback: function(){
265                     this.updateDetails();
266                 }
267             });
268             this.preloadImages();
269         },
270
271         updateDetails: function(){
272             var detailsWidth = els.data.getWidth(true) - els.navClose.getWidth() - 10;
273             els.details.setWidth((detailsWidth > 0 ? detailsWidth : 0) + 'px');
274             
275             els.caption.update(images[activeImage][1]);
276
277             els.caption.show();
278             if (images.length > 1) {
279                 els.imageNumber.update(this.labelImage + ' ' + (activeImage + 1) + ' ' + this.labelOf + '  ' + images.length);
280                 els.imageNumber.show();
281             }
282
283             els.dataContainer.fadeIn({
284                 duration: this.resizeDuration/2,
285                 scope: this,
286                 callback: function() {
287                     var viewSize = this.getViewSize();
288                     els.overlay.setHeight(viewSize[1] + 'px');
289                     this.updateNav();
290                 }
291             });
292         },
293
294         updateNav: function(){
295             this.enableKeyNav();
296
297             els.hoverNav.show();
298
299             // if not first image in set, display prev image button
300             if (activeImage > 0)
301                 els.navPrev.show();
302
303             // if not last image in set, display next image button
304             if (activeImage < (images.length - 1))
305                 els.navNext.show();
306         },
307
308         enableKeyNav: function() {
309             Ext.fly(document).on('keydown', this.keyNavAction, this);
310         },
311
312         disableKeyNav: function() {
313             Ext.fly(document).un('keydown', this.keyNavAction, this);
314         },
315
316         keyNavAction: function(ev) {
317             var keyCode = ev.getKey();
318
319             if (
320                 keyCode == 88 || // x
321                 keyCode == 67 || // c
322                 keyCode == 27
323             ) {
324                 this.close();
325             }
326             else if (keyCode == 80 || keyCode == 37){ // display previous image
327                 if (activeImage != 0){
328                     this.setImage(activeImage - 1);
329                 }
330             }
331             else if (keyCode == 78 || keyCode == 39){ // display next image
332                 if (activeImage != (images.length - 1)){
333                     this.setImage(activeImage + 1);
334                 }
335             }
336         },
337
338         preloadImages: function(){
339             var next, prev;
340             if (images.length > activeImage + 1) {
341                 next = new Image();
342                 next.src = images[activeImage + 1][0];
343             }
344             if (activeImage > 0) {
345                 prev = new Image();
346                 prev.src = images[activeImage - 1][0];
347             }
348         },
349
350         close: function(){
351             this.disableKeyNav();
352             els.lightbox.hide();
353             els.overlay.fadeOut({
354                 duration: this.overlayDuration
355             });
356             els.shim.hide();
357             this.fireEvent('close', activeImage);
358         },
359
360         getViewSize: function() {
361             return [Ext.lib.Dom.getViewWidth(), Ext.lib.Dom.getViewHeight()];
362         }
363     }
364 })();
365
366 Ext.onReady(Ext.ux.Lightbox.init, Ext.ux.Lightbox);