/*
 * jQuery dragscrollable Plugin
 * version: 1.0 (25-Jun-2009)
 * Copyright (c) 2009 Miquel Herrera
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 */
/* altered to call scrollchanged if delta has a value */
;(function($){ // secure $ jQuery alias

/**
 * Adds the ability to manage elements scroll by dragging
 * one or more of its descendant elements. Options parameter
 * allow to specifically select which inner elements will
 * respond to the drag events.
 * 
 * options properties:
 * ------------------------------------------------------------------------    
 *  dragSelector         | jquery selector to apply to each wrapped element 
 *                       | to find which will be the dragging elements. 
 *                       | Defaults to '>:first' which is the first child of 
 *                       | scrollable element
 * ------------------------------------------------------------------------    
 *  acceptPropagatedEvent| Will the dragging element accept propagated 
 *                       | events? default is yes, a propagated mouse event 
 *                       | on a inner element will be accepted and processed.
 *                       | If set to false, only events originated on the
 *                       | draggable elements will be processed.
 * ------------------------------------------------------------------------
 *  preventDefault       | Prevents the event to propagate further effectivey
 *                       | dissabling other default actions. Defaults to true
 * ------------------------------------------------------------------------
 *  
 *  usage examples:
 *
 *  To add the scroll by drag to the element id=viewport when dragging its 
 *  first child accepting any propagated events
 *  $('#viewport').dragscrollable(); 
 *
 *  To add the scroll by drag ability to any element div of class viewport
 *  when dragging its first descendant of class dragMe responding only to
 *  evcents originated on the '.dragMe' elements.
 *  $('div.viewport').dragscrollable({dragSelector:'.dragMe:first',
 *                    acceptPropagatedEvent: false});
 *
 *  Notice that some 'viewports' could be nested within others but events
 *  would not interfere as acceptPropagatedEvent is set to false.
 *    
 */
$.fn.dragscrollable = function( options ){
   
  var settings = $.extend(
    {   
      dragSelector:'>:first',
      acceptPropagatedEvent: true,
            preventDefault: true,
      scrollChanged: null
    },options || {});
   
  
  var dragscroll= {
    mouseDownHandler : function(event) {
      // mousedown, left click, check propagation
      if (event.which!=1 ||
        (!event.data.acceptPropagatedEvent && event.target != this)){ 
        return false; 
      }
      
      // Initial coordinates will be the last when dragging
      event.data.lastCoord = {left: event.clientX, top: event.clientY}; 
    
      $.event.add( document, "mouseup", 
             dragscroll.mouseUpHandler, event.data );
      $.event.add( document, "mousemove", 
             dragscroll.mouseMoveHandler, event.data );
      if (event.data.preventDefault) {
                event.preventDefault();
                return false;
            }
    },
    mouseMoveHandler : function(event) { // User is dragging
      // How much did the mouse move?
      var delta = {left: (event.clientX - event.data.lastCoord.left),
             top: (event.clientY - event.data.lastCoord.top)};
      
      // Set the scroll position relative to what ever the scroll is now
      event.data.scrollable.scrollLeft(
              event.data.scrollable.scrollLeft() - delta.left);
      event.data.scrollable.scrollTop(
              event.data.scrollable.scrollTop() - delta.top);
      
      // call the event if we have one
      if (event.data.scrollChanged != undefined && event.data.scrollChanged != null)
      {
            event.data.scrollChanged.call(event.data.scrollable, event);
      }
      
      // Save where the cursor is
      event.data.lastCoord={left: event.clientX, top: event.clientY}
      if (event.data.preventDefault) {
                event.preventDefault();
                return false;
            }

    },
    mouseUpHandler : function(event) { // Stop scrolling
      $.event.remove( document, "mousemove", dragscroll.mouseMoveHandler);
      $.event.remove( document, "mouseup", dragscroll.mouseUpHandler);
      if (event.data.preventDefault) {
                event.preventDefault();
                return false;
            }
    }
  }
  
  // set up the initial events
  this.each(function() {
    // closure object data for each scrollable element
    var data = {scrollable : $(this),
          acceptPropagatedEvent : settings.acceptPropagatedEvent,
                    preventDefault : settings.preventDefault,
                scrollChanged : settings.scrollChanged }
    // Set mouse initiating event on the desired descendant
    $(this).find(settings.dragSelector).
            bind('mousedown', data, dragscroll.mouseDownHandler);
  });
}; //end plugin dragscrollable

})( jQuery ); // confine scope
