.
[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     self.getParameterByName = function (name) {
48         name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
49         var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
50         results = regex.exec(location.search);
51         return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
52     }
53 };
54
55
56
57
58 SWS.Templates = new function () {
59     var self = this;
60     self.controlPanel = "<div id='sws-control-panel-canvas'><div id='sws-control-panel'>\
61 <div id='sws-control-panel-title-bar'>\
62 <a title='Toggle fullscreen' id='sws-control-panel-fullscreen' class='sws-symbol' onclick='SWS.Presentation.toggleFullScreen();'></a>\
63 <a title='Close panel' id='sws-control-panel-close' onclick='$(\"#sws-control-panel-canvas\").toggle();'>&#x2716;</a>\
64 </div>\
65 <div id='sws-control-panel-options'>\
66 <span title='Change the aspect ratio' class='sws-symbol' >&#x1f4bb;</span><select id='sws-aspect-select' onchange='SWS.Presentation.changeAspect();'>\
67 <option value='sws-aspect-4-3'>4:3</option>\
68 <option value='sws-aspect-16-9'>16:9</option>\
69 <option value='sws-aspect-16-10'>16:10</option>\
70 </select>\
71 <span title='Change the theme' class='sws-symbol'>&#x1f3a8;</span><select id='sws-theme-select' onchange='SWS.Presentation.changeTheme();'></select>\
72 <a onclick='SWS.Presentation.openPrint()' ><span title='Open Print-Out' class='sws-symbol'>&#59158;</span></a>\
73 </div>\
74 <div id='sws-control-panel-navigation'>\
75 <a title='First slide' class='sws-symbol' onclick='SWS.Presentation.goToSlide(SWS.Presentation.firstSlide());' >&#x23ee;</a>\
76 <a title='Previous slide' class='sws-symbol' onclick='SWS.Presentation.previousSlide();SWS.Presentation.refresh();'>&#x23ea;</a>\
77 <a title='Previous step' class='sws-symbol' style='-webkit-transform: scaleX(-1);' onclick='SWS.Presentation.previous();SWS.Presentation.refresh();'>&#x25b6;</a>\
78 <a title='Next step' class='sws-symbol' onclick='SWS.Presentation.next();SWS.Presentation.refresh();'>&#x25b6;</a>\
79 <a title='Next slide' class='sws-symbol' onclick='SWS.Presentation.nextSlide();SWS.Presentation.refresh();'>&#x23e9;</a>\
80 <a title='Last slide' class='sws-symbol' onclick='SWS.Presentation.goToSlide(SWS.Presentation.lastSlide());'>&#x23ed;</a>\
81 <br/>\
82 <span class='sws-symbol'>&#xe4ae;</span><input type='text' id='sws-control-panel-slide-input' oninput='SWS.Presentation.goToSlide($(\"#sws-control-panel-slide-input\").val()-1);'></input><span id='sws-control-panel-total-slides'></span>\
83 <input type='range' title='Navigate the presentation' id='sws-control-panel-navigation-bar' onchange='SWS.Presentation.navigate();' step='1'></input>\
84 </div>\
85 </div>\
86 </div>";
87     self.slideActivate = function (o) {
88         if (!(o.hasClass("sws-active-slide"))){
89             o.removeClass("sws-inactive-slide").addClass("sws-active-slide");
90         };
91     };
92
93     self.slideDeactivate = function (o) {
94         if (!(o.hasClass("sws-inactive-slide"))){
95             o.removeClass("sws-active-slide").addClass("sws-inactive-slide");
96         };
97     };
98
99     self.slideChange = function (from, to) {
100         var canvas = $(".sws-canvas");
101         self.slideDeactivate($(canvas[from]));
102         self.slideActivate($(canvas[to]));
103     };
104
105     self.objectActivate = function (o) {
106         if (!(o.hasClass("sws-active-object"))){
107             o.removeClass("sws-inactive-object").addClass("sws-active-object");
108             return true;
109         };
110         return false;
111     };
112
113     self.objectDeactivate = function (o) {
114         if (!(o.hasClass("sws-inactive-object"))){
115             o.addClass("sws-inactive-object").removeClass("sws-active-object");
116             return true;
117         };
118         return false;
119     };
120
121     self.updateFooter = function (o) {
122         var footer = o.find(".sws-footer");
123         if (footer.length && (footer.children("*").length == 0)) {
124             var i = SWS.Presentation.getCurrentSlide();
125             var cur = $( "<span class='sws-current-slide-number'>"
126                          + (i + 1)
127                          +"</span>");
128             var sep = $( "<span class='sws-slide-num-sep' />");
129             var tot = $( "<span class='sws-last-slide-number'>"
130                          + (SWS.Presentation.getNumSlides())
131                          +"</span>");
132             footer.append(cur).append(sep).append(tot);
133         };
134     };
135     self.updateHeader = function (o) {};
136 };
137 SWS.ConfigBuilder = function () {
138     var self = this;
139     self['sws-object-activate'] = SWS.Templates.objectActivate;
140     self['sws-object-deactivate'] = SWS.Templates.objectDeactivate;
141     self['sws-slide-change'] = SWS.Templates.slideChange;
142     self['sws-update-footer'] = SWS.Templates.updateFooter;
143     self['sws-update-header'] = SWS.Templates.updateHeader;
144 };
145
146 SWS.Defaults = new SWS.ConfigBuilder ();
147
148 SWS.Config = new SWS.ConfigBuilder ();
149
150
151 SWS.Effects = new function () {
152     var self = this;
153
154     self.objectDeactivateFadeOut = function (o) {
155         if (o.is("embed")) return;
156         o.animate({'opacity': '0'}, 200,
157                   function () {
158
159                       SWS.Templates.objectDeactivate(o);
160                   });
161     };
162
163     self.objectActivateFadeIn = function (o) {
164
165         if (SWS.Templates.objectActivate(o)){
166             o.animate({'opacity': '1' }, 200);
167         };
168
169     };
170
171     self.slideChangeHorizontalFlip = function (from, to){
172         var f = SWS.Presentation.getSlide(from);
173         var t = SWS.Presentation.getSlide(to);
174         f.animate({ 'left': '50%', 'width': '0pt', 'opacity':'0' }, 150,
175                   function  () {
176                       SWS.Templates.slideDeactivate(f);
177                       f.css({'left':'0%', 'width': '100%'});
178                       t.css({ 'left': '50%', 'width': '0pt','opacity':'0' });
179                       SWS.Templates.slideActivate(t);
180                       t.animate({'left':'0%', 'width': '100%','opacity':'1'});
181                   });
182     };
183     self.slideChangeFadeOutIn = function (from, to) {
184         var f = SWS.Presentation.getSlide(from);
185         var t = SWS.Presentation.getSlide(to);
186         f.animate({ 'opacity': '0'}, 150,
187                   function () { SWS.Templates.slideDeactivate(f);
188                                 SWS.Templates.slideActivate(t);
189                                 t.css('opacity', '0');
190                                 t.animate({ 'opacity': '1'}, 150);
191                               });
192     };
193     self.slideChangeHorizontalSlide = function (from, to) {
194         var f = SWS.Presentation.getSlide(from);
195         var t = SWS.Presentation.getSlide(to);
196         if (from < to) {
197             t.css('left', '100%');
198             t.css('opacity', '1');
199             SWS.Templates.slideActivate(t);
200             f.animate({ 'left': '-100%' }, 250, function () { SWS.Templates.slideDeactivate(f);
201                                                               f.css('opacity', '0');
202                                                               t.animate({ 'left': '0%' }, 250);
203                                                             });
204         } else {
205             t.css('left', '-100%');
206             SWS.Templates.slideActivate(t);
207             f.animate({ 'left': '100%' }, 250, function () { SWS.Templates.slideDeactivate(f);
208                                                              f.css('opacity', '0');
209                                                            });
210             t.css('opacity', '1');
211             t.animate({ 'left': '0%' }, 250);
212         };
213     };
214
215
216     self.slideChangeVerticalSlide = function (from, to) {
217         var f = SWS.Presentation.getSlide(from);
218         var t = SWS.Presentation.getSlide(to);
219         if (from < to) {
220             t.css('top', '100%');
221             SWS.Templates.slideActivate(t);
222             f.animate({ 'top': '-100%' }, 250, function () { SWS.Templates.slideDeactivate(f); });
223             t.animate({ 'top': '0%' }, 250);
224         } else {
225             t.css('top', '-100%');
226             SWS.Templates.slideActivate(t);
227             f.animate({ 'top': '100%' }, 250, function () { SWS.Templates.slideDeactivate(f); });
228             t.animate({ 'top': '0%' }, 250);
229         };
230     };
231
232 };
233
234 SWS.Fullscreen = new function () {
235     var self = this;
236
237     if (SWS.Utils.isUndefined(document.fullScreen)) {
238         if (SWS.Utils.isUndefined(document.mozfullScreen)) {
239             self.status = function () { return document.webkitIsFullScreen; };
240             self.enter = function(e) {
241                 e.webkitRequestFullScreen();
242             };
243             self.exit = function () {
244                 document.webkitCancelFullScreen();
245             };
246
247         } else {
248             self.status = function () { return document.mozfullScreen; };
249             self.enter = function(e) {
250                 e.mozRequestFullScreen();
251             };
252             self.exit = function () {
253                 document.mozCancelFullScreen();
254             };
255
256         };
257     } else {
258             self.status = function () { return document.fullScreen; };
259             self.enter = function(e) {
260                 e.requestFullScreen();
261             };
262             self.exit = function () {
263                 document.cancelFullScreen();
264             };
265
266     };
267
268
269 };
270
271 SWS.Presentation = new function () {
272
273
274     var self = this;
275
276     //TODO move outside of the Presentation object
277
278
279     var _total_slides;
280     var _initialized = false;
281     var _disable_input_events = false;
282     var _print_mode = false;
283     var _slide_callbacks = new Array ();
284     var _total_steps = -1;
285     var _current_theme = "";
286
287     self.getNumSlides = function () { return _total_slides; };
288
289     self.getSlide = function(i) {
290         return $($(".sws-canvas")[i]);
291     };
292
293     self.registerCallback = function (i, f) {
294         if (_initialized) return;
295         //jQuery does not seem to work well
296         //on a partial DOM
297         var slides = $(".sws-slide");
298         var h1s = $("body").children("h1");
299         var slide_num = slides.add(h1s).length - 1;
300
301         SWS.Utils.push2(_slide_callbacks, slide_num,{ 'fn': f, 'frame': i });
302
303     };
304
305     if (typeof(Storage)!=="undefined"){
306         self.getCurrentSlide = function () {
307             //unary + casts to integer
308             var i = +(sessionStorage.getItem("current_slide"));
309             if (!(i >= 0 && i < self.getNumSlides())){
310                 return 0;
311             } else {
312                 return i;
313             };
314         };
315
316         self.setCurrentSlide = function (i) {
317             sessionStorage.setItem("current_slide", i);
318         };
319
320     } else {
321         var _current_slide = 0;
322         self.getCurrentSlide = function () { return _current_slide; };
323         self.setCurrentSlide = function (i) { _current_slide = i; };
324
325     };
326     self.firstSlide = function () { return 0; };
327     self.lastSlide = function () { return self.getNumSlides() - 1; };
328     self.refresh = function () {
329         /* block upcoming input event until all animations are finished */
330         _disable_input_events = true;
331
332         var canvas = $(".sws-canvas");
333         var from_slide_num = canvas.index($(".sws-active-slide"));
334         var to_slide_num = self.getCurrentSlide();
335         var watch_slide_anim = false;
336         var to_slide = $(canvas[to_slide_num]);
337         var from_slide = $(canvas[from_slide_num]);
338         var slide_change = (from_slide_num != to_slide_num);
339
340
341         var info = to_slide.data("sws-frame-info");
342         SWS.Config['sws-update-header'](to_slide);
343         SWS.Config['sws-update-footer'](to_slide);
344
345         if (slide_change) {
346             //Launch a slide transition:
347             SWS.Config['sws-slide-change'](from_slide_num, to_slide_num);
348             watch_slide_anim = true;
349             for (var i = 0; i < info.callbacks.at_slide.length;i++){
350                 info.callbacks.at_slide[i](to_slide);
351             };
352         };
353
354
355         var cur = info.current;
356         var custom = info.custom;
357         var real_slide = to_slide.find(".sws-slide");
358         var dont_touch = real_slide.find("sws-protect").find("*").addBack();
359         real_slide.find("*").addBack().not(dont_touch).each(function (i){
360             var frameset = $(this).data("sws-frame-set") || {};
361             if (frameset[cur])
362                 SWS.Config['sws-object-activate']($(this));
363             else
364                 SWS.Config['sws-object-deactivate']($(this));
365         });
366
367
368         var all = $(from_slide).add(to_slide);
369         all.find("*").addBack().promise().done(function() {
370             var callbacks;
371             //execute callbacks when all elements are finished transitioning
372             if (callbacks = info.callbacks.at_frame[self.getCurrentFrame()]){
373                 for (var k = 0; k < callbacks.length; k++)
374                     callbacks[k]($(to_slide));
375             };
376             all.find("*").addBack().promise().done(function() {
377                 //wait for all elements to finish transitionning, in case a callback animate something
378                 //an denable _input_events again.
379                 _disable_input_events = false;
380             });
381         });
382     };
383
384     self.nextSlide = function () {
385         self.setCurrentSlide(Math.min(self.getCurrentSlide()+1,
386                                       self.lastSlide()));
387         self.setCurrentFrame(self.firstFrame());
388     };
389
390     self.previousSlide = function () {
391         self.setCurrentSlide(Math.max(self.getCurrentSlide()-1,
392                                       self.firstSlide()));
393         self.setCurrentFrame(self.firstFrame());
394     };
395
396     self.getFrameInfo = function () {
397
398         var i = self.getCurrentSlide();
399         var canvas = $($(".sws-canvas")[i]);
400         var infos = canvas.data("sws-frame-info");
401         return infos;
402     };
403
404     self.getTotalSteps = function () {
405         if (_total_steps >= 0) return _total_steps;
406         _total_steps = 0;
407         $(".sws-canvas").each(function(i) {
408             var canvas = $($(".sws-canvas")[i]);
409             var infos = canvas.data("sws-frame-info");
410             _total_steps += infos.last + 1;
411         });
412         return _total_steps;
413     };
414
415     self.getCurrentFrame = function () { return self.getFrameInfo().current; };
416     self.setCurrentFrame = function (i) { self.getFrameInfo().current = i; };
417     self.firstFrame = function () { return 0; };
418     self.lastFrame = function () { return self.getFrameInfo().last; };
419
420     self.nextFrame = function () {
421         self.setCurrentFrame(Math.min(self.getCurrentFrame()+1,
422                                       self.lastFrame()));
423
424     };
425     self.previousFrame = function () {
426         self.setCurrentFrame(Math.max(self.getCurrentFrame()-1,
427                                        self.firstFrame()));
428     };
429
430     self.next = function () {
431         var i = self.getCurrentFrame();
432         if (i == self.lastFrame()) {
433             self.nextSlide();
434             self.setCurrentFrame(self.firstFrame());
435         } else
436             self.nextFrame();
437     };
438
439     self.previous = function () {
440         var i = self.getCurrentFrame();
441         if (i == self.firstFrame()){
442             self.previousSlide();
443             self.setCurrentFrame(self.lastFrame());
444         }
445         else
446             self.previousFrame();
447     };
448
449     self.goToSlide = function (s, f) {
450         if (SWS.Utils.isUndefined(f))
451             f = 0;
452         if (!(s >= self.firstSlide() && s <= self.lastSlide())) return;
453         self.setCurrentSlide(s);
454         if (!(f >= self.firstFrame() && f <= self.lastFrame())) f = 0;
455         self.setCurrentFrame(f);
456         self.refresh();
457     };
458
459     self.cycleStyle = function() {
460         var styles = $("head").children('link[rel$="stylesheet"][title]');
461         var j = styles.index(styles.filter(':not(:disabled)'));
462         styles[j].disabled = true;
463         if (++j == styles.length) j = 0;
464         styles[j].disabled = false;
465     };
466
467
468     self.printMode = function () {
469         _print_mode = true;
470         var progress = $("<div style='position:fixed;top:0pt;left:0pt;background:white;color:black;width:100%;height:100vh;z-index:200;' id='sws-print-progress'>Rendering presentation: <span id='sws-percent-progress'></span>%</div>");
471         $("body").append(progress);
472
473         $("html").removeClass("sws-display").addClass("sws-print");
474         self.goToSlide(0,0);
475         var steps = self.getTotalSteps();
476         var total_steps = steps;
477         var loop;
478         loop = function () {
479             if (steps >= 0) {
480                 //Crazy workaround for chromium
481                 ($("link.sws-theme[rel='stylesheet']")[0]).disabled = false;
482                 $(".sws-canvas").find("*").addBack().promise().done(function() {
483                     var percent = ((total_steps - steps) / total_steps) * 100;
484                     $("#sws-percent-progress").text(Math.round(percent));
485                     SWS.Config['sws-slide-change'] = SWS.Templates.slideChange;
486                     self.refresh();
487                     $($(".sws-canvas")[self.getCurrentSlide()]).css('opacity', 1 );
488                     self.next();
489                     steps--;
490                     loop();
491                     })
492             } else {
493                 $("#sws-percent-progress").text(100);
494                 progress.remove();
495             }
496         };
497         loop();
498
499     }
500
501     self.buildFullTOC = function () {
502
503         var build_sections = function (doc) {
504             var res = [];
505             var h1s = doc.find("body").first().children("h1");
506             var slides = doc.find("body").first().children(".sws-slide");
507             var slide_num = 1;
508             var collection = h1s.add(slides);
509             collection.each (function () {
510                 if ($(this).is("h1")) {
511                     res.push({ 'title' : $(this).text(),
512                                'slide' : slide_num });
513                 } else {
514                     slide_num++;
515                 }
516             });
517             return res;
518         };
519
520         var toc = [];
521
522         var append = function (a,e) {
523             return a.push(e);
524         };
525         var prepend = function (a,e) {
526             return a.unshift(e);
527         };
528
529         var loop = function (doc, dir, add, ignoreFirst) {
530             if (ignoreFirst !== true) {
531                 var this_toc = { 'title' : doc.find("title").first().text(),
532                                  'sections' : build_sections(doc) };
533                 add(toc, this_toc);
534             };
535             var url = doc.find(dir).first().attr("href");
536             if (!SWS.Utils.isUndefined(url) && url != "") {
537                 $.ajax({ 'url' : url, 'async' : false ,'success' : function (page) {
538                     loop ($(page), dir, add, false);
539                 }});
540             };
541         };
542         loop ($(document), ".sws-previous", prepend, false);
543         return toc;
544
545     };
546
547
548
549     var _xstart = 0;
550     self.inputHandler = function (event) {
551         if (_disable_input_events || _print_mode) return;
552         var code = 0;
553         switch (event.type) {
554         case 'touchstart':
555             _xstart = event.changedTouches[0].clientX;
556             return;
557         case 'touchend':
558
559             var dist = event.changedTouches[0].clientX - _xstart;
560             if (dist > 20) code = 37
561             else if (dist < -20) code = 39
562             else if (!$("#sws-control-panel-canvas").is(":visible")) code = 67;
563             break;
564         case 'keydown':
565             code = event.which;
566             break;
567         default:
568             return;
569         };
570         switch (code) {
571         case 36:/* Home */
572             self.setCurrentSlide(self.firstSlide());
573             break;
574
575         case 35:/* End */
576             self.setCurrentSlide(self.lastSlide());
577             break;
578         case 32: /* space */
579         case 34: /* PgDown */
580         case 39: /* -> */
581             if (self.getCurrentSlide() == self.lastSlide()
582                 && self.getCurrentFrame() == self.lastFrame()) return;
583             self.next();
584             break;
585         case 78: /* n */
586             self.nextSlide();
587             break;
588         case 8: /* backspace */
589         case 33: /* PgUp */
590         case 37: /* <-   */
591             self.previous();
592             break;
593         case 80: /* p */
594             self.previousSlide();
595             break;
596         case 83: /* s */
597                 self.cycleStyle();
598             return;
599         case 67: /* c */
600             $("#sws-control-panel-canvas").toggle();
601
602         default:
603             return;
604         };
605         self.refresh();
606 };
607
608
609
610     function init_canvas(canvas, custom) {
611         var cur_frame = 0;
612         var last_frame = canvas.find(".sws-pause").length;
613         //Add all regular elements to the frame list
614         var slide = $(canvas.find(".sws-slide")[0]);
615
616         var callbacks = { at_slide : new Array(),
617                           at_frame : new Array() }
618
619         if (SWS.Utils.isUndefined(custom)) {
620             custom = new Array ();
621         };
622
623         for (var i = 0; i < custom.length; i++) {
624             if (isFinite(custom[i].frame)){
625                 var num = +(custom[i].frame);
626                 if (num > last_frame) last_frame = num;
627                 SWS.Utils.push2(callbacks.at_frame, num, custom[i].fn);
628             } else if (custom[i].frame == "slide")
629                 callbacks.at_slide.push(custom[i].fn);
630             else {
631                 var frame_set = SWS.Utils.parseFrameSpec(custom[i].frame);
632                 for(var f in frame_set){
633                     if (f > last_frame) last_frame = f;
634                     SWS.Utils.push2(callbacks.at_frame, +(f), custom[i].fn);
635                 };
636             }
637         };
638
639         var specials = $([]);
640
641         slide.find('*[class*="sws-onframe-"]').each(function(_){
642             var cls = $(this).attr('class');
643             var idx = cls.indexOf("sws-onframe-");
644             if (idx >= 0) {
645                 var end = cls.indexOf(" ", idx);
646                 end = (end == -1) ? cls.length : end;
647                 var spec = cls.substring(idx+12, end);
648                 var o = SWS.Utils.parseFrameSpec(spec);
649                 for(var f in o)
650                     if (f > last_frame) last_frame = f;
651                 $(this).find("*").andSelf().each(function(_){
652                     if (!SWS.Utils.isEmpty(o)){
653                         $(this).data("sws-frame-set", o);
654                     }
655                     specials = specials.add($(this));
656                 });
657             };
658         });
659
660         slide.find("*").andSelf().not(specials).each(function(i) {
661             if ($(this).hasClass("sws-pause"))  cur_frame++;
662             var o = {};
663             for (var j = cur_frame; j <= last_frame; j++)
664                 o[ j ] = true;
665             if (!SWS.Utils.isEmpty(o))
666                 $(this).data("sws-frame-set", o);
667         });
668
669         canvas.data("sws-frame-info", { current: 0,
670                                         last: (last_frame - 0), // force cast to integer
671                                         callbacks: callbacks
672                                       });
673
674     };
675
676     /* Forces redrawing the page without reloading */
677     self.redraw = function (f) {
678         if (SWS.Utils.isUndefined(f))
679             $("body").hide().show(400, function () {
680                 $("body").css("display","block");
681                 if (!SWS.Utils.isUndefined(f))
682                     f();
683             });
684     };
685     self.changeAspect = function() {
686         $("html").removeClass("sws-aspect-4-3")
687             .removeClass("sws-aspect-16-9")
688             .removeClass("sws-aspect-16-10")
689             .addClass($("#sws-aspect-select").val());
690         self.redraw();
691     };
692
693     self.getCurrentTheme = function () {
694         var l = $("link.sws-theme[rel='stylesheet']")[0];
695
696         if (l) {
697             return  l.title;
698         } else
699             return ""
700     };
701
702     self.changeTheme = function (name) {
703         var theme_name;
704         if (typeof name === 'undefined')
705             theme_name = $("#sws-theme-select").val()
706         else
707             theme_name = name;
708
709         _current_theme = theme_name;
710         $("link.sws-theme").each (function (i) {
711             var e = this;
712             var title =  e.title;
713             if (title == theme_name) {
714                 e.rel = "stylesheet";
715                 e.disabled = false;
716                 e.media="all";
717             } else {
718                 e.rel = "alternate stylesheet";
719                 e.disabled = true;
720                 e.media="all";
721             };
722         });
723         self.redraw();
724
725     };
726
727     self.openPrint = function () {
728         window.open ("?mode=print&theme=" + self.getCurrentTheme());
729     }
730     var _fullscreen_icon_on = "&#xe746;";
731     var _fullscreen_icon_off = "&#xe744;";
732
733     self.toggleFullScreen = function () {
734         if (SWS.Fullscreen.status()) {
735             SWS.Fullscreen.exit();
736             $("a#sws-control-panel-fullscreen")
737                 .html(_fullscreen_icon_off);
738
739
740
741         } else {
742             SWS.Fullscreen.enter($("body")[0]);
743             $("a#sws-control-panel-fullscreen")
744                 .html(_fullscreen_icon_on);
745         };
746     };
747     function _update_ui() {
748         var nav = $('#sws-control-panel-navigation-bar');
749         nav.val(SWS.Presentation.getCurrentSlide() + 1);
750         $('#sws-control-panel-slide-input').val(nav.val());
751     }
752     self.navigate = function () {
753         self.goToSlide($("#sws-control-panel-navigation-bar").val()-1);
754         _update_ui();
755     };
756
757
758     self.init = function () {
759
760
761         $("html").addClass("sws-display");
762         //$(window).resize(self.redraw);
763
764         var slides = $(".sws-slide");
765         var h1s = $("body").children("h1");
766         var slide_num = slides.add(h1s).length - 1;
767
768         _total_slides = $(".sws-slide").add($("body").children("h1")).length;
769
770         var cur = self.getCurrentSlide();
771         var toc = self.buildFullTOC();
772         var common_html = "<div class='sws-slide sws-toc'><h1>Plan</h1><ul style='list-style-type:none'>";
773         var i;
774         for (i= 0; i < toc.length - 1; i++)
775             common_html += "<li class='done'>" + (i+1) +
776             ' ' + toc[i].title + "</li>";
777
778         common_html += "<li>" + toc.length + ' ' + toc[toc.length - 1].title;
779         common_html += "<ul style='list-style-type:none' >";
780         var sections = toc[toc.length - 1].sections;
781         $("body").children("h1").each(function (i) {
782             var this_html = common_html;
783             var j;
784             var secnum = toc.length + '.';
785             for (j = 0; j < i; ++j)
786                 this_html += "<li class='done'>" + secnum + (j+1) + ' ' + 
787                 sections[j].title + "</li>";
788             this_html += "<li class='hl'>" + secnum + (i+1) + ' ' +
789                 sections[i].title + "</li>";
790             for (j = i+1; j < sections.length; j++)
791                 this_html += "<li>" + secnum + (j+1) + ' ' 
792                 +sections[j].title + "</li>";
793             this_html += "</ul></li></ul></div>";
794             $(this).after(this_html);
795         });
796
797         $(".sws-slide").each(function (i) {
798
799             var par = $(this).parent();
800
801
802             $(this).remove();
803             var canvas = $('<div class="sws-canvas"/>');
804
805             if (!($(this).hasClass("sws-option-noheader"))) {
806                 canvas.append($('<div class="sws-header"/>'));
807             };
808             if (!$(this).hasClass("sws-cover")) {
809                 var title = $($(this).find("h1")[0]);
810                 var title_div = $('<div class="sws-title" />');
811                 title_div.append(title);
812                 canvas.append(title_div);
813             }
814             var inner = $('<div class="sws-inner-canvas"/>');
815             var content = $('<div class="sws-content"/>');
816             $(this).find('script[type="text/javascript"]').remove();
817             content.append($(this));
818             inner.append(content);
819             canvas.append(inner);
820
821             if (!($(this).hasClass("sws-option-nofooter"))) {
822                 canvas.append($('<div class="sws-footer"/>'));
823             };
824
825             par.append(canvas);
826
827             if (i == cur) {
828                 canvas
829                     .addClass("sws-active-slide")
830                     .removeClass("sws-inactive-slide");
831             } else {
832                 canvas
833                     .addClass("sws-inactive-slide")
834                     .removeClass("sws-active-slide");
835             };
836             init_canvas(canvas,_slide_callbacks[i]);
837
838         });
839
840         // Initialize the control panel
841         $("body").append($(SWS.Templates.controlPanel));
842         // Fill the theme switcher
843         $("link.sws-theme").each (function (i) {
844             var e = $(this);
845             var opt = "<option value='";
846             opt += e.attr("title");
847             opt += "' ";
848             if (e.attr("rel") == "stylesheet") {
849                 opt+= "selected='selected'";
850             };
851             opt += ">" + e.attr("title") + "</option>";
852             $("#sws-theme-select").append($(opt));
853         });
854         // Set the fullscreen icon
855         if (SWS.Fullscreen.status()) {
856             $("a#sws-control-panel-fullscreen")
857                 .html(_fullscreen_icon_on);
858         } else {
859             $("a#sws-control-panel-fullscreen")
860                 .html(_fullscreen_icon_off);
861         };
862         // Put the navigation range at the correct position
863         var nav = $('#sws-control-panel-navigation-bar');
864         nav.attr("min", SWS.Presentation.firstSlide() + 1);
865         nav.attr("max", SWS.Presentation.lastSlide() + 1);
866         $('#sws-control-panel-total-slides').text('/' + SWS.Presentation.getNumSlides());
867         _update_ui();
868
869         _slide_callbacks = null; /* avoid a leak */
870         var passed_theme = SWS.Utils.getParameterByName("theme");
871
872
873         //workaround weird chrome CSS loading bug
874         var f =
875             function () {
876                 if (passed_theme == "")
877                     self.changeTheme();
878                 else
879                     self.changeTheme(passed_theme);
880                 if (SWS.Utils.getParameterByName("mode") == "print") {
881                     self.printMode();
882                 }
883                 else
884                     self.refresh();
885                 $(document).keydown(self.inputHandler);
886                 document.body.addEventListener('touchstart',self.inputHandler, false);
887                 document.body.addEventListener('touchend',self.inputHandler, false);
888
889                 _initialized = true;
890             };
891         setTimeout(f, 100);
892     };
893
894 };