Make the style as resolution independent as possible.
[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-last-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 from_slide = $(canvas[from_slide_num]);
266         var slide_change = (from_slide_num != to_slide_num);
267
268         var info = to_slide.data("sws-frame-info");
269         SWS.Config['sws-update-header'](to_slide);
270         SWS.Config['sws-update-footer'](to_slide);
271
272         if (slide_change) {
273             //Launch a slide transition:
274             SWS.Config['sws-slide-change'](from_slide_num, to_slide_num);
275             watch_slide_anim = true;
276             for (var i = 0; i < info.callbacks.at_slide.length;i++){
277                 info.callbacks.at_slide[i](to_slide);
278             };
279         };
280
281
282         var cur = info.current;
283         var custom = info.custom;
284         var real_slide = to_slide.find(".sws-slide");
285
286         real_slide.find("*").andSelf().each(function (i){
287             var frameset = $(this).data("sws-frame-set") || {};
288             if (frameset[cur])
289                 SWS.Config['sws-object-activate']($(this));
290             else
291                 SWS.Config['sws-object-deactivate']($(this));
292         });
293
294         var callbacks;
295         if (callbacks = info.callbacks.at_frame[self.getCurrentFrame()]){
296             for (var k = 0; k < callbacks.length; k++)
297                 callbacks[k]($(to_slide));
298         };
299
300         var all = $(from_slide).add(to_slide);
301         all.find("*").addBack().promise().done(function() {
302             _disable_input_events = false;
303         });
304     };
305
306     self.nextSlide = function () {
307         self.setCurrentSlide(Math.min(self.getCurrentSlide()+1,
308                                       self.lastSlide()));
309         self.setCurrentFrame(self.firstFrame());
310     };
311
312     self.previousSlide = function () {
313         self.setCurrentSlide(Math.max(self.getCurrentSlide()-1,
314                                       self.firstSlide()));
315         self.setCurrentFrame(self.firstFrame());
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             self.setCurrentFrame(self.firstFrame());
345         } else
346             self.nextFrame();
347     };
348
349     self.previous = function () {
350         var i = self.getCurrentFrame();
351         if (i == self.firstFrame()){
352             self.previousSlide();
353             self.setCurrentFrame(self.lastFrame());
354         }
355         else
356             self.previousFrame();
357     };
358
359     self.goToSlide = function (s, f) {
360         if (SWS.Utils.isUndefined(f))
361             f = 0;
362         if (!(s >= self.firstSlide() && s <= self.lastSlide())) return;
363         self.setCurrentSlide(s);
364         if (!(f >= self.firstFrame() && f <= self.lastFrame())) f = 0;
365         self.setCurrentFrame(f);
366         self.refresh();
367     };
368
369     self.cycleStyle = function() {
370         var styles = $("head").children('link[rel$="stylesheet"][title]');
371         var j = styles.index(styles.filter(':not(:disabled)'));
372         styles[j].disabled = true;
373         if (++j == styles.length) j = 0;
374         styles[j].disabled = false;
375     };
376
377     self.inputHandler = function (event) {
378         if (_disable_input_events) return;
379         switch (event.which) {
380         case 36:/* Home */
381             self.setCurrentSlide(self.firstSlide());
382             break;
383
384         case 35:/* End */
385             self.setCurrentSlide(self.lastSlide());
386             break;
387
388         case 32: /* space */
389         case 39: /* -> */
390
391             self.next();
392             break;
393         case 34: /* PgDown */
394         case 78: /* n */
395             self.nextSlide();
396             break;
397         case 8: /* backspace */
398         case 37: /* <-   */
399             self.previous();
400             break;
401         case 33: /* PgUp */
402         case 80: /* p */
403             self.previousSlide();
404             break;
405         case 83: /* s */
406             self.cycleStyle();
407             return;
408         case 67: /* c */
409             $("#sws-control-panel").toggle();
410         default:
411             return;
412         };
413         self.refresh();
414     };
415
416
417
418     function init_canvas(canvas, custom) {
419         var cur_frame = 0;
420         var last_frame = canvas.find(".sws-pause").length;
421         //Add all regular elements to the frame list
422         var slide = $(canvas.find(".sws-slide")[0]);
423
424         var callbacks = { at_slide : new Array(),
425                           at_frame : new Array() }
426
427         if (SWS.Utils.isUndefined(custom)) {
428             custom = new Array ();
429         };
430
431         for (var i = 0; i < custom.length; i++) {
432             if (isFinite(custom[i].frame)){
433                 var num = +(custom[i].frame);
434                 if (num > last_frame) last_frame = num;
435                 SWS.Utils.push2(callbacks.at_frame, num, custom[i].fn);
436             } else if (custom[i].frame == "slide")
437                 callbacks.at_slide.push(custom[i].fn);
438             else {
439                 var frame_set = SWS.Utils.parseFrameSpec(custom[i].frame);
440                 for(var f in frame_set){
441                     if (f > last_frame) last_frame = f;
442                     SWS.Utils.push2(callbacks.at_frame, +(f), custom[i].fn);
443                 };
444             }
445         };
446
447         var specials = null;
448
449         slide.find('*[class*="sws-onframe-"]').each(function(_){
450             var cls = $(this).attr('class');
451             var idx = cls.indexOf("sws-onframe-");
452             if (idx >= 0) {
453                 var end = cls.indexOf(" ", idx);
454                 end = (end == -1) ? cls.length : end;
455                 var spec = cls.substring(idx+12, end);
456                 var o = SWS.Utils.parseFrameSpec(spec);
457                 for(var f in o)
458                     if (f > last_frame) last_frame = f;
459                 $(this).find("*").andSelf().each(function(_){
460                     if (!SWS.Utils.isEmpty(o))
461                         $(this).data("sws-frame-set", o);
462                     if (specials)
463                         specials.add($(this));
464                     else
465                         specials = $(this);
466                 });
467             };
468         });
469
470         slide.find("*").andSelf().not(specials).each(function(i) {
471             if ($(this).hasClass("sws-pause"))  cur_frame++;
472             var o = {};
473             for (var j = cur_frame; j <= last_frame; j++)
474                 o[ j ] = true;
475             if (!SWS.Utils.isEmpty(o))
476                 $(this).data("sws-frame-set", o);
477         });
478
479         canvas.data("sws-frame-info", { current: 0,
480                                         last: last_frame,
481                                         callbacks: callbacks
482                                       });
483
484     };
485
486     self.init = function () {
487         console.log("inited");
488         $(window).bind('storage', function (e) {
489             console.log(e);
490         });
491
492         _total_slides = $(".sws-slide").length;
493
494         $(document).keydown(self.inputHandler);
495         $("body").append($(SWS.Templates.controlPanel));
496         var cur = self.getCurrentSlide();
497         $(".sws-slide").each (function (i) {
498             var par = $(this).parent();
499
500             $(this).remove();
501             var canvas = $('<div class="sws-canvas"/>');
502
503             if (!($(this).hasClass("sws-option-noheader"))) {
504                 canvas.append($('<div class="sws-header"/><br/>'));
505             };
506
507             var inner = $('<div class="sws-inner-canvas"/>');
508             var content = $('<div class="sws-content"/>');
509             var title = $('<div class="sws-title"/>');
510             var h1 = $(this).children("h1");
511             if (h1) {
512               h1.detach();
513               title.append(h1);
514             }
515             canvas.append(title);
516             canvas.append($("<br/>"));
517             $(this).find('script[type="text/javascript"]').remove();
518             content.append($(this));
519             inner.append(content);
520             inner.append($('<span class="sws-vertical-align"> </span><br/>'));
521             canvas.append(inner);
522
523             if (!($(this).hasClass("sws-option-nofooter"))) {
524                 canvas.append($('<div class="sws-footer"/>'));
525             };
526
527             par.append(canvas);
528
529             if (i == cur) {
530                 canvas
531                     .addClass("sws-active-slide")
532                     .removeClass("sws-inactive-slide");
533             } else {
534                 canvas
535                     .addClass("sws-inactive-slide")
536                     .removeClass("sws-active-slide");
537             };
538             init_canvas(canvas,_slide_callbacks[i]);
539
540         });
541         _slide_callbacks = null; /* avoid a leak */
542         self.refresh();
543         _initialized = true;
544
545     };
546
547 };