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++)
53 SWS.Templates = new function () {
55 self.controlPanel = "<div id='sws-control-panel-canvas'><div id='sws-control-panel'>\
56 <div id='sws-control-panel-title-bar'>\
57 <a title='Toggle fullscreen' id='sws-control-panel-fullscreen' class='sws-symbol' onclick='SWS.Presentation.toggleFullScreen();'></a>\
58 <a title='Close panel' id='sws-control-panel-close' onclick='$(\"#sws-control-panel-canvas\").toggle();'>✖</a>\
60 <div id='sws-control-panel-options'>\
61 <span title='Change the aspect ratio' class='sws-symbol' >💻</span><select id='sws-aspect-select' onchange='SWS.Presentation.changeAspect();'>\
62 <option value='sws-aspect-4-3'>4:3</option>\
63 <option value='sws-aspect-16-9'>16:9</option>\
64 <option value='sws-aspect-16-10'>16:10</option>\
66 <span title='Change the theme' class='sws-symbol'>🎨</span><select id='sws-theme-select' onchange='SWS.Presentation.changeTheme();'></select>\
68 <div id='sws-control-panel-navigation'>\
69 <a title='First slide' class='sws-symbol' onclick='SWS.Presentation.goToSlide(SWS.Presentation.firstSlide());' >⏮</a>\
70 <a title='Previous slide' class='sws-symbol' onclick='SWS.Presentation.previousSlide();SWS.Presentation.refresh();'>⏪</a>\
71 <a title='Previous step' class='sws-symbol' style='-webkit-transform: scaleX(-1);' onclick='SWS.Presentation.previous();SWS.Presentation.refresh();'>▶</a>\
72 <a title='Next step' class='sws-symbol' onclick='SWS.Presentation.next();SWS.Presentation.refresh();'>▶</a>\
73 <a title='Next slide' class='sws-symbol' onclick='SWS.Presentation.nextSlide();SWS.Presentation.refresh();'>⏩</a>\
74 <a title='Last slide' class='sws-symbol' onclick='SWS.Presentation.goToSlide(SWS.Presentation.lastSlide());'>⏭</a>\
76 <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>\
77 <input type='range' title='Navigate the presentation' id='sws-control-panel-navigation-bar' onchange='SWS.Presentation.navigate();' step='1'></input>\
81 self.slideActivate = function (o) {
82 if (!(o.hasClass("sws-active-slide"))){
83 o.removeClass("sws-inactive-slide").addClass("sws-active-slide");
87 self.slideDeactivate = function (o) {
88 if (!(o.hasClass("sws-inactive-slide"))){
89 o.removeClass("sws-active-slide").addClass("sws-inactive-slide");
93 self.slideChange = function (from, to) {
94 var canvas = $(".sws-canvas");
95 self.slideDeactivate($(canvas[from]));
96 self.slideActivate($(canvas[to]));
99 self.objectActivate = function (o) {
100 if (!(o.hasClass("sws-active-object"))){
101 o.removeClass("sws-inactive-object").addClass("sws-active-object");
102 o.css({'visibility':'visible'});
108 self.objectDeactivate = function (o) {
109 if (!(o.hasClass("sws-inactive-object"))){
110 o.addClass("sws-inactive-object").removeClass("sws-active-object");
116 self.updateFooter = function (o) {
117 var footer = o.find(".sws-footer");
118 if (footer.length && (footer.children("*").length == 0)) {
119 var i = SWS.Presentation.getCurrentSlide();
120 var cur = $( "<span class='sws-current-slide-number'>"
123 var sep = $( "<span class='sws-slide-num-sep' />");
124 var tot = $( "<span class='sws-last-slide-number'>"
125 + (SWS.Presentation.getNumSlides())
127 footer.append(cur).append(sep).append(tot);
130 self.updateHeader = function (o) {};
132 SWS.ConfigBuilder = function () {
134 self['sws-object-activate'] = SWS.Templates.objectActivate;
135 self['sws-object-deactivate'] = SWS.Templates.objectDeactivate;
136 self['sws-slide-change'] = SWS.Templates.slideChange;
137 self['sws-update-footer'] = SWS.Templates.updateFooter;
138 self['sws-update-header'] = SWS.Templates.updateHeader;
141 SWS.Defaults = new SWS.ConfigBuilder ();
143 SWS.Config = new SWS.ConfigBuilder ();
146 SWS.Effects = new function () {
149 self.objectDeactivateFadeOut = function (o) {
150 o.animate({'opacity': '0'}, 200,
151 function () { SWS.Templates.objectDeactivate(o)});
154 self.objectActivateFadeIn = function (o) {
156 if (SWS.Templates.objectActivate(o)){
157 o.animate({'opacity': '1' }, 200);
162 self.slideChangeHorizontalFlip = function (from, to){
163 var f = SWS.Presentation.getSlide(from);
164 var t = SWS.Presentation.getSlide(to);
165 f.animate({ 'left': '50%', 'width': '0pt', 'opacity':'0' }, 150,
167 SWS.Templates.slideDeactivate(f);
168 f.css({'left':'0%', 'width': '100%'});
169 t.css({ 'left': '50%', 'width': '0pt','opacity':'0' });
170 SWS.Templates.slideActivate(t);
171 t.animate({'left':'0%', 'width': '100%','opacity':'1'});
174 self.slideChangeFadeOutIn = function (from, to) {
175 var f = SWS.Presentation.getSlide(from);
176 var t = SWS.Presentation.getSlide(to);
177 f.animate({ 'opacity': '0'}, 150,
178 function () { SWS.Templates.slideDeactivate(f);
179 SWS.Templates.slideActivate(t);
180 t.css('opacity', '0');
181 t.animate({ 'opacity': '1'}, 150);
184 self.slideChangeHorizontalSlide = function (from, to) {
185 var f = SWS.Presentation.getSlide(from);
186 var t = SWS.Presentation.getSlide(to);
188 t.css('left', '100%');
189 t.css('opacity', '1');
190 SWS.Templates.slideActivate(t);
191 f.animate({ 'left': '-100%' }, 250, function () { SWS.Templates.slideDeactivate(f);
192 f.css('opacity', '0');
193 t.animate({ 'left': '0%' }, 250);
196 t.css('left', '-100%');
197 SWS.Templates.slideActivate(t);
198 f.animate({ 'left': '100%' }, 250, function () { SWS.Templates.slideDeactivate(f);
199 f.css('opacity', '0');
201 t.css('opacity', '1');
202 t.animate({ 'left': '0%' }, 250);
207 self.slideChangeVerticalSlide = function (from, to) {
208 var f = SWS.Presentation.getSlide(from);
209 var t = SWS.Presentation.getSlide(to);
211 t.css('top', '100%');
212 SWS.Templates.slideActivate(t);
213 f.animate({ 'top': '-100%' }, 250, function () { SWS.Templates.slideDeactivate(f); });
214 t.animate({ 'top': '0%' }, 250);
216 t.css('top', '-100%');
217 SWS.Templates.slideActivate(t);
218 f.animate({ 'top': '100%' }, 250, function () { SWS.Templates.slideDeactivate(f); });
219 t.animate({ 'top': '0%' }, 250);
225 SWS.Fullscreen = new function () {
228 if (SWS.Utils.isUndefined(document.fullScreen)) {
229 if (SWS.Utils.isUndefined(document.mozfullScreen)) {
230 self.status = function () { return document.webkitIsFullScreen; };
231 self.enter = function(e) {
232 e.webkitRequestFullScreen();
234 self.exit = function () {
235 document.webkitCancelFullScreen();
239 self.status = function () { return document.mozfullScreen; };
240 self.enter = function(e) {
241 e.mozRequestFullScreen();
243 self.exit = function () {
244 document.mozCancelFullScreen();
249 self.status = function () { return document.fullScreen; };
250 self.enter = function(e) {
251 e.requestFullScreen();
253 self.exit = function () {
254 document.cancelFullScreen();
262 SWS.Presentation = new function () {
267 //TODO move outside of the Presentation object
271 var _initialized = false;
272 var _disable_input_events = false;
274 var _slide_callbacks = new Array ();
277 self.getNumSlides = function () { return _total_slides; };
279 self.getSlide = function(i) {
280 return $($(".sws-canvas")[i]);
283 self.registerCallback = function (i, f) {
284 if (_initialized) return;
285 //jQuery does not seem to work well
288 var slide_num = $(".sws-slide").length - 1;
290 SWS.Utils.push2(_slide_callbacks, slide_num,{ 'fn': f, 'frame': i });
294 if (typeof(Storage)!=="undefined"){
295 self.getCurrentSlide = function () {
296 //unary + casts to integer
297 var i = +(sessionStorage.getItem("current_slide"));
298 if (!(i >= 0 && i < self.getNumSlides())){
305 self.setCurrentSlide = function (i) {
306 sessionStorage.setItem("current_slide", i);
310 var _current_slide = 0;
311 self.getCurrentSlide = function () { return _current_slide; };
312 self.setCurrentSlide = function (i) { _current_slide = i; };
315 self.firstSlide = function () { return 0; };
316 self.lastSlide = function () { return self.getNumSlides() - 1; };
317 self.refresh = function () {
318 /* block upcoming input event until all animations are finished */
319 _disable_input_events = true;
321 var canvas = $(".sws-canvas");
322 var from_slide_num = canvas.index($(".sws-active-slide"));
323 var to_slide_num = self.getCurrentSlide();
324 var watch_slide_anim = false;
325 var to_slide = $(canvas[to_slide_num]);
326 var from_slide = $(canvas[from_slide_num]);
327 var slide_change = (from_slide_num != to_slide_num);
329 var info = to_slide.data("sws-frame-info");
330 SWS.Config['sws-update-header'](to_slide);
331 SWS.Config['sws-update-footer'](to_slide);
334 //Launch a slide transition:
335 SWS.Config['sws-slide-change'](from_slide_num, to_slide_num);
336 watch_slide_anim = true;
337 for (var i = 0; i < info.callbacks.at_slide.length;i++){
338 info.callbacks.at_slide[i](to_slide);
343 var cur = info.current;
344 var custom = info.custom;
345 var real_slide = to_slide.find(".sws-slide");
347 real_slide.find("*").andSelf().each(function (i){
348 var frameset = $(this).data("sws-frame-set") || {};
350 SWS.Config['sws-object-activate']($(this));
352 SWS.Config['sws-object-deactivate']($(this));
356 if (callbacks = info.callbacks.at_frame[self.getCurrentFrame()]){
357 for (var k = 0; k < callbacks.length; k++)
358 callbacks[k]($(to_slide));
361 var all = $(from_slide).add(to_slide);
362 all.find("*").addBack().promise().done(function() {
363 _disable_input_events = false;
367 self.nextSlide = function () {
368 self.setCurrentSlide(Math.min(self.getCurrentSlide()+1,
370 self.setCurrentFrame(self.firstFrame());
373 self.previousSlide = function () {
374 self.setCurrentSlide(Math.max(self.getCurrentSlide()-1,
376 self.setCurrentFrame(self.firstFrame());
379 self.getFrameInfo = function () {
381 var i = self.getCurrentSlide();
382 var canvas = $($(".sws-canvas")[i]);
383 var infos = canvas.data("sws-frame-info");
386 self.getCurrentFrame = function () { return self.getFrameInfo().current; };
387 self.setCurrentFrame = function (i) { self.getFrameInfo().current = i; };
388 self.firstFrame = function () { return 0; };
389 self.lastFrame = function () { return self.getFrameInfo().last; };
391 self.nextFrame = function () {
392 self.setCurrentFrame(Math.min(self.getCurrentFrame()+1,
396 self.previousFrame = function () {
397 self.setCurrentFrame(Math.max(self.getCurrentFrame()-1,
401 self.next = function () {
402 var i = self.getCurrentFrame();
403 if (i == self.lastFrame()) {
405 self.setCurrentFrame(self.firstFrame());
410 self.previous = function () {
411 var i = self.getCurrentFrame();
412 if (i == self.firstFrame()){
413 self.previousSlide();
414 self.setCurrentFrame(self.lastFrame());
417 self.previousFrame();
420 self.goToSlide = function (s, f) {
421 if (SWS.Utils.isUndefined(f))
423 if (!(s >= self.firstSlide() && s <= self.lastSlide())) return;
424 self.setCurrentSlide(s);
425 if (!(f >= self.firstFrame() && f <= self.lastFrame())) f = 0;
426 self.setCurrentFrame(f);
430 self.cycleStyle = function() {
431 var styles = $("head").children('link[rel$="stylesheet"][title]');
432 var j = styles.index(styles.filter(':not(:disabled)'));
433 styles[j].disabled = true;
434 if (++j == styles.length) j = 0;
435 styles[j].disabled = false;
438 self.inputHandler = function (event) {
439 if (_disable_input_events) return;
440 switch (event.which) {
442 self.setCurrentSlide(self.firstSlide());
446 self.setCurrentSlide(self.lastSlide());
451 if (self.getCurrentSlide() == self.lastSlide()
452 && self.getCurrentFrame() == self.lastFrame()) return;
455 case 34: /* PgDown */
459 case 8: /* backspace */
465 self.previousSlide();
471 $("#sws-control-panel-canvas").toggle();
480 function init_canvas(canvas, custom) {
482 var last_frame = canvas.find(".sws-pause").length;
483 //Add all regular elements to the frame list
484 var slide = $(canvas.find(".sws-slide")[0]);
486 var callbacks = { at_slide : new Array(),
487 at_frame : new Array() }
489 if (SWS.Utils.isUndefined(custom)) {
490 custom = new Array ();
493 for (var i = 0; i < custom.length; i++) {
494 if (isFinite(custom[i].frame)){
495 var num = +(custom[i].frame);
496 if (num > last_frame) last_frame = num;
497 SWS.Utils.push2(callbacks.at_frame, num, custom[i].fn);
498 } else if (custom[i].frame == "slide")
499 callbacks.at_slide.push(custom[i].fn);
501 var frame_set = SWS.Utils.parseFrameSpec(custom[i].frame);
502 for(var f in frame_set){
503 if (f > last_frame) last_frame = f;
504 SWS.Utils.push2(callbacks.at_frame, +(f), custom[i].fn);
511 slide.find('*[class*="sws-onframe-"]').each(function(_){
512 var cls = $(this).attr('class');
513 var idx = cls.indexOf("sws-onframe-");
515 var end = cls.indexOf(" ", idx);
516 end = (end == -1) ? cls.length : end;
517 var spec = cls.substring(idx+12, end);
518 var o = SWS.Utils.parseFrameSpec(spec);
520 if (f > last_frame) last_frame = f;
521 $(this).find("*").andSelf().each(function(_){
522 if (!SWS.Utils.isEmpty(o))
523 $(this).data("sws-frame-set", o);
525 specials.add($(this));
532 slide.find("*").andSelf().not(specials).each(function(i) {
533 if ($(this).hasClass("sws-pause")) cur_frame++;
535 for (var j = cur_frame; j <= last_frame; j++)
537 if (!SWS.Utils.isEmpty(o))
538 $(this).data("sws-frame-set", o);
541 canvas.data("sws-frame-info", { current: 0,
548 /* Forces redrawing the page without reloading */
549 self.redraw = function () {
554 self.changeAspect = function() {
555 $("html").removeClass("sws-aspect-4-3")
556 .removeClass("sws-aspect-16-9")
557 .removeClass("sws-aspect-16-10")
558 .addClass($("#sws-aspect-select").val());
563 self.changeTheme = function () {
564 var theme_name = $("#sws-theme-select").val();
565 $("link.sws-theme").each (function (i) {
567 var title = e.attr("title");
568 e[0].disabled = (title != theme_name);
574 var _fullscreen_icon_on = "";
575 var _fullscreen_icon_off = "";
577 self.toggleFullScreen = function () {
578 if (SWS.Fullscreen.status()) {
579 SWS.Fullscreen.exit();
580 $("a#sws-control-panel-fullscreen")
581 .html(_fullscreen_icon_off);
586 SWS.Fullscreen.enter($("body")[0]);
587 $("a#sws-control-panel-fullscreen")
588 .html(_fullscreen_icon_on);
591 function _update_ui() {
592 var nav = $('#sws-control-panel-navigation-bar');
593 nav.val(SWS.Presentation.getCurrentSlide() + 1);
594 $('#sws-control-panel-slide-input').val(nav.val());
596 self.navigate = function () {
597 self.goToSlide($("#sws-control-panel-navigation-bar").val()-1);
602 self.init = function () {
606 $(window).resize(self.redraw);
609 $(window).bind('storage', function (e) {
613 _total_slides = $(".sws-slide").length;
615 $(document).keydown(self.inputHandler);
618 var cur = self.getCurrentSlide();
619 $(".sws-slide").each (function (i) {
620 var par = $(this).parent();
623 var canvas = $('<div class="sws-canvas"/>');
625 if (!($(this).hasClass("sws-option-noheader"))) {
626 canvas.append($('<div class="sws-header"/><br/>'));
629 var inner = $('<div class="sws-inner-canvas"/>');
630 var content = $('<div class="sws-content"/>');
631 $(this).find('script[type="text/javascript"]').remove();
632 content.append($(this));
633 inner.append(content);
634 inner.append($('<span class="sws-vertical-align"> </span><br/>'));
635 canvas.append(inner);
637 if (!($(this).hasClass("sws-option-nofooter"))) {
638 canvas.append($('<div class="sws-footer"/>'));
645 .addClass("sws-active-slide")
646 .removeClass("sws-inactive-slide");
649 .addClass("sws-inactive-slide")
650 .removeClass("sws-active-slide");
652 init_canvas(canvas,_slide_callbacks[i]);
658 // Initialize the control panel
659 $("body").append($(SWS.Templates.controlPanel));
660 // Fill the theme switcher
661 $("link.sws-theme").each (function (i) {
663 var opt = "<option value='";
664 opt += e.attr("title");
666 if (e.attr("rel") == "stylesheet") {
667 opt+= "selected='selected'";
669 opt += ">" + e.attr("title") + "</option>";
670 $("#sws-theme-select").append($(opt));
672 // Set the fullscreen icon
673 if (SWS.Fullscreen.status()) {
674 $("a#sws-control-panel-fullscreen")
675 .html(_fullscreen_icon_on);
677 $("a#sws-control-panel-fullscreen")
678 .html(_fullscreen_icon_off);
680 // Put the navigation range at the correct position
681 var nav = $('#sws-control-panel-navigation-bar');
682 nav.attr("min", SWS.Presentation.firstSlide() + 1);
683 nav.attr("max", SWS.Presentation.lastSlide() + 1);
684 $('#sws-control-panel-total-slides').text('/' + SWS.Presentation.getNumSlides());
687 _slide_callbacks = null; /* avoid a leak */