(function($) {
   // 
   var slider = function (node, options) {
      var self = this;
      var defaults = {
         interval : 4000,
         transition : 800,
         reversed : false,
         autoplay : true,
         loadPanel : function (self, tab, onloaded) { onloaded(); },
         paused : function () { return false; },
         onSelected : null // function (slider, panel, tab) { }
      };
      var options = $.extend(defaults, options);  
      
      // caching
      var $slider = $(node);
      var $pc = $("ul.panels", $slider);
      var $panels = this.$panels = $("ul.panels li", $slider);
      var $tc = $("ul.tabs", $slider);
      var $tabs = this.$tabs = $("ul.tabs li", $slider);
      var tabHeight = $tabs.first().height();
      
      // linking tab & panels.
      $panels.each(function (i) { $(this).attr("num", i); });
      $tabs.each(function (i) { $(this).attr("num", i); });
      
      // selecting first tab.
      $panels.each(function (i) { $(this).hide(); }).first().show();
      $tabs.first().addClass("selected");
      
      // ticker
      var autoselect = true;
      
      // binding actions.
      $tc.hover(function () { autoselect = false; }, function () { autoselect = true; });
      $tabs.each(function (i) { $(this).mouseenter(function () { self.select($(this)); }); });
      
      // FUNCTIONS
      var s = false; // semaphore
      this.nextTab = function () {
         if (s) return;
         s=true;
         $tc.animate({top:'-='+tabHeight}, options.transition, function () { self.cycleTab(); s=false; });
         if (autoselect)
            self.select($tabs.first().next());
      };
      
      this.prevTab = function () {
         if (s) return;
         s=true;
         self.reverseCycleTab();
         $tc.animate({top:'+='+tabHeight}, options.transition, function () { s=false; });
         if (autoselect)
            self.select($tabs.first());
      };
      
      this.select = function ($tab) {
         options.loadPanel(self, $tab, function () { // default: nothing to do.
            var $currentPanel = self.getPanel(self.getSelectedTab().attr("num"));
            var $newPanel = self.getPanel($tab.attr("num"));
            if ($currentPanel.attr("num") == $newPanel.attr("num"))
               return;
            $currentPanel.fadeOut();
            $newPanel.fadeIn();
            self.getSelectedTab().removeClass("selected");
            $tab.addClass("selected");
            if (typeof options.onSelected == "function")
               options.onSelected($slider, $newPanel, $tab);
         });
      };
      
      // last => first
      this.reverseCycleTab = function () {
         $tabs.last().detach().insertBefore($tabs.first());
         $tc.offset({ 'top' : $pc.offset().top - tabHeight, 'left' : $tc.offset().left});
         $tabs = self.$tabs = $("ul.tabs li", $slider); // updating selector.
      };
      
      // first => last
      this.cycleTab = function () {
         $tabs.first().detach().insertAfter($tabs.last()); // appendTo($tc);
         $tc.offset({ 'top' : $pc.offset().top, 'left' : $tc.offset().left});
         $tabs = self.$tabs = $("ul.tabs li", $slider); // updating selector.
      };
      
      //
      this.play = function () {
         setInterval(function () { if (!options.paused()) { if (options.reversed) self.prevTab(); else self.nextTab(); } }, options.interval);
      };

      //
      this.getSelectedTab = function (i) { return $('li.selected', $tc); };
      this.getTab = function (i) { return $('li[num="'+i+'"]', $tc); };
      this.getPanel = function (i) { return $('li[num="'+i+'"]', $pc); };
      
      //
      if (options.autoplay)
         this.play();
   };

   // 
   $.fn.sliderVertical = function (options){
      if (this.length > 1)
         this.each(function () { $(this).sliderVertical(); });
      return new slider(this, options);
   };
})(jQuery)


