Add key 's' for cycling through stylesheets.
[hacks/simpleWebSlides.git] / simpleWebSlides.js
index 26ea8e2..10d5d03 100644 (file)
   Namespace object.
 */
 
-var SWS = {};
+var SWS = SWS || {};
 
-/* Utility functions */
-SWS.isUndefined = function (o) { return typeof o == "undefined"; };
 
-/*
-  Representation of intervals of positive integers.
-
-SWS.Interval = new function (init) {
 
+SWS.Utils = new function () {
     var self = this;
-    self.data = new Array();
-    self.parseString(s) {
+
+    self.isUndefined = function (o) { return typeof o == "undefined"; };
+    self.push2 = function (t, i, v) {
+        if ((typeof t[i]) == 'undefined') {
+            t[i] = new Array();
+        };
+        var l = t[i].length;
+        t[i][l] = v;
+    };
+
+    self.isEmpty = function (o) {
+        for(var _ in o) return false;
+        return true;
+    };
+
+    self.parseFrameSpec = function (s) {
         var elems = s.split("_");
+        var result = {};
+        var min_last = 10000;
+        var max_value = -1;
         for(var i = 0; i < elems.length; i++){
             var bounds = elems[i].split("-");
-            if (bounds.length > 2) continue;
-            if (SWS.isUndefined(bounds[1])) bounds[1] = bounds[0];
-            if (!(isFinite(bounds[0]) && isFinite(bounds[1]))) continue;
-
-            self.add(+(bounds[0]), +(bounds[1]));
+            if (bounds.length > 2 || bounds.length == 0) return {};
+            if (bounds.length == 1) bounds[1] = bounds[0];
+            var a = parseInt(bounds[0]);
+            var b = parseInt(bounds[1])
+            if (!isFinite(a) || !isFinite(b)) return {};
+            a = Math.min(a, 1000); // don't allow more than 1000 frames/slide
+            b = Math.min(b, 1000);
+            if (b > max_value) max_value = b;
+            for (var j = a; j <= b; j++)
+                result[j] = true;
         };
-
+        return result;
     };
-    self.add(x, y) {
-        if (SWS.isUndefined(y) && isFinite(x)) self.add(x,x);
 
 
+};
+
 
 
+
+SWS.Templates = new function () {
+    var self = this;
+    self.controlPanel = "<div id='sws-control-panel'>\
+<a onclick='SWS.Presentation.goToSlide(SWS.Presentation.firstSlide());'>◀◀◀</a>\
+<a onclick='SWS.Presentation.previousSlide();SWS.Presentation.refresh();'>◀◀ </a>\
+<a onclick='SWS.Presentation.previous();SWS.Presentation.refresh();'>◀</a>\
+<a onclick='SWS.Presentation.next();SWS.Presentation.refresh();'>▶</a>\
+<a onclick='SWS.Presentation.nextSlide();SWS.Presentation.refresh();'>▶▶</a>\
+<a rel='Last slide' onclick='SWS.Presentation.goToSlide(SWS.Presentation.lastSlide());'>▶▶▶</a>\
+</div>";
+    self.slideActivate = function (o) {
+        if (!(o.hasClass("sws-active-slide"))){
+            o.removeClass("sws-inactive-slide").addClass("sws-active-slide");
+        };
     };
-5B
 
+    self.slideDeactivate = function (o) {
+        if (!(o.hasClass("sws-inactive-slide"))){
+            o.removeClass("sws-active-slide").addClass("sws-inactive-slide");
+        };
+    };
 
+    self.slideChange = function (from, to) {
+        var canvas = $(".sws-canvas");
+        self.slideDeactivate($(canvas[from]));
+        self.slideActivate($(canvas[to]));
     };
-};
-*/
 
