/*
 * jQuery Slider
 * author:     Johannes Wüller
 * created on: 08.10.2009
 *
 * This sliding-mechanism is capable of scrolling the content by moving the
 * mouse over certain areas or holding certain buttons pressed.
 *
 * Usage:
 *    jQuery('.slider').mediaSlider(options);
 *
 * Options:
 *    [selector] leftButton   scroll to the left on mousedown.
 *    [selector] rightButton  scroll to the right on mousedown.
 *    [selector] leftArea     scroll to the left on mouseover.
 *    [selector] rightArea    scroll to the right on mouseover.
 *    [integer]  buttonSpeed  scrolling speed when clicking buttons.
 *    [integer]  areaSpeed    scrolling speed when hovering areas.
 *    [float]    falloff      scrolling fall off.
 *    [string]   falloffType  'linear' or 'exponential'.
 *    [bool]     continuous   wether the slider should be endless scrollable
 *    [bool]     autoScroll   wether the slider should scroll automatically
 *                            (only works if continuous is true)
 *    [integer]  autoSpeed    scrolling speed when automated scrolling is active
 *    [selector] entry        .find() selector to determine auto scrolling
 */
jQuery.fn.mediaSlider = function(options) {

   /*
    * options
    */
   var defaults = {
      buttonSpeed: 20,
      areaSpeed:   5,
      falloff:     0.93,
      falloffType: 'exponential',
      continuous:  false,
      autoScroll:  false,
      autoSpeed:   1
   };
   options = jQuery.extend(defaults, options);

   /*
    * slider
    */
   jQuery(this).each(function () {

      /*
       * variables
       */
      var velocity         = 0;            // how fast we are moving
      var velocityInterval = false;        // reference to animation interval
      var slider           = jQuery(this); // reference to this at entry point
      var slideOut         = false;        // wether to apply the falloff

      /*
       * functions
       */
      // actual animation
      var applyVelocity = function () {
         if (slideOut) {
            velocity = applyFalloff(velocity);
         }
         var newPos = slider[0].scrollLeft + parseInt(velocity);
         if (options.continuous) {
            var entries = slider.find(options.entry);
            var entryWidth = jQuery(entries[0]).outerWidth(true);
            if (newPos > entryWidth) {
               newPos -= entryWidth;
               var element = jQuery(entries[0]);
               jQuery(entries[entries.length - 1]).after(element.clone());
               element.remove();
            }
            if (newPos < 0) {
               newPos += entryWidth;
               var element = jQuery(entries[entries.length - 1]);
               jQuery(entries[0]).before(element.clone());
               element.remove();
            }
         }
         slider[0].scrollLeft = newPos;
         if (slideOut && velocity == 0) {
            slideOut = false;
            velocity = 0;
            clearVelocityInterval();
         }
      };

      // create animation interval
      var setVelocityInterval = function () {
         slideOut = false;
         clearVelocityInterval();
         velocityInterval = window.setInterval(function(){
            applyVelocity();
         }, 13); // ~60fps
      };

      // remove animation interval
      var clearVelocityInterval = function () {
         window.clearInterval(velocityInterval);
         velocityInterval = false;
      };

      // enable fall off
      var stopSliding = function () {
         slideOut = true;
      };

      // calculate fall off
      var applyFalloff = function(velocity) {
         if (options.falloffType == 'exponential') {
            if (velocity != 0) {
               velocity *= options.falloff;
               if (parseInt(velocity) == 0) {
                  velocity = 0;
               }
            }
         } else if (options.falloffType == 'linear') {
            if (velocity < 0) {
               velocity += options.falloff;
               if (velocity > 0) {
                  velocity = 0;
               }
            } else if (velocity > 0) {
               velocity -= options.falloff;
               if (velocity < 0) {
                  velocity = 0;
               }
            }
         }
         return velocity;
      };

      /*
       * bindings
       */
      if (options.leftButton && !options.autoScroll) {
         jQuery(options.leftButton).mousedown(function () {
            velocity = options.buttonSpeed * -1;
            setVelocityInterval();
         }).mouseup(function () {
            stopSliding();
         }).hover(function () {}, function () {
            stopSliding();
         });
      }
      if (options.rightButton && !options.autoScroll) {
         jQuery(options.rightButton).mousedown(function () {
            velocity = options.buttonSpeed;
            setVelocityInterval();
         }).mouseup(function () {
            stopSliding();
         }).hover(function () {}, function () {
            stopSliding();
         });
      }
      if (options.leftArea && !options.autoScroll) {
         jQuery(options.leftArea).hover(function () {
            velocity = options.areaSpeed * -1;
            setVelocityInterval();
         }, function () {
            stopSliding();
         });
      }
      if (options.rightArea && !options.autoScroll) {
         jQuery(options.rightArea).hover(function () {
            velocity = options.areaSpeed;
            setVelocityInterval();
         }, function () {
            stopSliding();
         });
      }

      /*
       * automated sliding
       */
      if (options.continuous && options.autoScroll && options.autoSpeed && options.entry) {
         velocity = options.autoSpeed;
         setVelocityInterval();
      }

   });

};

/**
 * This slider is used to slide the content step-wise.
 *
 * Usage:
 *    jQuery('.slider').clickSlider(options);
 *
 * Options:
 *    leftButton      anything that is accepted by jQuery(), scroll to the left
 *                    on click
 *    rightButton     anything that is accepted by jQuery(), scroll to the right
 *                    on click
 *    duration        integer, scrolling duration when clicking buttons (in
 *                    milliseconds)
 *    visibleEntries  integer, number of entries that are visible at the same
 *                    time
 *    entrySelector   .find() selector to determine a set of content elements,
 *                    relative to the slider parent
 */
(function ($) {
   $.fn.stepSlider = function(options) {

      // Merge with defaults.
      options = $.extend({
         leftButton:     null,
         rightButton:    null,
         duration:       250,
         visibleEntries: 1,
         entrySelector:  null
      }, options);

      // Discard invalid configurations.
      if (options.entrySelector === null) {
         throw "No entry selector specified.";
      }

      // Turn each element of the current set into a slider.
      $(this).each(function () {
         // Keep some useful references.
         var $this = $(this),
            entries = $this.find(options.entrySelector),
            entryWidth = entries.outerWidth(true),
            position = 0,
            maxPosition = Math.max(entries.length - options.visibleEntries, 0),

            /**
             * Slides the slider (heh) to the specified position.
             */
            slideTo = function (targetPos) {
               $this.stop().animate({
                  scrollLeft: Math.round(entryWidth * targetPos) + 'px'
               }, options.duration);
            };

         // Bind everything.
         if (options.leftButton !== null) {
            $(options.leftButton).click(function (e) {
               e.preventDefault();
               position = Math.max(position - 1, 0);
               slideTo(position);
            });
         }

         if (options.rightButton !== null) {
            $(options.rightButton).click(function (e) {
               e.preventDefault();
               position = Math.min(position + 1, maxPosition);
               slideTo(position);
            });
         }

         // Reset.
         $this.scrollLeft(0);
      });

   };


}(jQuery));

