Fix the CSS a bit.
[hacks/simpleWebSlides.git] / simpleWebSlides.js
1 /*
2   Namespace object.
3 */
4
5 var SWS = SWS || {};
6
7
8
9 SWS.Utils = new function () {
10     var self = this;
11
12     self.isUndefined = function (o) { return typeof o == "undefined"; };
13     self.push2 = function (t, i, v) {
14         if ((typeof t[i]) == 'undefined') {
15             t[i] = new Array();
16         };
17         var l = t[i].length;
18         t[i][l] = v;
19     };
20
21     self.isEmpty = function (o) {
22         for(var _ in o) return false;
23         return true;
24     };
25
26     self.parseFrameSpec = function (s) {
27         var elems = s.split("_");
28         var result = {};
29         var min_last = 10000;
30         var max_value = -1;
31         for(var i = 0; i < elems.length; i++){
32             var bounds = elems[i].split("-");
33             if (bounds.length > 2 || bounds.length == 0) return {};
34             if (bounds.length == 1) bounds[1] = bounds[0];
35             var a = parseInt(bounds[0]);
36             var b = parseInt(bounds[1])
37             if (!isFinite(a) || !isFinite(b)) return {};
38             a = Math.min(a, 1000); // don't allow more than 1000 frames/slide
39             b = Math.min(b, 1000);
40             if (b > max_value) max_value = b;
41             for (var j = a; j <= b; j++)
42                 result[j] = true;
43         };
44         return result;
45     };
46
47
48 };
49
50
51
52
53 SWS.Templates = new function () {
54     var self = this;
55     self.controlPanel = "<div id='sws-control-panel'>\
56 <a onclick='SWS.Presentation.goToSlide(SWS.Presentation.firstSlide());'>◀◀◀</a>\
57 <a onclick='SWS.Presentation.previousSlide();SWS.Presentation.refresh();'>◀◀ </a>\
58 <a onclick='SWS.Presentation.previous();SWS.Presentation.refresh();'>◀</a>\
59 <a onclick='SWS.Presentation.next();SWS.Presentation.refresh();'>▶</a>\
60 <a onclick='SWS.Presentation.nextSlide();SWS.Presentation.refresh();'>▶▶</a>\
61 <a rel='Last slide' onclick='SWS.Presentation.goToSlide(SWS.Presentation.lastSlide());'>▶▶▶</a>\
62 </div>";
63     self.slideActivate = function (o) {
64         if (!(o.hasClass("sws-active-slide"))){
65             o.removeClass("sws-inactive-slide").addClass("sws-active-slide");
66         };
67     };
68
69     self.slideDeactivate = function (o) {
70         if (!(o.hasClass("sws-inactive-slide"))){
71             o.removeClass("sws-active-slide").addClass("sws-inactive-slide");
72         };
73     };
74
75     self.slideChange = function (from, to) {
76         var canvas = $(".sws-canvas");
77         self.slideDeactivate($(canvas[from]));
78         self.slideActivate($(canvas[to]));
79     };
80
81     self.objectActivate = function (o) {
82         if (!(o.hasClass("sws-active-object"))){
83             o.removeClass("sws-inactive-object").addClass("sws-active-object");
84             o.css({'visibility':'visible'});
85             return true;
86         };
87         return false;
88     };
89
90     self.objectDeactivate = function (o) {
91         if (!(o.hasClass("sws-inactive-object"))){
92             o.addClass("sws-inactive-object").removeClass("sws-active-object");
93             return true;
94         };
95         return false;
96     };
97
98     self.updateFooter = function (o) {
99         var footer = o.find(".sws-footer");
100         if (footer.length && (footer.children("*").length == 0)) {
101             var i = SWS.Presentation.getCurrentSlide();
102             var cur = $( "<span class='sws-current-slide-number'>"
103                          + (i + 1)
104                          +"</span>");
105             var sep = $( "<span class='sws-slide-num-sep' />");
106             var tot = $( "<span class='sws-current-slide-number'>"
107                          + (SWS.Presentation.getNumSlides())
108                          +"</span>");
109             footer.append(cur).append(sep).append(tot);
110         };
111     };
112     self.updateHeader = function (o) {};
113 };
114 SWS.ConfigBuilder = function () {
115     var self = this;
116     self['sws-object-activate'] = SWS.Templates.objectActivate;
117     self['sws-object-deactivate'] = SWS.Templates.objectDeactivate;
118     self['sws-slide-change'] = SWS.Templates.slideChange;
119     self['sws-update-footer'] = SWS.Templates.updateFooter;
120     self['sws-update-header'] = SWS.Templates.updateHeader;
121 };
122
123 SWS.Defaults = new SWS.ConfigBuilder ();
124
125 SWS.Config = new SWS.ConfigBuilder ();
126
127
128 SWS.Effects = new function () {
129     var self = this;
130
131     self.objectDeactivateFadeOut = function (o) {
132         o.animate({'opacity': '0'}, 200,
133                   function () { SWS.Templates.objectDeactivate(o)});
134     };
135
136     self.objectActivateFadeIn = function (o) {
137
138         if (SWS.Templates.objectActivate(o)){
139             o.animate({'opacity': '1' }, 200);
140         };
141
142     };
143
144     self.slideChangeHorizontalFlip = function (from, to){
145         var f = SWS.Presentation.getSlide(from);
146         var t = SWS.Presentation.getSlide(to);
147         f.animate({ 'left': '50%', 'width': '0pt', 'opacity':'0' }, 150,
148                   function  () {
149                       SWS.Templates.slideDeactivate(f);
150                       f.css({'left':'0%', 'width': '100%'});
151                       t.css({ 'left': '50%', 'width': '0pt','opacity':'0' });
152                       SWS.Templates.slideActivate(t);
153                       t.animate({'left':'0%', 'width': '100%','opacity':'1'});
154                   });
155     };
156     self.slideChangeFadeOutIn = function (from, to) {
157         var f = SWS.Presentation.getSlide(from);
158         var t = SWS.Presentation.getSlide(to);
159         f.animate({ 'opacity': '0'}, 150,
160                   function () { SWS.Templates.slideDeactivate(f);
161                                 SWS.Templates.slideActivate(t);
162                                 t.css('opacity', '0');
163                                 t.animate({ 'opacity': '1'}, 150);
164                               });
165     };
166     self.slideChangeHorizontalSlide = function (from, to) {
167         var f = SWS.Presentation.getSlide(from);
168         var t = SWS.Presentation.getSlide(to);
169         if (from < to) {
170             t.css('left', '100%');
171             SWS.Templates.slideActivate(t);
172             f.animate({ 'left': '-100%' }, 250, function () { SWS.Templates.slideDeactivate(f); });
173             t.animate({ 'left': '0%' }, 250);
174         } else {
175             t.css('left', '-100%');
176             SWS.Templates.slideActivate(t);
177             f.animate({ 'left': '100%' }, 250, function () { SWS.Templates.slideDeactivate(f); });
178             t.animate({ 'left': '0%' }, 250);
179         };
180     };
181
182
183     self.slideChangeVerticalSlide = function (from, to) {
184         var f = SWS.Presentation.getSlide(from);
185         var t = SWS.Presentation.getSlide(to);
186         if (from < to) {
187             t.css('top', '100%');
188             SWS.Templates.slideActivate(t);
189             f.animate({ 'top': '-100%' }, 250, function () { SWS.Templates.slideDeactivate(f); });
190             t.animate({ 'top': '0%' }, 250);
191         } else {
192             t.css('top', '-100%');
193             SWS.Templates.slideActivate(t);
194             f.animate({ 'top': '100%' }, 250, function () { SWS.Templates.slideDeactivate(f); });
195             t.animate({ 'top': '0%' }, 250);
196         };
197     };
198
199 };
200
201 SWS.Presentation = new function () {
202
203
204     var self = this;
205
206     //TODO move outside of the Presentation object
207
208
209     var _total_slides;
210     var _initialized = false;
211     var _disable_input_events = false;
212
213     var _slide_callbacks = new Array ();
214
215
216     self.getNumSlides = function () { return _total_slides; };
217
218     self.getSlide = function(i) {
219         return $($(".sws-canvas")[i]);
220     };
221
222     self.registerCallback = function (i, f) {
223         if (_initialized) return;
224         //jQuery does not seem to work well
225         //on a partial DOM
226
227         var slide_num = $(".sws-slide").length - 1;
228
229         SWS.Utils.push2(_slide_callbacks, slide_num,{ 'fn': f, 'frame': i });
230
231     };
232
233     if (typeof(Storage)!=="undefined"){
234         self.getCurrentSlide = function () {
235             //unary + casts to integer
236             var i = +(sessionStorage.getItem("current_slide"));
237             if (!(i >= 0 && i < self.getNumSlides())){
238                 return 0;
239             } else {
240                 return i;
241             };
242         };
243
244         self.setCurrentSlide = function (i) {
245             sessionStorage.setItem("current_slide", i);
246         };
247
248     } else {
249         var _current_slide = 0;
250         self.getCurrentSlide = function () { return _current_slide; };
251         self.setCurrentSlide = function (i) { _current_slide = i; };
252
253     };
254     self.firstSlide = function () { return 0; };
255     self.lastSlide = function () { return self.getNumSlides() - 1; };
256     self.refresh = function () {
257         /* block upcoming input event until all animations are finished */
258         _disable_input_events = true;
259
260         var canvas = $(".sws-canvas");
261         var from_slide_num = canvas.index($(".sws-active-slide"));
262         var to_slide_num = self.getCurrentSlide();
263         var watch_slide_anim = false;
264         var to_slide = $(canvas[to_slide_num]);
265         var slide_change = (from_slide_num != to_slide_num);
266
267         var info = to_slide.data("sws-frame-info");
268         if (slide_change) {
269             //Launch a slide transition:
270             SWS.Config['sws-slide-change'](from_slide_num, to_slide_num);
271             watch_slide_anim = true;
272             SWS.Config['sws-update-header'](to_slide);
273             SWS.Config['sws-update-footer'](to_slide);
274             for (var i = 0; i < info.callbacks.at_slide.length;i++){
275                 info.callbacks.at_slide[i](to_slide);
276             };
277         };
278
279
280         var cur = info.current;
281         var custom = info.custom;
282         var real_slide = to_slide.children(".sws-slide");
283
284         to_slide.children(".sws-slide").find("*").andSelf().each(function (i){
285             var frameset = $(this).data("sws-frame-set") || {};
286             if (frameset[cur])
287                 SWS.Config['sws-object-activate']($(this));
288             else
289                 SWS.Config['sws-object-deactivate']($(this));
290         });
291
292         var callbacks;
293         if (callbacks = info.callbacks.at_frame[self.getCurrentFrame()]){
294             for (var k = 0; k < callbacks.length; k++)
295                 callbacks[k]($(to_slide));
296         };
297
298         var to_watch = $(to_slide).find("*");
299         if (watch_slide_anim) {
300             to_watch = to_watch.add(to_slide).add($(canvas[from_slide_num]));
301         };
302
303         to_watch.find("*").promise().done(function() {
304             _disable_input_events = false;
305         });
306     };
307
308     self.nextSlide = function () {
309         self.setCurrentSlide(Math.min(self.getCurrentSlide()+1,
310                                       self.lastSlide()));
311     };
312
313     self.previousSlide = function () {
314         self.setCurrentSlide(Math.max(self.getCurrentSlide()-1,
315                                       self.firstSlide()));
316     };
317
318     self.getFrameInfo = function () {
319
320         var i = self.getCurrentSlide();
321         var canvas = $($(".sws-canvas")[i]);
322         var infos = canvas.data("sws-frame-info");
323         return infos;
324     };
325     self.getCurrentFrame = function () { return self.getFrameInfo().current; };
326     self.setCurrentFrame = function (i) { self.getFrameInfo().current = i; };
327     self.firstFrame = function () { return 0; };
328     self.lastFrame = function () { return self.getFrameInfo().last; };
329
330     self.nextFrame = function () {
331         self.setCurrentFrame(Math.min(self.getCurrentFrame()+1,
332                                       self.lastFrame()));
333
334     };
335     self.previousFrame = function () {
336         self.setCurrentFrame(Math.max(self.getCurrentFrame()-1,
337                                        self.firstFrame()));
338     };
339
340     self.next = function () {
341         var i = self.getCurrentFrame();
342         if (i == self.lastFrame())
343             self.nextSlide();
344         else
345             self.nextFrame();
346     };
347
348     self.previous = function () {
349         var i = self.getCurrentFrame();
350         if (i == self.firstFrame()){
351             self.previousSlide();
352             self.setCurrentFrame(self.lastFrame());
353         }
354         else
355             self.previousFrame();
356     };
357
358     self.goToSlide = function (s, f) {
359         if (SWS.Utils.isUndefined(f))
360             f = 0;
361         if (!(s >= self.firstSlide() && s <= self.lastSlide())) return;
362         self.setCurrentSlide(s);
363         if (!(f >= self.firstFrame() && s <= self.lastFrame())) f = 0;
364         self.setCurrentFrame(f);
365         self.refresh();
366     };
367
368
369     self.inputHandler = function (event) {
370         if (_disable_input_events) return;
371         switch (event.which) {
372         case 36:/* Home */
373             self.setCurrentSlide(self.firstSlide());
374             break;
375
376         case 35:/* End */
377             self.setCurrentSlide(self.lastSlide());
378             break;
379
380         case 32: /* space */
381         case 39: /* -> */
382
383             self.next();
384             break;
385         case 34: /* PgDown */
386         case 78: /* n */
387             self.nextSlide();
388             break;
389         case 8: /* backspace */
390         case 37: /* <-   */
391             self.previous();
392             break;
393         case 33: /* PgUp */
394         case 80: /* p */
395             self.previousSlide();
396             break;
397         case 67: /* c */
398             $("#sws-control-panel").toggle();
399         default:
400             return;
401         };
402         self.refresh();
403     };
404
405
406
407     function init_canvas(canvas, custom) {
408         var cur_frame = 0;
409         var last_frame = canvas.find(".sws-pause").length;
410         //Add all regular elements to the frame list
411         var slide = $(canvas.children(".sws-slide")[0]);
412
413         var callbacks = { at_slide : new Array(),
414                           at_frame : new Array() }
415
416         if (SWS.Utils.isUndefined(custom)) {
417             custom = new Array ();
418         };
419
420         for (var i = 0; i < custom.length; i++) {
421             if (isFinite(custom[i].frame)){
422                 var num = +(custom[i].frame);
423                 if (num > last_frame) last_frame = num;
424                 SWS.Utils.push2(callbacks.at_frame, num, custom[i].fn);
425             } else if (custom[i].frame == "slide")
426                 callbacks.at_slide.push(custom[i].fn);
427             else {
428                 var frame_set = SWS.Utils.parseFrameSpec(custom[i].frame);
429                 for(var f in frame_set){
430                     if (f > last_frame) last_frame = f;
431                     SWS.Utils.push2(callbacks.at_frame, +(f), custom[i].fn);
432                 };
433             }
434         };
435
436         var specials = null;
437
438         slide.find('*[class*="sws-onframe-"]').each(function(_){
439             var cls = $(this).attr('class');
440             var idx = cls.indexOf("sws-onframe-");
441             if (idx >= 0) {
442                 var end = cls.indexOf(" ", idx);
443                 end = (end == -1) ? cls.length : end;
444                 var spec = cls.substring(idx+12, end);
445                 var o = SWS.Utils.parseFrameSpec(spec);
446                 for(var f in o)
447                     if (f > last_frame) last_frame = f;
448                 $(this).find("*").andSelf().each(function(_){
449                     if (!SWS.Utils.isEmpty(o))
450                         $(this).data("sws-frame-set", o);
451                     if (specials)
452                         specials.add($(this));
453                     else
454                         specials = $(this);
455                 });
456             };
457         });
458
459         slide.find("*").andSelf().not(specials).each(function(i) {
460             if ($(this).hasClass("sws-pause"))  cur_frame++;
461             var o = {};
462             for (var j = cur_frame; j <= last_frame; j++)
463                 o[ j ] = true;
464             if (!SWS.Utils.isEmpty(o))
465                 $(this).data("sws-frame-set", o);
466         });
467
468         canvas.data("sws-frame-info", { current: 0,
469                                         last: last_frame,
470                                         callbacks: callbacks
471                                       });
472
473     };
474
475     self.init = function () {
476         console.log("inited");
477         $(window).bind('storage', function (e) {
478             console.log(e);
479         });
480
481         _total_slides = $(".sws-slide").length;
482
483         $(document).keydown(self.inputHandler);
484         $("body").append($(SWS.Templates.controlPanel));
485         var cur = self.getCurrentSlide();
486         $(".sws-slide").each (function (i) {
487             var par = $(this).parent();
488
489             $(this).remove();
490             var canvas = $('<div class="sws-canvas"/>');
491
492             if (!($(this).hasClass("sws-option-noheader"))) {
493                 canvas.append($('<div class="sws-header"/>'));
494             };
495             $(this).find('script[type="text/javascript"]').remove();
496             canvas.append($(this));
497             if (!($(this).hasClass("sws-option-nofooter"))) {
498                 canvas.append($('<div class="sws-footer"/>'));
499             };
500             par.append(canvas);
501
502             if (i == cur) {
503                 canvas
504                     .addClass("sws-active-slide")
505                     .removeClass("sws-inacitve-slide");
506             } else {
507                 canvas
508                     .addClass("sws-inactive-slide")
509                     .removeClass("sws-active-slide");
510             };
511             init_canvas(canvas,_slide_callbacks[i]);
512
513         });
514         _slide_callbacks = null; /* avoid a leak */
515         self.refresh();
516         _initialized = true;
517
518     };
519
520 };