9 SWS.Utils = new function () {
12 self.isUndefined = function (o) { return typeof o == "undefined"; };
13 self.push2 = function (t, i, v) {
14 if ((typeof t[i]) == 'undefined') {
21 self.isEmpty = function (o) {
22 for(var _ in o) return false;
26 self.parseFrameSpec = function (s) {
27 var elems = s.split("_");
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++)
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, " "));
58 SWS.Templates = new function () {
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();'>✖</a>\
65 <div id='sws-control-panel-options'>\
66 <span title='Change the aspect ratio' class='sws-symbol' >💻</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>\
71 <span title='Change the theme' class='sws-symbol'>🎨</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'></span></a>\
74 <div id='sws-control-panel-navigation'>\
75 <a title='First slide' class='sws-symbol' onclick='SWS.Presentation.goToSlide(SWS.Presentation.firstSlide());' >⏮</a>\
76 <a title='Previous slide' class='sws-symbol' onclick='SWS.Presentation.previousSlide();SWS.Presentation.refresh();'>⏪</a>\
77 <a title='Previous step' class='sws-symbol' style='-webkit-transform: scaleX(-1);' onclick='SWS.Presentation.previous();SWS.Presentation.refresh();'>▶</a>\
78 <a title='Next step' class='sws-symbol' onclick='SWS.Presentation.next();SWS.Presentation.refresh();'>▶</a>\
79 <a title='Next slide' class='sws-symbol' onclick='SWS.Presentation.nextSlide();SWS.Presentation.refresh();'>⏩</a>\
80 <a title='Last slide' class='sws-symbol' onclick='SWS.Presentation.goToSlide(SWS.Presentation.lastSlide());'>⏭</a>\
82 <span class='sws-symbol'></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>\
87 self.slideActivate = function (o) {
88 if (!(o.hasClass("sws-active-slide"))){
89 o.removeClass("sws-inactive-slide").addClass("sws-active-slide");
93 self.slideDeactivate = function (o) {
94 if (!(o.hasClass("sws-inactive-slide"))){
95 o.removeClass("sws-active-slide").addClass("sws-inactive-slide");
99 self.slideChange = function (from, to) {
100 var canvas = $(".sws-canvas");
101 self.slideDeactivate($(canvas[from]));
102 self.slideActivate($(canvas[to]));
105 self.objectActivate = function (o) {
106 if (!(o.hasClass("sws-active-object"))){
107 o.removeClass("sws-inactive-object").addClass("sws-active-object");
113 self.objectDeactivate = function (o) {
114 if (!(o.hasClass("sws-inactive-object"))){
115 o.addClass("sws-inactive-object").removeClass("sws-active-object");
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'>"
128 var sep = $( "<span class='sws-slide-num-sep' />");
129 var tot = $( "<span class='sws-last-slide-number'>"
130 + (SWS.Presentation.getNumSlides())
132 footer.append(cur).append(sep).append(tot);
135 self.updateHeader = function (o) {};
137 SWS.ConfigBuilder = function () {
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;
146 SWS.Defaults = new SWS.ConfigBuilder ();
148 SWS.Config = new SWS.ConfigBuilder ();
151 SWS.Effects = new function () {
154 self.objectDeactivateFadeOut = function (o) {
155 o.animate({'opacity': '0'}, 200,
158 SWS.Templates.objectDeactivate(o);
162 self.objectActivateFadeIn = function (o) {
164 if (SWS.Templates.objectActivate(o)){
165 o.animate({'opacity': '1' }, 200);
170 self.slideChangeHorizontalFlip = function (from, to){
171 var f = SWS.Presentation.getSlide(from);
172 var t = SWS.Presentation.getSlide(to);
173 f.animate({ 'left': '50%', 'width': '0pt', 'opacity':'0' }, 150,
175 SWS.Templates.slideDeactivate(f);
176 f.css({'left':'0%', 'width': '100%'});
177 t.css({ 'left': '50%', 'width': '0pt','opacity':'0' });
178 SWS.Templates.slideActivate(t);
179 t.animate({'left':'0%', 'width': '100%','opacity':'1'});
182 self.slideChangeFadeOutIn = function (from, to) {
183 var f = SWS.Presentation.getSlide(from);
184 var t = SWS.Presentation.getSlide(to);
185 f.animate({ 'opacity': '0'}, 150,
186 function () { SWS.Templates.slideDeactivate(f);
187 SWS.Templates.slideActivate(t);
188 t.css('opacity', '0');
189 t.animate({ 'opacity': '1'}, 150);
192 self.slideChangeHorizontalSlide = function (from, to) {
193 var f = SWS.Presentation.getSlide(from);
194 var t = SWS.Presentation.getSlide(to);
196 t.css('left', '100%');
197 t.css('opacity', '1');
198 SWS.Templates.slideActivate(t);
199 f.animate({ 'left': '-100%' }, 250, function () { SWS.Templates.slideDeactivate(f);
200 f.css('opacity', '0');
201 t.animate({ 'left': '0%' }, 250);
204 t.css('left', '-100%');
205 SWS.Templates.slideActivate(t);
206 f.animate({ 'left': '100%' }, 250, function () { SWS.Templates.slideDeactivate(f);
207 f.css('opacity', '0');
209 t.css('opacity', '1');
210 t.animate({ 'left': '0%' }, 250);
215 self.slideChangeVerticalSlide = function (from, to) {
216 var f = SWS.Presentation.getSlide(from);
217 var t = SWS.Presentation.getSlide(to);
219 t.css('top', '100%');
220 SWS.Templates.slideActivate(t);
221 f.animate({ 'top': '-100%' }, 250, function () { SWS.Templates.slideDeactivate(f); });
222 t.animate({ 'top': '0%' }, 250);
224 t.css('top', '-100%');
225 SWS.Templates.slideActivate(t);
226 f.animate({ 'top': '100%' }, 250, function () { SWS.Templates.slideDeactivate(f); });
227 t.animate({ 'top': '0%' }, 250);
233 SWS.Fullscreen = new function () {
236 if (SWS.Utils.isUndefined(document.fullScreen)) {
237 if (SWS.Utils.isUndefined(document.mozfullScreen)) {
238 self.status = function () { return document.webkitIsFullScreen; };
239 self.enter = function(e) {
240 e.webkitRequestFullScreen();
242 self.exit = function () {
243 document.webkitCancelFullScreen();
247 self.status = function () { return document.mozfullScreen; };
248 self.enter = function(e) {
249 e.mozRequestFullScreen();
251 self.exit = function () {
252 document.mozCancelFullScreen();
257 self.status = function () { return document.fullScreen; };
258 self.enter = function(e) {
259 e.requestFullScreen();
261 self.exit = function () {
262 document.cancelFullScreen();
270 SWS.Presentation = new function () {
275 //TODO move outside of the Presentation object
279 var _initialized = false;
280 var _disable_input_events = false;
281 var _print_mode = false;
282 var _slide_callbacks = new Array ();
283 var _total_steps = -1;
284 var _current_theme = "";
286 self.getNumSlides = function () { return _total_slides; };
288 self.getSlide = function(i) {
289 return $($(".sws-canvas")[i]);
292 self.registerCallback = function (i, f) {
293 if (_initialized) return;
294 //jQuery does not seem to work well
297 var slide_num = $(".sws-slide").length - 1;
299 SWS.Utils.push2(_slide_callbacks, slide_num,{ 'fn': f, 'frame': i });
303 if (typeof(Storage)!=="undefined"){
304 self.getCurrentSlide = function () {
305 //unary + casts to integer
306 var i = +(sessionStorage.getItem("current_slide"));
307 if (!(i >= 0 && i < self.getNumSlides())){
314 self.setCurrentSlide = function (i) {
315 sessionStorage.setItem("current_slide", i);
319 var _current_slide = 0;
320 self.getCurrentSlide = function () { return _current_slide; };
321 self.setCurrentSlide = function (i) { _current_slide = i; };
324 self.firstSlide = function () { return 0; };
325 self.lastSlide = function () { return self.getNumSlides() - 1; };
326 self.refresh = function () {
327 /* block upcoming input event until all animations are finished */
328 _disable_input_events = true;
330 var canvas = $(".sws-canvas");
331 var from_slide_num = canvas.index($(".sws-active-slide"));
332 var to_slide_num = self.getCurrentSlide();
333 var watch_slide_anim = false;
334 var to_slide = $(canvas[to_slide_num]);
335 var from_slide = $(canvas[from_slide_num]);
336 var slide_change = (from_slide_num != to_slide_num);
339 var info = to_slide.data("sws-frame-info");
340 SWS.Config['sws-update-header'](to_slide);
341 SWS.Config['sws-update-footer'](to_slide);
344 //Launch a slide transition:
345 SWS.Config['sws-slide-change'](from_slide_num, to_slide_num);
346 watch_slide_anim = true;
347 for (var i = 0; i < info.callbacks.at_slide.length;i++){
348 info.callbacks.at_slide[i](to_slide);
353 var cur = info.current;
354 var custom = info.custom;
355 var real_slide = to_slide.find(".sws-slide");
357 real_slide.find("*").andSelf().each(function (i){
358 var frameset = $(this).data("sws-frame-set") || {};
360 SWS.Config['sws-object-activate']($(this));
362 SWS.Config['sws-object-deactivate']($(this));
365 if (callbacks = info.callbacks.at_frame[self.getCurrentFrame()]){
366 for (var k = 0; k < callbacks.length; k++)
367 callbacks[k]($(to_slide));
370 var all = $(from_slide).add(to_slide);
371 all.find("*").addBack().promise().done(function() {
372 _disable_input_events = false;
376 self.nextSlide = function () {
377 self.setCurrentSlide(Math.min(self.getCurrentSlide()+1,
379 self.setCurrentFrame(self.firstFrame());
382 self.previousSlide = function () {
383 self.setCurrentSlide(Math.max(self.getCurrentSlide()-1,
385 self.setCurrentFrame(self.firstFrame());
388 self.getFrameInfo = function () {
390 var i = self.getCurrentSlide();
391 var canvas = $($(".sws-canvas")[i]);
392 var infos = canvas.data("sws-frame-info");
396 self.getTotalSteps = function () {
397 if (_total_steps >= 0) return _total_steps;
399 $(".sws-canvas").each(function(i) {
400 var canvas = $($(".sws-canvas")[i]);
401 var infos = canvas.data("sws-frame-info");
402 _total_steps += infos.last + 1;
407 self.getCurrentFrame = function () { return self.getFrameInfo().current; };
408 self.setCurrentFrame = function (i) { self.getFrameInfo().current = i; };
409 self.firstFrame = function () { return 0; };
410 self.lastFrame = function () { return self.getFrameInfo().last; };
412 self.nextFrame = function () {
413 self.setCurrentFrame(Math.min(self.getCurrentFrame()+1,
417 self.previousFrame = function () {
418 self.setCurrentFrame(Math.max(self.getCurrentFrame()-1,
422 self.next = function () {
423 var i = self.getCurrentFrame();
424 if (i == self.lastFrame()) {
426 self.setCurrentFrame(self.firstFrame());
431 self.previous = function () {
432 var i = self.getCurrentFrame();
433 if (i == self.firstFrame()){
434 self.previousSlide();
435 self.setCurrentFrame(self.lastFrame());
438 self.previousFrame();
441 self.goToSlide = function (s, f) {
442 if (SWS.Utils.isUndefined(f))
444 if (!(s >= self.firstSlide() && s <= self.lastSlide())) return;
445 self.setCurrentSlide(s);
446 if (!(f >= self.firstFrame() && f <= self.lastFrame())) f = 0;
447 self.setCurrentFrame(f);
451 self.cycleStyle = function() {
452 var styles = $("head").children('link[rel$="stylesheet"][title]');
453 var j = styles.index(styles.filter(':not(:disabled)'));
454 styles[j].disabled = true;
455 if (++j == styles.length) j = 0;
456 styles[j].disabled = false;
460 self.printMode = function () {
462 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>");
463 $("body").append(progress);
465 $("html").removeClass("sws-display").addClass("sws-print");
467 var steps = self.getTotalSteps();
468 var total_steps = steps;
472 //Crazy workaround for chromium
473 ($("link.sws-theme[rel='stylesheet']")[0]).disabled = false;
474 $(".sws-canvas").find("*").addBack().promise().done(function() {
475 var percent = ((total_steps - steps) / total_steps) * 100;
476 $("#sws-percent-progress").text(Math.round(percent));
477 SWS.Config['sws-slide-change'] = SWS.Templates.slideChange;
479 $($(".sws-canvas")[self.getCurrentSlide()]).css('opacity', 1 );
485 $("#sws-percent-progress").text(100);
495 self.inputHandler = function (event) {
496 if (_disable_input_events || _print_mode) return;
498 switch (event.type) {
500 _xstart = event.changedTouches[0].clientX;
504 var dist = event.changedTouches[0].clientX - _xstart;
505 if (dist > 20) code = 37
506 else if (dist < -20) code = 39
507 else if (!$("#sws-control-panel-canvas").is(":visible")) code = 67;
517 self.setCurrentSlide(self.firstSlide());
521 self.setCurrentSlide(self.lastSlide());
524 case 34: /* PgDown */
526 if (self.getCurrentSlide() == self.lastSlide()
527 && self.getCurrentFrame() == self.lastFrame()) return;
533 case 8: /* backspace */
539 self.previousSlide();
545 $("#sws-control-panel-canvas").toggle();
555 function init_canvas(canvas, custom) {
557 var last_frame = canvas.find(".sws-pause").length;
558 //Add all regular elements to the frame list
559 var slide = $(canvas.find(".sws-slide")[0]);
561 var callbacks = { at_slide : new Array(),
562 at_frame : new Array() }
564 if (SWS.Utils.isUndefined(custom)) {
565 custom = new Array ();
568 for (var i = 0; i < custom.length; i++) {
569 if (isFinite(custom[i].frame)){
570 var num = +(custom[i].frame);
571 if (num > last_frame) last_frame = num;
572 SWS.Utils.push2(callbacks.at_frame, num, custom[i].fn);
573 } else if (custom[i].frame == "slide")
574 callbacks.at_slide.push(custom[i].fn);
576 var frame_set = SWS.Utils.parseFrameSpec(custom[i].frame);
577 for(var f in frame_set){
578 if (f > last_frame) last_frame = f;
579 SWS.Utils.push2(callbacks.at_frame, +(f), custom[i].fn);
584 var specials = $([]);
586 slide.find('*[class*="sws-onframe-"]').each(function(_){
587 var cls = $(this).attr('class');
588 var idx = cls.indexOf("sws-onframe-");
590 var end = cls.indexOf(" ", idx);
591 end = (end == -1) ? cls.length : end;
592 var spec = cls.substring(idx+12, end);
593 var o = SWS.Utils.parseFrameSpec(spec);
595 if (f > last_frame) last_frame = f;
596 $(this).find("*").andSelf().each(function(_){
597 if (!SWS.Utils.isEmpty(o)){
598 $(this).data("sws-frame-set", o);
600 specials = specials.add($(this));
605 slide.find("*").andSelf().not(specials).each(function(i) {
606 if ($(this).hasClass("sws-pause")) cur_frame++;
608 for (var j = cur_frame; j <= last_frame; j++)
610 if (!SWS.Utils.isEmpty(o))
611 $(this).data("sws-frame-set", o);
614 canvas.data("sws-frame-info", { current: 0,
615 last: (last_frame - 0), // force cast to integer
621 /* Forces redrawing the page without reloading */
622 self.redraw = function (f) {
623 if (SWS.Utils.isUndefined(f))
624 $("body").hide().show(400, function () {
625 $("body").css("display","block");
626 if (!SWS.Utils.isUndefined(f))
630 self.changeAspect = function() {
631 $("html").removeClass("sws-aspect-4-3")
632 .removeClass("sws-aspect-16-9")
633 .removeClass("sws-aspect-16-10")
634 .addClass($("#sws-aspect-select").val());
638 self.getCurrentTheme = function () {
639 var l = $("link.sws-theme[rel='stylesheet']")[0];
647 self.changeTheme = function (name) {
649 if (typeof name === 'undefined')
650 theme_name = $("#sws-theme-select").val()
654 _current_theme = theme_name;
655 $("link.sws-theme").each (function (i) {
658 if (title == theme_name) {
659 e.rel = "stylesheet";
663 e.rel = "alternate stylesheet";
672 self.openPrint = function () {
673 window.open ("?mode=print&theme=" + self.getCurrentTheme());
675 var _fullscreen_icon_on = "";
676 var _fullscreen_icon_off = "";
678 self.toggleFullScreen = function () {
679 if (SWS.Fullscreen.status()) {
680 SWS.Fullscreen.exit();
681 $("a#sws-control-panel-fullscreen")
682 .html(_fullscreen_icon_off);
687 SWS.Fullscreen.enter($("body")[0]);
688 $("a#sws-control-panel-fullscreen")
689 .html(_fullscreen_icon_on);
692 function _update_ui() {
693 var nav = $('#sws-control-panel-navigation-bar');
694 nav.val(SWS.Presentation.getCurrentSlide() + 1);
695 $('#sws-control-panel-slide-input').val(nav.val());
697 self.navigate = function () {
698 self.goToSlide($("#sws-control-panel-navigation-bar").val()-1);
703 self.init = function () {
706 $("html").addClass("sws-display");
707 //$(window).resize(self.redraw);
711 _total_slides = $(".sws-slide").length;
713 var cur = self.getCurrentSlide();
715 $(".sws-slide").each(function (i) {
717 var par = $(this).parent();
721 var canvas = $('<div class="sws-canvas"/>');
723 if (!($(this).hasClass("sws-option-noheader"))) {
724 canvas.append($('<div class="sws-header"/>'));
726 if (!$(this).hasClass("sws-cover")) {
727 var title = $($(this).find("h1")[0]);
728 var title_div = $('<div class="sws-title" />');
729 title_div.append(title);
730 canvas.append(title_div);
732 var inner = $('<div class="sws-inner-canvas"/>');
733 var content = $('<div class="sws-content"/>');
734 $(this).find('script[type="text/javascript"]').remove();
735 content.append($(this));
736 inner.append(content);
737 canvas.append(inner);
739 if (!($(this).hasClass("sws-option-nofooter"))) {
740 canvas.append($('<div class="sws-footer"/>'));
747 .addClass("sws-active-slide")
748 .removeClass("sws-inactive-slide");
751 .addClass("sws-inactive-slide")
752 .removeClass("sws-active-slide");
754 init_canvas(canvas,_slide_callbacks[i]);
758 // Initialize the control panel
759 $("body").append($(SWS.Templates.controlPanel));
760 // Fill the theme switcher
761 $("link.sws-theme").each (function (i) {
763 var opt = "<option value='";
764 opt += e.attr("title");
766 if (e.attr("rel") == "stylesheet") {
767 opt+= "selected='selected'";
769 opt += ">" + e.attr("title") + "</option>";
770 $("#sws-theme-select").append($(opt));
772 // Set the fullscreen icon
773 if (SWS.Fullscreen.status()) {
774 $("a#sws-control-panel-fullscreen")
775 .html(_fullscreen_icon_on);
777 $("a#sws-control-panel-fullscreen")
778 .html(_fullscreen_icon_off);
780 // Put the navigation range at the correct position
781 var nav = $('#sws-control-panel-navigation-bar');
782 nav.attr("min", SWS.Presentation.firstSlide() + 1);
783 nav.attr("max", SWS.Presentation.lastSlide() + 1);
784 $('#sws-control-panel-total-slides').text('/' + SWS.Presentation.getNumSlides());
787 _slide_callbacks = null; /* avoid a leak */
788 var passed_theme = SWS.Utils.getParameterByName("theme");
789 //workaround weird chrome CSS loading bug
792 if (passed_theme == "")
795 self.changeTheme(passed_theme);
796 if (SWS.Utils.getParameterByName("mode") == "print") {
801 $(document).keydown(self.inputHandler);
802 document.body.addEventListener('touchstart',self.inputHandler, false);
803 document.body.addEventListener('touchend',self.inputHandler, false);