-SWS.Presentation = new function () {
+    self.objectActivate = function (o) {
+        if (!(o.hasClass("sws-active-object"))){
+            o.removeClass("sws-inactive-object").addClass("sws-active-object");
+            o.css({'visibility':'visible'});
+            return true;
+        };
+        return false;
+    };
 
+    self.objectDeactivate = function (o) {
+        if (!(o.hasClass("sws-inactive-object"))){
+            o.addClass("sws-inactive-object").removeClass("sws-active-object");
+            return true;
+        };
+        return false;
+    };
 
+    self.updateFooter = function (o) {
+        var footer = o.find(".sws-footer");
+        if (footer.length && (footer.children("*").length == 0)) {
+            var i = SWS.Presentation.getCurrentSlide();
+            var cur = $( "<span class='sws-current-slide-number'>"
+                         + (i + 1)
+                         +"</span>");
+            var sep = $( "<span class='sws-slide-num-sep' />");
+            var tot = $( "<span class='sws-last-slide-number'>"
+                         + (SWS.Presentation.getNumSlides())
+                         +"</span>");
+            footer.append(cur).append(sep).append(tot);
+        };
+    };
+    self.updateHeader = function (o) {};
+};
+SWS.ConfigBuilder = function () {
     var self = this;
+    self['sws-object-activate'] = SWS.Templates.objectActivate;
+    self['sws-object-deactivate'] = SWS.Templates.objectDeactivate;
+    self['sws-slide-change'] = SWS.Templates.slideChange;
+    self['sws-update-footer'] = SWS.Templates.updateFooter;
+    self['sws-update-header'] = SWS.Templates.updateHeader;
+};
 
-    var templates = {};
+SWS.Defaults = new SWS.ConfigBuilder ();
 
-    //TODO: clean-up;
-    var _total_slides;
-    var _defered_custom = new Array();
-    var _initialized = false;
-    var _animations_running = false;
+SWS.Config = new SWS.ConfigBuilder ();
 
-    //Utility functions
 
