attempt to support utf8 better
[mkgallery.git] / include / show.js
1 /*
2         $Id$
3
4         This is a part of mkgallery.pl suite
5         http://www.average.org/mkgallery/
6
7         Uses mootools (1.2) http://www.mootools.net/
8         Inspired by slideshow http://www.phatfusion.net/slideshow/
9 */
10
11 /*
12         Slideshow
13
14   - On show image: find this and next urls; put in place
15     those that are already here; free unneeded; initiate download of
16     the rest; if needed image is ready then initiate "transitioning", else
17     initiate "loading".
18   - On load complete: if this is the target image, initiate "transitioning".
19   - On "loading": show "loading" image
20   - On "transitioning": hide "loading" image; initiate FX animation to the
21     needed image.
22   - On animation complete: blank previous image; if "playing" then schedule
23     autoswitch to next image in the future.
24   - On autoswitch to next image: if "playing" then switch to next image.
25   - On switch to next image: if next exists, show next image, else show
26     "last image" message.
27   - On switch to prev image: if prev exists, show prev image, else show
28     "first image" message.
29   - On "play": make "playing"; switch to next image.
30   - On "stop": if "playing" cancel autoswitch; break "playing".
31   - On "start show": set up things; set "playing" state; show needed image.
32   - On "stop show": cancel any schedules, hide things.
33   - On resize: recalculate existing image size and position; determine
34     what image is needed; if it is not the one on display then request
35     "show image" for the new image.
36 */
37
38 var Show = new Class({
39
40         getOptions: function(){
41                 return {
42                         cbStart: function(){ alert('show start undefined'); },
43                         cbExit: function(){ alert('show exit undefined'); },
44                         percentage: 98,
45                         delay: 5000,
46                         fxduration: 200
47                 }
48         },
49
50         initialize: function(vimgs, container, controls, options){
51                 this.setOptions(this.getOptions(), options);
52                 this.vimgs = vimgs;
53                 this.container = container;
54                 this.controls = controls;
55                 this.controls.registershow(this);
56                 this.timer = 0;
57                 this.delay = this.options.delay;
58                 this.cache = {
59                         prev: {},
60                         curr: {},
61                         next: {}
62                 };
63 /*
64  *  thescripts.com/forum/thread170365.html
65  */
66                 var hashpos = document.URL.search(/#/);
67                 if (hashpos > 0) {
68                         this.baseurl = document.URL.slice(0,hashpos);
69                 } else {
70                         this.baseurl = document.URL
71                 }
72
73                 this.updatecoords();
74                 this.prevdisplay = new Element('img').
75                         setStyle('opacity', 0);
76                 this.container.grab(this.prevdisplay);
77                 this.ondisplay = this.prevdisplay.clone();
78                 this.container.grab(this.ondisplay);
79                 this.loadingdiv = new Element('div').
80                 addClass('loading').setStyles({
81                         position: 'absolute',
82                         top: 0,
83                         left: 0,
84                         zIndex: 4,
85                         display: 'none',
86                         width: this.coords.width,
87                         height: this.coords.height
88                 });
89                 this.container.grab(this.loadingdiv);
90
91                 window.addEvent('resize', this.resizer.bind(this))
92         },
93
94         /* event handler for window resize */
95
96         resizer: function(){
97                 this.updatecoords();
98                 var newstyle = this.calcsize(this.cache.curr);
99                 this.ondisplay.setStyles(newstyle);
100                 /* check if we need reload */
101         },
102
103         /* prev, play, stop, next, exit, comm are methods for button presses */
104
105         prev: function(){
106                 this.cleartimer();
107                 this.stopfx();
108                 if (this.currentid > 0) {
109                         this.show(this.currentid-1);
110                 } else {
111                         /* alert('show.prev called beyond first element'); */
112                 }
113         },
114
115         stop: function(){
116                 this.cleartimer()
117                 this.isplaying = false;
118                 this.controls.running(0);
119         },
120
121         play: function(){
122                 this.isplaying = true;
123                 this.timer = this.autonext.delay(this.delay,this);
124                 this.controls.running(1);
125         },
126
127         toggleplay: function(){
128                 if (this.isplaying) { this.stop(); }
129                 else { this.play(); }
130         },
131
132         next: function(){
133                 this.cleartimer();
134                 this.stopfx();
135                 if (this.currentid < this.vimgs.length-1) {
136                         this.show(this.currentid+1);
137                 } else {
138                         /* alert('show.next called beyond last element'); */
139                 }
140         },
141
142         exit: function(){
143                 this.cleartimer();
144                 this.stopfx();
145                 this.prevdisplay.setStyle('display', 'none');
146                 this.ondisplay.setStyle('display', 'none');
147                 document.location.href = this.baseurl;
148                 this.options.cbExit();
149         },
150
151         comm: function(){
152                 /* alert('show.comm called, do nothing'); */
153         },
154
155         /* Entry point: called to start doing things */
156
157         start: function(id, play){
158                 this.options.cbStart();
159                 this.isplaying = play;
160                 this.controls.running(this.isplaying);
161                 this.updatecoords();
162                 this.show(id);
163                 return false; /* to make it usable from href links */
164         },
165
166         /* "Private" methods to do the real job */
167
168         show: function(id){
169                 /* alert('called show.show('+id+')'); */
170                 this.currentid = id;
171                 var newcache = {
172                         prev: (id > 0)?this.prepare(id-1):{},
173                         curr: this.prepare(id),
174                         next: (id < (this.vimgs.length-1))?this.prepare(id+1):{}
175                 };
176                 delete this.cache;
177                 this.cache = newcache;
178                 if (this.cache.curr.ready) {
179                         this.display(this.cache.curr);
180                 } else {
181                         this.pendingload = true;
182                         this.showloading();
183                 }
184                 document.location.href = this.baseurl+'#'+this.vimgs[id][0];
185                 this.controls.info(id,this.vimgs.length,
186                                 '#'+this.vimgs[id][0],
187                                 this.vimgs[id][1]);
188         },
189
190         prepare: function(id){
191                 var vi;
192                 for (vi=0;vi<this.vimgs[id][2].length-1;vi++) {
193                         if ((this.vimgs[id][2][vi][0] >= this.target.width) ||
194                             (this.vimgs[id][2][vi][1] >= this.target.height)) {
195                                 break;
196                         }
197                 }
198                 /* alert('prepare id='+id+', selected '+vi+' at '+
199                         this.vimgs[id][2][vi][0]+'x'+
200                         this.vimgs[id][2][vi][1]); */
201                 var cachel;
202                 ['prev', 'curr', 'next'].each(function(el){
203                         if (this.cache[el] &&
204                             this.cache[el].id == id &&
205                             this.cache[el].vi == vi) {
206                                 cachel = this.cache[el];
207                         }
208                 }.bind(this));
209                 if (! cachel) {
210                         cachel = {
211                                 id: id,
212                                 vi: vi,
213                                 ready: false,
214                                 url: this.vimgs[id][2][vi][2]
215                         };
216                         cachel.img = this.bgload(cachel);
217                 }
218                 return cachel;
219         },
220
221         bgload: function(cachel){
222                 /* alert('bgload: id='+cachel.id+' vi='+cachel.vi+
223                         ' url='+cachel.url); */
224                 return new Asset.image(this.vimgs[cachel.id][2][cachel.vi][2],{
225                         id: this.vimgs[cachel.id][0],
226                         title: this.vimgs[cachel.id][1],
227                         onload: this.loadcomplete.bind(this,[cachel])
228                 });
229         },
230
231         loadcomplete: function(cachel){
232                 /* alert('loadcomplete '+cachel.url+' id='+cachel.id+
233                         ' vi='+cachel.vi); */
234                 cachel.ready = true;
235                 if (cachel.id == this.currentid &&
236                     this.pendingload) {
237                         this.pendingload = false;
238                         this.hideloading();
239                         this.display(cachel);
240                 }
241         },
242
243         display: function(cachel){
244                 var newimg = cachel.img.clone().
245                 set('class', 'mainformat').
246                 setProperty('alt', 'Current Image').
247                 setStyles(this.calcsize(cachel)).
248                 setStyles({
249                         zIndex: 3,
250                         opacity: 0
251                 });
252                 this.ondisplay.replaces(this.prevdisplay).
253                 setProperty('alt', 'Previous Image').
254                 setStyle('zIndex', 2);
255                 this.prevdisplay = this.ondisplay;
256                 this.ondisplay = newimg;
257                 this.container.grab(this.ondisplay);
258                 this.effect();
259         },
260
261         effect: function(){
262                 this.fx = new Fx.Tween(this.ondisplay, {
263                         duration: this.options.fxduration
264                 });
265                 this.fx.addEvent('complete',this.displaycomplete.bind(this));
266                 this.fx.start('opacity', 0, 1);
267         },
268
269         displaycomplete: function(){
270                 this.prevdisplay.setStyle('display', 'none');
271                 if (this.isplaying) {
272                         this.timer = this.autonext.delay(this.delay,this);
273                 }
274         },
275
276         autonext: function(){
277                 if (this.isplaying) {
278                         if (this.currentid < this.vimgs.length-1) {
279                                 this.show(this.currentid+1);
280                         } else {
281                                 this.exit();
282                         }
283                 }
284         },
285
286         calcsize: function(cachel){
287                 if (! cachel.url) {
288                         return {
289                                 position: 'absolute',
290                                 top: 0+'px',
291                                 left: 0+'px',
292                                 width: this.coords.width,
293                                 height: this.coords.height
294                         };
295                 }
296                 var factor = 1;
297                 var candidate;
298                 candidate = this.target.width /
299                                 this.vimgs[cachel.id][2][cachel.vi][0];
300                 if (factor > candidate) { factor = candidate; }
301                 candidate = this.target.height /
302                                 this.vimgs[cachel.id][2][cachel.vi][1];
303                 if (factor > candidate) { factor = candidate; }
304                 var w = Math.round(this.vimgs[cachel.id][2][cachel.vi][0] *
305                         factor);
306                 var h = Math.round(this.vimgs[cachel.id][2][cachel.vi][1] *
307                         factor);
308                 var t = Math.round((this.coords.height-h)/2);
309                 var l = Math.round((this.coords.width-w)/2);
310                 /* alert('new size: '+w+'x'+h+'+'+l+'+'+t); */
311                 return {
312                         position: 'absolute',
313                         top: t+'px',
314                         left: l+'px',
315                         width: w,
316                         height: h
317                 };
318         },
319
320         showloading: function(){
321                 this.loadingdiv.setStyles({
322                         display: 'block',
323                         width: this.coords.width,
324                         height: this.coords.height
325                 });
326         },
327
328         hideloading: function(){
329                 this.loadingdiv.setStyle('display', 'none');
330         },
331
332         cleartimer: function(){
333                 if (this.isplaying) { $clear(this.timer); }
334         },
335
336         stopfx: function(){
337                 if (this.fx) this.fx.cancel();
338         },
339
340         updatecoords: function(){
341                 this.coords = this.container.getCoordinates();
342                 this.target = {
343                         width: Math.round(this.coords.width *
344                                                 this.options.percentage / 100),
345                         height: Math.round(this.coords.height *
346                                                 this.options.percentage / 100)
347                 };
348                 /* alert('coords: '+this.coords.width+'x'+this.coords.height+
349                      ', target: '+this.target.width+'x'+this.target.height); */
350         }
351
352 });
353
354 Show.implement(new Options);
355 Show.implement(new Events);
356