 var Rotator = function(ul, options){
      var rotator = this;
      this.ul = ul;
      this.container = $(ul).parents('div').eq(0);
      this.options = options;
      this.rotationSlides = [];
      ul.children().not('.suite').each(function(){
        rotator.rotationSlides.push($(this));
      });
      this.rotationSlides.reverse();
      ul.children().not(this.rotationSlides[0]).hide();
      this.rotationSlides[0].show();
      if($.browser.msie) this.container.addClass('ie');
      this.rotationInterval = this.startTimeout = null;

      this.container.hover(
          function(){ rotator.stop(); },
          function(){ rotator.start(rotator.options['seconds to delay before restart'] * 1000); }
      );
      this.start();

      this.suiteControls = new SuiteControls(this.container.get(0), this.container.find('li.suite'), options['suite controls'], this);
    }
    Rotator.prototype.start = function(delay){
      var rotator = this;
      clearTimeout(this.startTimeout);
      clearInterval(this.rotationInterval);
      if(delay != undefined)
        this.startTimeout = setTimeout(function(){
			rotator.container.find('li').not(rotator.rotationSlides[0], rotator.currentInterrupt).css({zIndex: 1});
          	rotator.advance();
			rotator.start();
			}, delay)
      else
        this.rotationInterval = setInterval(function(){
          rotator.container.find('li').not(rotator.rotationSlides[0], rotator.currentInterrupt).css({zIndex: 1});
          rotator.advance();
        }, this.options['seconds per slide'] * 1000);
    }
    Rotator.prototype.stop = function(){
      clearInterval(this.rotationInterval);
      clearInterval(this.startTimeout);
    }
    Rotator.prototype.advance = function(){
      var currentSlide = this.currentInterrupt || this.rotationSlides[0],
          nextSlide = this.rotationSlides.pop(), rotator = this;
      this.rotationSlides.unshift(nextSlide);
      currentSlide.fadeOut(function(){
        rotator.ul.children().hide();
        nextSlide.fadeIn();
      });
      this.currentInterrupt = undefined;
      if(this.suiteControls.isOpen) this.suiteControls.close();
    }
    Rotator.prototype.interrupt = function(slide){
      this.stop();
      var currentSlide = this.currentInterrupt || this.rotationSlides[0]
          rotator = this;
      currentSlide.fadeOut(function(){
        rotator.ul.children().hide();
        $(slide).fadeIn();
      });
      this.currentInterrupt = $(slide);
    }

    var SuiteControls = function(root, suiteSlide, options, rotator){
      var categoryOrbRadius = (options['shield radius'] - options['category orb spacing']) / 2,
          widthOfPaper = $(root).innerWidth(),
          heightOfPaper = $(root).innerHeight(),
          paper = Raphael(root, widthOfPaper, heightOfPaper),
          suite = paper.set(),
          state = 'minimized',
          svgStylesToCopy = {
            strings: "cursor,fill,font,font-family,font-weight,stroke".split(','),
            ints: "fill-opacity,font-size,opacity,stroke-opacity,stroke-width".split(',')
          },
          productLinks = paper.set(),
          productsSet = paper.set(),
          categoryOrbs = paper.set(),
          suiteControls = this;

      var suiteShield = addSuiteContainer();
      var footnote = addFootnote();
      addCategories();
      setupSuiteOpenEvents();

      this.close = function(){
        productsSet.hide();
        productLinks.attr('fill', '#fff');
        this.isOpen = false
        mode = 'animating';
        suite.animate({transform: 's1,1,'+widthOfPaper+','+heightOfPaper}, 1000, 'elastic', function(){
          state = 'minimized';
        });
        footnote.animate({transform: 't0,0'}, 1000, 'linear');
        suiteShield.attr('cursor', 'pointer');
        for(var i=0,j=categoryOrbs.length; i<j; i++){
          categoryOrbs[i].locked = false;
          categoryOrbs[i].glowElement.hide();
        }
      }

      function x(val){ return widthOfPaper - val - 15; }
      function y(val){ return heightOfPaper - val; }

      function addSuiteContainer(){
        var shieldStrokeWidth = parseInt(suiteSlide.css('strokeWidth'), 10) || 0,
            suiteShield = paper.circle(
              x(options['shield radius'] + categoryOrbRadius),
              y(options['shield radius'] + shieldStrokeWidth / 2 + 30),
              options['shield radius']
            );
        copyStyles(suiteSlide, suiteShield);
        suiteShield.attr('cursor', 'pointer');
        suite.push(suiteShield);
        return suiteShield;
      }

      function addFootnote(){
        var suiteBox = suiteShield.getBBox(),
            footnote = paper.text(suiteBox.x + suiteBox.width / 2, suiteBox.y + suiteBox.height + 20, suiteSlide.data('title').replace(' / ','\n'));
        footnote.attr('fill', '#fff');
        footnote.attr('cursor', 'pointer');
        return footnote;
      }

      function addCategories(){
        var verticalOffsetOfOrbs = suiteShield.attr('cy'),
            horizontalOffsetOfOrbs = x(options['shield radius'] * 2 + categoryOrbRadius),
            horizontalIncrementOfOrbs = options['shield radius'];

        suiteSlide.find('ul.categories > li').each(function(i){
          buildCategoryOrb(
              $(this).data('title'), this, {
                x: horizontalOffsetOfOrbs + i * horizontalIncrementOfOrbs,
                y: verticalOffsetOfOrbs, r: categoryOrbRadius},
              $(this).find('ul.products > li'));
          $(this).insertAfter(suiteSlide).hide();
        });
      }

      function buildCategoryOrb(title, slide, position, products){
        var orb = paper.circle(position.x, position.y, position.r),
            orbSet = paper.set();
        copyStyles(slide, orb);
        orbSet.push(orb);
        categoryOrbs.push(orb);

        var orbLabel = paper.text(orb.attr('cx'), orb.attr('cy'), title);
        copyStyles(slide, orbLabel);
        orbLabel.attr('fill', $(slide).css('color'));
        orbSet.push(orbLabel);

        orb.glowElement = orb.glow({color: '#FFF',width:15});
        orb.glowElement.hide();
        orbSet.push(orb.glowElement);
        orbSet.hover(
            function(){if(state != 'animating' && state != 'minimized') orb.glowElement.show();},
            function(){
              if(orb.locked) return;
              orb.glowElement.hide();
            });
        orbSet.click(function(){
          if(state == 'animating' || state == 'minimized') return;
          for(var i=0,j=categoryOrbs.length; i<j; i++){
            if(categoryOrbs[i] == orb) continue;
            categoryOrbs[i].locked = false;
            categoryOrbs[i].glowElement.hide();
          }
          orb.locked = true;
          rotator.interrupt(slide);
        });

        addProducts(slide, orbSet);

        suite.push(orbSet);
        return orb;
      }

      function addProducts(category, orbSet){
        var productsSetForThisCategory = paper.set(),
            productLinksForThisCategory = paper.set();
        $(category).find('ul.products li').each(function(i){
          var link = paper.text(x(options['shield radius'] * 2 + categoryOrbRadius * 2 + 5), suiteShield.attr('cy') + categoryOrbRadius + 10 + i * 15 , $(this).data('title'));
          copyStyles(this, link);
            link.attr('stroke', 'none')
                                .attr('text-anchor', 'end')
                                .attr('font-weight', 'bold')
                                .attr('font-size', 7)
                                .hide();


          var slide = $(this);
          link.click(function(){
            rotator.interrupt(slide);
            productLinksForThisCategory.attr('fill', '#fff');
            link.attr('fill', '#FFD06C');
          });

          productsSetForThisCategory.push(link);
          productLinksForThisCategory.push(link);
          $(this).insertAfter(suiteSlide).hide();
        })
        productsSetForThisCategory.push(productLinksForThisCategory);
        suite.push(productsSetForThisCategory);
        productLinks.push(productLinksForThisCategory);
        productsSet.push(productsSetForThisCategory);

        orbSet.click(function(){
          if(state != 'animating' && state != 'minimized'){
            productsSet.hide();
            productLinks.attr('fill', '#fff');
            productsSetForThisCategory.show();
          }
        });
        addLineToProducts(productsSetForThisCategory, orbSet);
      }

      function addLineToProducts(productSet, orbSet){
            var orbBox = orbSet.getBBox(),
                productsBox = productSet.getBBox(),
                line = paper.path(
                      "M"+ (orbBox.x + 0.5 * orbBox.width) +","+ (orbBox.y + orbBox.height) +
                      "V"+ (productsBox.y + 0.5* productsBox.height) +
                      "H"+ (productsBox.x + productsBox.width + 12) +
                      "M"+ (productsBox.x + productsBox.width + 4 ) +","+ ( productsBox.y + 4 ) +""+
                      "h8" +
                      "v"+ (productsBox.height - 8) +
                      "h-8");

            line.attr('stroke', '#fff');
            line.hide();
            productSet.push(line);
      }

      function setupSuiteOpenEvents(){
        var open = function(){
          if(state != 'minimized') return;
          suiteControls.isOpen = true;
          state = 'animating';
          rotator.interrupt(suiteSlide);
          var footnoteOffset = heightOfPaper - footnote.getBBox().y;
          suite.animate({transform: 's'+options.magnification+','+options.magnification+','+widthOfPaper+','+heightOfPaper+'t0,'+footnoteOffset}, 400, 'backOut', function(){
            state = 'magnified';
          });
          footnote.animate({transform: 't0,'+footnoteOffset}, 400, 'linear');
          suiteShield.attr('cursor', 'default');
        }
        suite.click(open);
        footnote.click(open);
      }

      function copyStyles(from, to){
        from = $(from);
        for(var i=0,j=svgStylesToCopy.strings.length;i<j;i++){
          var style = svgStylesToCopy.strings[i];
          to.attr(style, from.css(style));
        }
        for(var i=0,j=svgStylesToCopy.ints.length;i<j;i++){
          var style = svgStylesToCopy.ints[i];
          to.attr(style, parseInt(from.css(style),10));
        }
      }
    }

    $(function(){
        new Rotator($('#message-rotator > ul'), {
            'seconds per slide': 7,
            'seconds to delay before restart': 2,
            'suite controls': {
              'shield radius': 50,
              'magnification': 2,
              'category orb spacing': 10
            }
        });
    })