+SWS.Effects = new function () {
+    var self = this;
 
+    self.objectDeactivateFadeOut = function (o) {
+        o.animate({'opacity': '0'}, 200,
+                  function () { SWS.Templates.objectDeactivate(o)});
+    };
 
-    function push_following(t, i, v) {
-        if ((typeof t[i]) == 'undefined') {
-            t[i] = new Array();
+    self.objectActivateFadeIn = function (o) {
+
+        if (SWS.Templates.objectActivate(o)){
+            o.animate({'opacity': '1' }, 200);
         };
-        var l = t[i].length;
-        if (l == 0 || !($.contains(t[i][l-1].get()[0], v.get()[0]))) {
-            t[i][l] = v;
+
+    };
+
+    self.slideChangeHorizontalFlip = function (from, to){
+        var f = SWS.Presentation.getSlide(from);
+        var t = SWS.Presentation.getSlide(to);
+        f.animate({ 'left': '50%', 'width': '0pt', 'opacity':'0' }, 150,
+                  function  () {
+                      SWS.Templates.slideDeactivate(f);
+                      f.css({'left':'0%', 'width': '100%'});
+                      t.css({ 'left': '50%', 'width': '0pt','opacity':'0' });
+                      SWS.Templates.slideActivate(t);
+                      t.animate({'left':'0%', 'width': '100%','opacity':'1'});
+                  });
+    };
+    self.slideChangeFadeOutIn = function (from, to) {
+        var f = SWS.Presentation.getSlide(from);
+        var t = SWS.Presentation.getSlide(to);
+        f.animate({ 'opacity': '0'}, 150,
+                  function () { SWS.Templates.slideDeactivate(f);
+                                SWS.Templates.slideActivate(t);
+                                t.css('opacity', '0');
+                                t.animate({ 'opacity': '1'}, 150);
+                              });
+    };
+    self.slideChangeHorizontalSlide = function (from, to) {
+        var f = SWS.Presentation.getSlide(from);
+        var t = SWS.Presentation.getSlide(to);
+        if (from < to) {
+            t.css('left', '100%');
+            SWS.Templates.slideActivate(t);
+            f.animate({ 'left': '-100%' }, 250, function () { SWS.Templates.slideDeactivate(f); });
+            t.animate({ 'left': '0%' }, 250);
+        } else {
+            t.css('left', '-100%');
+            SWS.Templates.slideActivate(t);
+            f.animate({ 'left': '100%' }, 250, function () { SWS.Templates.slideDeactivate(f); });
+            t.animate({ 'left': '0%' }, 250);
         };
     };
 
-    function push(t, i, v) {
-        if ((typeof t[i]) == 'undefined') {
-            t[i] = new Array();
+
+    self.slideChangeVerticalSlide = function (from, to) {
+        var f = SWS.Presentation.getSlide(from);
+        var t = SWS.Presentation.getSlide(to);
+        if (from < to) {
+            t.css('top', '100%');
+            SWS.Templates.slideActivate(t);
+            f.animate({ 'top': '-100%' }, 250, function () { SWS.Templates.slideDeactivate(f); });
+            t.animate({ 'top': '0%' }, 250);
+        } else {
+            t.css('top', '-100%');
+            SWS.Templates.slideActivate(t);
+            f.animate({ 'top': '100%' }, 250, function () { SWS.Templates.slideDeactivate(f); });
+            t.animate({ 'top': '0%' }, 250);
         };
-        var l = t[i].length;
-            t[i][l] = v;
     };
 
+};
 
-    function init_canvas(canvas) {
-        var cur_frame = 0;
-        var last_frame = canvas.find(".sws-pause").length;
-        //Add all regular elements to the frame list
-        canvas.find("*").each(function(i) {
-            if ($(this).filter('*[class*="sws-onslide-"]').length) {
-            } else {
-                if ($(this).hasClass("sws-pause"))  cur_frame++;
-                var o = {};
-                for (var j = cur_frame; j <= last_frame; j++)
-                    o[ "f" + j ] = true;
-                $(this).data("sws-frame-set", o);
-            };
-        });
+SWS.Presentation = new function () {
 
-        canvas.data("sws-frame-info", { current: 0,
-                                    last: last_frame,
-                                    custom: new Array() });
 
-    };
+    var self = this;
+
+    //TODO move outside of the Presentation object
+
+
+    var _total_slides;
+    var _initialized = false;
+    var _disable_input_events = false;
 
+    var _slide_callbacks = new Array ();
 
-    //Public methods
-    self.setTemplate = function (tn, fn) { templates[tn] = fn; };
 
     self.getNumSlides = function () { return _total_slides; };
 
+    self.getSlide = function(i) {
+        return $($(".sws-canvas")[i]);
+    };
 
-    self.registerCustom = function (i, f) {
+    self.registerCallback = function (i, f) {
         if (_initialized) return;
         //jQuery does not seem to work well
-        //on partal doms
+        //on a partial DOM
 
-        var scripts = document.getElementsByTagName("script");
-        var current = scripts[scripts.length-1];
+        var slide_num = $(".sws-slide").length - 1;
 
-        var slide = $(current).parents(".sws-slide");
-        if (slide.length == 0) {
-            console.log("no parent ?");
-            return;
-        }
-        _defered_custom[_defered_custom.length] = { src: slide[0],
-                                                    fn: f,
-                                                    frame: i };
-        $(current).remove();
+        SWS.Utils.push2(_slide_callbacks, slide_num,{ 'fn': f, 'frame': i });
 
     };
 
     if (typeof(Storage)!=="undefined"){
         self.getCurrentSlide = function () {
             //unary + casts to integer
-            var i = +(sessionStorage.current_slide);
+            var i = +(sessionStorage.getItem("current_slide"));
             if (!(i >= 0 && i < self.getNumSlides())){
                 return 0;
             } else {
@@ -136,87 +242,80 @@ SWS.Presentation = new function () {
         };
 
         self.setCurrentSlide = function (i) {
-            sessionStorage.current_slide = i;
+            sessionStorage.setItem("current_slide", i);
         };
 
     } else {
         var _current_slide = 0;
-        self.getCurrentSlide = function () { return current_slide; };
+        self.getCurrentSlide = function () { return _current_slide; };
         self.setCurrentSlide = function (i) { _current_slide = i; };
 
     };
     self.firstSlide = function () { return 0; };
     self.lastSlide = function () { return self.getNumSlides() - 1; };
-
     self.refresh = function () {
-        var to_slide = $(".sws-canvas")[self.getCurrentSlide()];
-        var from_slide = $(".sws-active-slide")[0];
-
-        if (!(to_slide === from_slide)) {
-            templates['sws-template-slide-deactivate'](
-                $(from_slide),
-                function (o){
-                    o.removeClass("sws-active-slide")
-                        .addClass("sws-inactive-slide");
-                    templates['sws-template-slide-activate'](
-                        $(to_slide).removeClass("sws-inactive-slide")
-                            .addClass("sws-active-slide"),
-                        function (_){
-                        });
-                });
-
+        /* block upcoming input event until all animations are finished */
+        _disable_input_events = true;
+
+        var canvas = $(".sws-canvas");
+        var from_slide_num = canvas.index($(".sws-active-slide"));
+        var to_slide_num = self.getCurrentSlide();
+        var watch_slide_anim = false;
+        var to_slide = $(canvas[to_slide_num]);
+        var slide_change = (from_slide_num != to_slide_num);
+
+        var info = to_slide.data("sws-frame-info");
+        SWS.Config['sws-update-header'](to_slide);
+        SWS.Config['sws-update-footer'](to_slide);
+
+        if (slide_change) {
+            //Launch a slide transition:
+            SWS.Config['sws-slide-change'](from_slide_num, to_slide_num);
+            watch_slide_anim = true;
+            for (var i = 0; i < info.callbacks.at_slide.length;i++){
+                info.callbacks.at_slide[i](to_slide);
+            };
         };
-        var info = $(to_slide).data("sws-frame-info");
+
+
         var cur = info.current;
         var custom = info.custom;
+        var real_slide = to_slide.children(".sws-slide");
 
-        $(to_slide).find("*").each (function (i) {
-            var fcur = "f" + cur;
+        real_slide.find("*").andSelf().each(function (i){
             var frameset = $(this).data("sws-frame-set") || {};
-            if (frameset[fcur]) {
-                if (!($(this).hasClass("sws-active-frame"))) {
-                    templates['sws-template-frame-activate'](
-                        $(this).removeClass("sws-inactive-frame")
-                            .addClass("sws-active-frame"),
-                        function (_){ });
-
-                };
-
-            } else {
-                if (!($(this).hasClass("sws-inactive-frame"))) {
-                    templates['sws-template-frame-deactivate'](
-                        $(this),
-                        function (o){
-                            o.removeClass("sws-active-frame")
-                                .addClass("sws-inactive-frame");
-                        });
-
-                };
-
-
-            };
+            if (frameset[cur])
+                SWS.Config['sws-object-activate']($(this));
+            else
+                SWS.Config['sws-object-deactivate']($(this));
         });
-        _animation_running = true;
-        var callbacks;
 
-        if (callbacks = custom[self.getCurrentFrame()]){
+        var callbacks;
+        if (callbacks = info.callbacks.at_frame[self.getCurrentFrame()]){
             for (var k = 0; k < callbacks.length; k++)
                 callbacks[k]($(to_slide));
         };
 
-        $(to_slide).find("*").promise().done(function() {
-            _animation_running = false;
+        var to_watch = $(to_slide).find("*");
+        if (watch_slide_anim) {
+            to_watch = to_watch.add(to_slide).add($(canvas[from_slide_num]));
+        };
+
+        to_watch.find("*").promise().done(function() {
+            _disable_input_events = false;
         });
     };
 
     self.nextSlide = function () {
         self.setCurrentSlide(Math.min(self.getCurrentSlide()+1,
-                                       self.lastSlide()));
+                                      self.lastSlide()));
+        self.setCurrentFrame(self.firstFrame());
     };
 
     self.previousSlide = function () {
         self.setCurrentSlide(Math.max(self.getCurrentSlide()-1,
                                       self.firstSlide()));
+        self.setCurrentFrame(self.firstFrame());
     };
 
     self.getFrameInfo = function () {
@@ -243,34 +342,55 @@ SWS.Presentation = new function () {
 
     self.next = function () {
         var i = self.getCurrentFrame();
-        if (i == self.lastFrame())
+        if (i == self.lastFrame()) {
             self.nextSlide();
-        else
+            self.setCurrentFrame(self.firstFrame());
+        } else
             self.nextFrame();
     };
 
     self.previous = function () {
         var i = self.getCurrentFrame();
-        if (i == self.firstFrame())
+        if (i == self.firstFrame()){
             self.previousSlide();
+            self.setCurrentFrame(self.lastFrame());
+        }
         else
             self.previousFrame();
     };
 
+    self.goToSlide = function (s, f) {
+        if (SWS.Utils.isUndefined(f))
+            f = 0;
+        if (!(s >= self.firstSlide() && s <= self.lastSlide())) return;
+        self.setCurrentSlide(s);
+        if (!(f >= self.firstFrame() && f <= self.lastFrame())) f = 0;
+        self.setCurrentFrame(f);
+        self.refresh();
+    };
+
+    self.cycleStyle = function() {
+        var styles = $("head").children('link[rel$="stylesheet"][title]');
+        var j = styles.index(styles.filter(':not(:disabled)'));
+        styles[j].disabled = true;
+        if (++j == styles.length) j = 0;
+        styles[j].disabled = false;
+    };
+
     self.inputHandler = function (event) {
-        if (_animation_running) return;
+        if (_disable_input_events) return;
         switch (event.which) {
         case 36:/* Home */
-            self.firstSlide();
+            self.setCurrentSlide(self.firstSlide());
             break;
 
         case 35:/* End */
-            self.lastSlide();
+            self.setCurrentSlide(self.lastSlide());
             break;
 
         case 32: /* space */
         case 39: /* -> */
-        case 1: /* mouse button 1 */
+
             self.next();
             break;
         case 34: /* PgDown */
@@ -285,6 +405,11 @@ SWS.Presentation = new function () {
         case 80: /* p */
             self.previousSlide();
             break;
+        case 83: /* s */
+            self.cycleStyle();
+            return;
+        case 67: /* c */
+            $("#sws-control-panel").toggle();
         default:
             return;
         };
@@ -292,68 +417,118 @@ SWS.Presentation = new function () {
     };
 
 
-    function default_footer_template() {
-        var elem = '<div class="sws-footer"><span class="sws-current-slide-num">' + (i+1);
-        elem += '</span><span class="sws-slide-num-sep"/><span class="sws-total-slide-num">';
-        elem += self.getNumSlides() + '</span></div>';
-        return $(elem);
-    };
 
-    self.setTemplate('sws-template-footer', function (x) { });
-    self.setTemplate('sws-template-frame-activate', function (x, f) {f(x);});
-    self.setTemplate('sws-template-frame-deactivate', function (x, f) {f(x);});
-    self.setTemplate('sws-template-slide-activate',  function (x, f) {f(x);});
-    self.setTemplate('sws-template-slide-deactivate', function (x, f) {f(x);});
+    function init_canvas(canvas, custom) {
+        var cur_frame = 0;
+        var last_frame = canvas.find(".sws-pause").length;
+        //Add all regular elements to the frame list
+        var slide = $(canvas.children(".sws-slide")[0]);
+
+        var callbacks = { at_slide : new Array(),
+                          at_frame : new Array() }
 
+        if (SWS.Utils.isUndefined(custom)) {
+            custom = new Array ();
+        };
 
+        for (var i = 0; i < custom.length; i++) {
+            if (isFinite(custom[i].frame)){
+                var num = +(custom[i].frame);
+                if (num > last_frame) last_frame = num;
+                SWS.Utils.push2(callbacks.at_frame, num, custom[i].fn);
+            } else if (custom[i].frame == "slide")
+                callbacks.at_slide.push(custom[i].fn);
+            else {
+                var frame_set = SWS.Utils.parseFrameSpec(custom[i].frame);
+                for(var f in frame_set){
+                    if (f > last_frame) last_frame = f;
+                    SWS.Utils.push2(callbacks.at_frame, +(f), custom[i].fn);
+                };
+            }
+        };
 
-    function init () {
+        var specials = null;
+
+        slide.find('*[class*="sws-onframe-"]').each(function(_){
+            var cls = $(this).attr('class');
+            var idx = cls.indexOf("sws-onframe-");
+            if (idx >= 0) {
+                var end = cls.indexOf(" ", idx);
+                end = (end == -1) ? cls.length : end;
+                var spec = cls.substring(idx+12, end);
+                var o = SWS.Utils.parseFrameSpec(spec);
+                for(var f in o)
+                    if (f > last_frame) last_frame = f;
+                $(this).find("*").andSelf().each(function(_){
+                    if (!SWS.Utils.isEmpty(o))
+                        $(this).data("sws-frame-set", o);
+                    if (specials)
+                        specials.add($(this));
+                    else
+                        specials = $(this);
+                });
+            };
+        });
+
+        slide.find("*").andSelf().not(specials).each(function(i) {
+            if ($(this).hasClass("sws-pause"))  cur_frame++;
+            var o = {};
+            for (var j = cur_frame; j <= last_frame; j++)
+                o[ j ] = true;
+            if (!SWS.Utils.isEmpty(o))
+                $(this).data("sws-frame-set", o);
+        });
+
+        canvas.data("sws-frame-info", { current: 0,
+                                        last: last_frame,
+                                        callbacks: callbacks
+                                      });
+
+    };
+
+    self.init = function () {
+        console.log("inited");
+        $(window).bind('storage', function (e) {
+            console.log(e);
+        });
 
         _total_slides = $(".sws-slide").length;
 
         $(document).keydown(self.inputHandler);
-        $(document).mousedown(self.inputHandler);
-
+        $("body").append($(SWS.Templates.controlPanel));
         var cur = self.getCurrentSlide();
         $(".sws-slide").each (function (i) {
-            if (!($(this).hasClass("sws-option-nofooter"))) {
-                $(this).append(templates['sws-template-footer']());
-            };
             var par = $(this).parent();
+
             $(this).remove();
             var canvas = $('<div class="sws-canvas"/>');
+
+            if (!($(this).hasClass("sws-option-noheader"))) {
+                canvas.append($('<div class="sws-header"/>'));
+            };
+            $(this).find('script[type="text/javascript"]').remove();
             canvas.append($(this));
+            if (!($(this).hasClass("sws-option-nofooter"))) {
+                canvas.append($('<div class="sws-footer"/>'));
+            };
             par.append(canvas);
+
             if (i == cur) {
                 canvas
                     .addClass("sws-active-slide")
-                    .removeClass("sws-inacitve-slide");
+                    .removeClass("sws-inactive-slide");
             } else {
                 canvas
                     .addClass("sws-inactive-slide")
                     .removeClass("sws-active-slide");
             };
-            init_canvas(canvas);
+            init_canvas(canvas,_slide_callbacks[i]);
 
         });
-        for (var i = 0; i < _defered_custom.length; i++){
-
-            var custom = _defered_custom[i];
-            var canvas = $(custom.src).parents(".sws-canvas");
-            if (canvas.length == 0) continue;
-            canvas = $(canvas[0]);
-
-            var info = canvas.data("sws-frame-info");
-
-            push(info.custom, custom.frame, custom.fn);
-        };
+        _slide_callbacks = null; /* avoid a leak */
         self.refresh();
         _initialized = true;
 
     };
 
-    self.init = function () {
-        $(document).ready(init);
-    };
-
 };