display & slideshow work, FX not implemented
[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                 }
47         },
48
49         initialize: function(vimgs, container, controls, options){
50                 this.setOptions(this.getOptions(), options);
51                 this.vimgs = vimgs;
52                 this.container = container;
53                 this.controls = controls;
54                 this.controls.registershow(this);
55                 this.timer = 0;
56                 this.delay = this.options.delay;
57                 this.cache = {
58                         prev: {},
59                         curr: {},
60                         next: {},
61                 };
62                 this.updatecoords();
63                 this.previd = -1;
64                 this.ondisplay = new Element('img').
65                         injectInside(this.container.container);
66                 this.loadingdiv = new Element('div').
67                 addClass('loading').setStyles({
68                         position: 'absolute',
69                         top: 0,
70                         left: 0,
71                         zIndex: 3,
72                         display: 'none',
73                         width: this.coords.width,
74                         height: this.coords.height,
75                 }).injectInside(this.container.container);
76
77                 window.addEvent('resize', this.resizer.bind(this))
78         },
79
80         /* event handler for window resize */
81
82         resizer: function(){
83                 this.updatecoords();
84                 var newstyle = this.calcsize(this.cache.curr);
85                 this.ondisplay.setStyles(newstyle);
86                 /* check if we need reload */
87         },
88
89         /* prev, play, stop, next, exit, comm are methods for button presses */
90
91         prev: function(){
92                 if (this.currentid > 0) {
93                         this.show(this.currentid-1);
94                 } else {
95                         alert('show.prev called beyond first element');
96                 }
97         },
98
99         stop: function(){
100                 if (this.isplaying) { $clear(this.timer); }
101                 this.isplaying = false;
102                 $clear(this.timer);
103                 this.controls.running(0);
104         },
105
106         play: function(){
107                 this.isplaying = true;
108                 this.timer = this.autonext.delay(this.delay,this);
109                 this.controls.running(1);
110         },
111
112         next: function(){
113                 if (this.currentid < this.vimgs.length-1) {
114                         this.show(this.currentid+1);
115                 } else {
116                         alert('show.next called beyond last element');
117                 }
118         },
119
120         exit: function(){
121                 if (this.isplaying) { $clear(this.timer); }
122                 this.stopfx();
123                 this.options.cbExit();
124         },
125
126         comm: function(){
127                 /* alert('show.comm called, do nothing'); */
128         },
129
130         /* Entry point: called to start doing things */
131
132         start: function(id, play){
133                 this.options.cbStart();
134                 this.isplaying = play;
135                 this.controls.running(this.isplaying);
136                 this.show(id);
137                 return false; /* to make it usable from href links */
138         },
139
140         /* "Private" methods to do the real job */
141
142         show: function(id){
143                 /* alert('called show.show('+id+')'); */
144                 this.previd = this.currentid;
145                 this.currentid = id;
146                 var newcache = {
147                         prev: (id > 0)?this.prepare(id-1):{},
148                         curr: this.prepare(id),
149                         next: (id < (this.vimgs.length-1))?this.prepare(id+1):{},
150                 };
151                 delete this.cache;
152                 this.cache = newcache;
153                 if (this.cache.curr.ready) {
154                         this.display(this.cache.curr);
155                 } else {
156                         this.pendingload = true;
157                         this.showloading();
158                 }
159                 this.controls.info(id,this.vimgs.length,
160                                 this.vimgs[id][0],
161                                 this.vimgs[id][1]);
162         },
163
164         prepare: function(id){
165                 var vi;
166                 for (vi=0;vi<this.vimgs[id][2].length-1;vi++) {
167                         if ((this.vimgs[id][2][vi][0] >= this.target.width) ||
168                             (this.vimgs[id][2][vi][1] >= this.target.height)) {
169                                 break;
170                         }
171                 }
172                 /* alert('prepare id='+id+', selected '+vi+' at '+
173                         this.vimgs[id][2][vi][0]+'x'+
174                         this.vimgs[id][2][vi][1]); */
175                 var cachel;
176                 ['prev', 'curr', 'next'].each(function(el){
177                         if (this.cache[el] &&
178                             this.cache[el].id == id &&
179                             this.cache[el].vi == vi) {
180                                 cachel = this.cache[el];
181                         }
182                 }.bind(this));
183                 if (! cachel) {
184                         cachel = {
185                                 id: id,
186                                 vi: vi,
187                                 ready: false,
188                                 url: this.vimgs[id][2][vi][2],
189                         };
190                         cachel.img = this.bgload(cachel);
191                 }
192                 return cachel;
193         },
194
195         bgload: function(cachel){
196                 /* alert('bgload: id='+cachel.id+' vi='+cachel.vi+
197                         ' url='+cachel.url); */
198                 return new Asset.image(this.vimgs[cachel.id][2][cachel.vi][2],{
199                         id: this.vimgs[cachel.id][0],
200                         title: this.vimgs[cachel.id][1],
201                         onload: this.loadcomplete.bind(this,[cachel]),
202                 });
203         },
204
205         loadcomplete: function(cachel){
206                 /* alert('loadcomplete '+cachel.url+' id='+cachel.id+
207                         ' vi='+cachel.vi); */
208                 cachel.ready = true;
209                 if (cachel.id == this.currentid &&
210                     this.pendingload) {
211                         this.pendingload = false;
212                         this.hideloading();
213                         this.display(cachel);
214                 }
215         },
216
217         display: function(cachel){
218                 var newstyle = this.calcsize(cachel);
219                 var newimg = cachel.img.clone();
220                 newimg.setStyles(newstyle);
221                 newimg.replaces(this.ondisplay);
222                 this.ondisplay = newimg;
223                 if (this.isplaying) {
224                         this.timer = this.autonext.delay(this.delay,this);
225                 }
226         },
227
228         autonext: function(){
229                 if (this.isplaying) {
230                         if (this.currentid < this.vimgs.length-1) {
231                                 this.show(this.currentid+1);
232                         } else {
233                                 this.exit();
234                         }
235                 }
236         },
237
238         calcsize: function(cachel){
239                 var factor = 1;
240                 var candidate;
241                 candidate = this.target.width /
242                                 this.vimgs[cachel.id][2][cachel.vi][0];
243                 if (factor > candidate) { factor = candidate; }
244                 candidate = this.target.height /
245                                 this.vimgs[cachel.id][2][cachel.vi][1];
246                 if (factor > candidate) { factor = candidate; }
247                 var w = Math.round(this.vimgs[cachel.id][2][cachel.vi][0] *
248                         factor);
249                 var h = Math.round(this.vimgs[cachel.id][2][cachel.vi][1] *
250                         factor);
251                 var t = Math.round((this.coords.height-h)/2);
252                 var l = Math.round((this.coords.width-w)/2);
253                 /* alert('new size: '+w+'x'+h+'+'+l+'+'+t); */
254                 return {
255                         position: 'absolute',
256                         top: t+'px',
257                         left: l+'px',
258                         width: w,
259                         height: h,
260                 };
261         },
262
263         showloading: function(){
264                 this.loadingdiv.setStyle('display', 'block');
265         },
266
267         hideloading: function(){
268                 this.loadingdiv.setStyle('display', 'none');
269         },
270
271         stopfx: function(){
272         },
273
274         updatecoords: function(){
275                 this.coords = this.container.getCoordinates();
276                 this.target = {
277                         width: Math.round(this.coords.width *
278                                                 this.options.percentage / 100),
279                         height: Math.round(this.coords.height *
280                                                 this.options.percentage / 100),
281                 };
282                 /* alert('coords: '+this.coords.width+'x'+this.coords.height+
283                      ', target: '+this.target.width+'x'+this.target.height); */
284         },
285
286 });
287 Show.implement(new Options);
288 Show.implement(new Events);
289