/* 
 *  NAME
 *    createSlider
 *  SYNOPSIS
 *    var slider = createSlider(cfg) ;
 *   
 *  AUTHOR:           Stergios Marinopoulos, stergios_marinopoulos at yahoo dot com
 *  CREATION DATE:    3/1/2007
 *  HISTORY:
 *  IDEAS:
 *
 *
 *  // Configuration object literal:
 *
 *      var cfg = {
 *        containerDiv:      'sliderContainerDiv',   // Surrounding container.  Both Thumbs must be inside.
 *        leftHandleDiv:     'leftHandleDiv',        // Left thumb.  Set it's back ground to the image you want.
 *        rightHandleDiv:    'rightHandleDiv',       // Right thumb.  Set it's back ground to the image you want.
 *        groupName:         'smSlider',             // A unique name.
 *        minValue:          0,                      // The minimum value for the slider range.
 *        maxValue:          100,                    // The maximum value for the slider range.
 *        callBack:          callBack,               // The call back function for when new values are set.  Values are on the range [minValue, maxValue]
 *        stepCount:         10,                     // The minimum increment required for a thumb move. 
 *        allowEqual:        true,                   // (Default==true) Are the 2 handles allowed to have the same value?
 *                                                   //  Useful for when the two hanles cannot have the same value.  Thanks to 
 *                                                   //  William Paoli for the idea!
 *        leftMoveCallBack:  mouseMoveCallBackLeft,  // Optional. The call back function for left mouse move events.   Values are on the range [minValue, maxValue]
 *        rightMoveCallBack: mouseMoveCallBackRight, // Optional. The call back function for right mouse move events.  Values are on the range [minValue, maxValue]
 *        leftDisplay:       'yearLeftDisplay',      // Optional. Id of element which has the left thumb value displayed in.
 *        rightDisplay:      'yearRightDisplay',     // Optional. Id of element which has the right thumb value displayed in.
 *        formatFunction:    'priceFormatFunction'   // Optional. Function to format slider values before in placing them in the left or right display.
 *
 *      };
 *
 *
 *  // Create and initiate a new slider:
 *
 *      var slider = createSlider(cfg) ;
 *      slider.init() ;
 *
 *  //  Reset a slider with new min and max values, and reset the thumb handles.
 *      slider.reset(newMinValue, newMaxValue) ;
 *
 *
 *  // Call Back Examples:
 *
 *      var callBack = function (v1, v2, sliderContainer) {
 *        console.log(v1 + ' -- ' + v2 + ' -- ' + sliderContainer) ;
 *      };
 *
 *      var mouseMoveCallBackLeft = function (v, sliderContainer) {
 *        console.log("Mouse Move Left CB: %o for ", v, sliderContainer) ;
 *      };
 *
 *      var mouseMoveCallBackRight = function (v, sliderContainer) {
 *        console.log("Mouse Move Right CB: %o for ", v, sliderContainer) ;
 *      };
 *
 *  // CSS examples:
 *      
 *      #sliderContainerDiv4
 *      {
 *        position: absolute;
 *        top:200px; left:600px; width:200px; height:20px;
 *        background:url(/img/horizSlider_background.gif) 0 0 repeat-x; 
 *      }
 *      #leftHandleDiv4
 *      {
 *        position: absolute;
 *        top:0px; left:0px; width:18px; height:18px;
 *        background:url(/img/horizSlider.png) 0 0 no-repeat; 
 *        color: #F00; 
 *        border: 0;
 *      }
 *      #rightHandleDiv4
 *      {
 *        position: absolute;
 *        top:0px; right:0px; width:18px; height:18px;
 *        background:url(/img/horizSlider.png) 0 0 no-repeat; 
 *        color: #F00; 
 *        border: 0;
 *      }
 *
 *
*/
var createSlider = function(cfg) {

  var cs ; // = cfg ; // configSlider
  var leftThumb = null, rightThumb = null;
  var handleDragged = null ;
  var bb ; // = document.getElementById(cs.containerDiv);
  var leftThumbDiv ; // = document.getElementById(cs.leftHandleDiv);
  var thumbRegion ; // = YAHOO.util.Dom.getRegion(leftThumbDiv) ;
  var thumbWidth ; // = thumbRegion.right - thumbRegion.left ;
  var containerRegion ; // = YAHOO.util.Dom.getRegion(bb) ;
  var containerWidth ; // = containerRegion.right - containerRegion.left ;
  var containerLeft ; // = containerRegion.left ;
  var span ; // = containerWidth - 2*thumbWidth ;
  var tickCount ; // = 1 ;
  var constraintAdjust = 0 ;
  var rightThumbDiv ; //= document.getElementById(cs.rightHandleDiv);
  var leftThumbValue ;
  var rightThumbValue ;

  var resetLeftConstraint = function(ltx, rtx) {
    // (ltx, rts) = X positions of the the handles in page coordinates.
    var leftThumbTravel2Left  = Number(ltx - containerLeft).toFixed(0) ;
    var leftThumbTravel2Right = Number(rtx - ltx - thumbWidth ).toFixed(0) ;
    // QQQQ console.log("\tleftThumbTravel2Left = %d", leftThumbTravel2Left) ;
    // QQQQ console.log("\tleftThumbTravel2Right = %d", leftThumbTravel2Right) ;
    if ( ! cs.allowEqual ) {
      leftThumbTravel2Right -= constraintAdjust ; 
    }
    leftThumb.setXConstraint(leftThumbTravel2Left, leftThumbTravel2Right, Math.floor(tickCount) );
    leftThumb.resetConstraints(true);  

  };

  var resetRightConstraint = function (ltx, rtx) {
    // (ltx, rts) = X positions of the the handles in page coordinates.
    var rightThumbTravel2Left  = Number(rtx - ltx - thumbWidth).toFixed(0) ;
    var rightThumbTravel2Right = Number((containerLeft + containerWidth) - ( rtx + thumbWidth )).toFixed(0) ;
    // QQQQ console.log("\trightThumbTravel2Left = %d", rightThumbTravel2Left) ;
    // QQQQ console.log("\trightThumbTravel2Right = %d", rightThumbTravel2Right) ;
    if ( ! cs.allowEqual ) {
      rightThumbTravel2Left -= constraintAdjust ; 
    }
    rightThumb.setXConstraint(rightThumbTravel2Left, rightThumbTravel2Right, Math.floor(tickCount) );
    rightThumb.resetConstraints(true);  

  };

  return {

    init: function() {

      YAHOO.util.DragDropMgr.clickPixelThresh = 1 ;

      cs = cfg ; // configSlider
      leftThumbValue = cs.minValue ;
      rightThumbValue = cs.maxValue ;
 
      var configDD = {scroll:false};

      bb = document.getElementById(cs.containerDiv);
      bb.mm_cfg = cfg ;
      leftThumbDiv = document.getElementById(cs.leftHandleDiv);
      leftThumb = new YAHOO.util.DD(cs.leftHandleDiv, cs.groupName, configDD);

      thumbRegion = YAHOO.util.Dom.getRegion(leftThumbDiv) ;
      thumbWidth = thumbRegion.right - thumbRegion.left ;

      containerRegion = YAHOO.util.Dom.getRegion(bb) ;
      containerWidth = containerRegion.right - containerRegion.left ;
      containerLeft = containerRegion.left ;

      // QQQQ console.log("containerWidth = %d", containerWidth) ;
      // QQQQ console.log("containerLeft = %d", containerLeft) ;
      // QQQQ console.log("thumbWidth = %d", thumbWidth) ;
      // QQQQ console.log("containerRegion = %o", containerRegion ) ;

      span = containerWidth - 2*thumbWidth ;
      // QQQQ console.log("span = %d", span) ;
      tickCount = 1 ;

      if ( cs['leftDisplay'] ) {
        bb.mm_cfg.leftDisplayElem = document.getElementById(cs.leftDisplay);
        if ( cs['formatFunction'] ) {
          bb.mm_cfg.leftDisplayElem.innerHTML = cs.formatFunction(cs.minValue) ;
        } else {
          bb.mm_cfg.leftDisplayElem.innerHTML = cs.minValue ;
        }

      }
      if ( cs['rightDisplay'] ) {
        bb.mm_cfg.rightDisplayElem = document.getElementById(cs.rightDisplay);
        if ( cs['formatFunction'] ) {
          bb.mm_cfg.rightDisplayElem.innerHTML = cs.formatFunction(cs.maxValue) ;
        } else {
          bb.mm_cfg.rightDisplayElem.innerHTML = cs.maxValue ;
        }
      }

      if ( cs['stepCount'] ) {
        if ( cs.stepCount > 1 ) {
          tickCount = span / cs.stepCount ;
          // QQQQ console.log("tickCount: %o:", tickCount) ;

          tickCount = tickCount.toFixed(0) ;
          // QQQQ console.log("tickCount (after toFixed(1)): %o:", tickCount) ;
          constraintAdjust = tickCount / 2;
          constraintAdjust = constraintAdjust.toFixed(0) ;
        }
      } 
      if (! cs.hasOwnProperty('allowEqual') ){
        cs['allowEqual'] = true ;
      }

      var leftTravel2Right = containerWidth - 2 * thumbWidth ;
      // QQQQ console.log("leftTravel2Right = %d", leftTravel2Right) ;
      if ( ! cs.allowEqual ) {
        leftTravel2Right -= constraintAdjust ; 
      }

      leftThumb.setXConstraint(0, leftTravel2Right, Math.floor(tickCount));
      leftThumb.setYConstraint(0, 0, 0);


      rightThumbDiv = document.getElementById(cs.rightHandleDiv);
      var rightTravel2Left = containerWidth - 2 * thumbWidth ;
      // QQQQ console.log("rightTravel2Left = %d", rightTravel2Left) ;

      rightThumb = new YAHOO.util.DD(cs.rightHandleDiv, cs.groupName, configDD);
      if ( ! cs.allowEqual ) {
        rightTravel2Left -= constraintAdjust ; 
      }
      rightThumb.setXConstraint(rightTravel2Left, 0, Math.floor(tickCount));
      rightThumb.setYConstraint(0, 0, 0);

      var mapValue = function(p)
      {
        var base = containerLeft + thumbWidth ;
        var v = (p - base)/span * (cs.maxValue - cs.minValue) + cs.minValue ;

        if ( cs['stepCount'] ) {
          // compute the stepInterval
          var stepInterval = (cs.maxValue - cs.minValue) / cs.stepCount ;
          // convert to a zero minimum base
          var zeroBase = v - cs.minValue ;
          // divide by stepInterval to get SI Units
          var stepIntervalUnits =  zeroBase / stepInterval ;
          // round to the proper number of step intervals
          var stepIntervalUnitsRounded = Math.round ( stepIntervalUnits ) ;
          // convert back to original range
          v = stepIntervalUnitsRounded * stepInterval + cs.minValue ;
        } 

        return v ;
      };

      var endDrag = function(e)
      {
        // QQQQ console.log("endDrag: event: %o", e);
        var x = YAHOO.util.Dom.getX(handleDragged) ;
        var y = YAHOO.util.Dom.getY(handleDragged) ;
        // QQQQ console.log("\t(x,y) (%d,%d)", x,y) ;

        if ( leftThumbDiv === handleDragged) {

          // QQQQ console.log("Reseting rightThumb constraints");
          var rtx01 = YAHOO.util.Dom.getX(rightThumbDiv) ;

          resetRightConstraint(x, rtx01) ;

          leftThumbValue = mapValue(x + thumbWidth);
          cs.callBack( leftThumbValue, rightThumbValue, cs.containerDiv, false, bb) ;


        } else if ( rightThumbDiv === handleDragged) {

          // QQQQ console.log("Reseting leftThumb constraints");
          var ltx01 = YAHOO.util.Dom.getX(leftThumbDiv) ;

          resetLeftConstraint(ltx01, x) ;

          rightThumbValue = mapValue(x) ;
          cs.callBack( leftThumbValue, rightThumbValue, cs.containerDiv, false, bb) ;
        }

        //handleDragged.style.background = "url(img/horizSlider.png)" ;

      } ;
      rightThumb.endDrag   = endDrag ;
      leftThumb.endDrag    = endDrag ;

      var startDrag = function(x,y)
      {
        //handleDragged.style.background = "url(img/horizSlider_on.png)" ;
      } ;

      rightThumb.startDrag = startDrag ;
      leftThumb.startDrag  = startDrag ;

      var mouseDown = function(e)
      {
        // QQQQ console.log("mouseDown: event: %o", e);
        handleDragged = YAHOO.util.Event.getTarget(e) ;
        // QQQQ console.log("\telem: %o", handleDragged) ;
        var x = YAHOO.util.Dom.getX(handleDragged) ;
        var y = YAHOO.util.Dom.getY(handleDragged) ;
        // QQQQ console.log("\t(x,y) (%d,%d)", x,y) ;
      } ;

      rightThumb.onMouseDown = mouseDown ;
      leftThumb.onMouseDown  = mouseDown ;

      var mouseMove = function(e) 
      {
        var x_mm ;
        // QQQQ var xMapped ;

        if ( handleDragged === leftThumbDiv ) {

          x_mm = YAHOO.util.Dom.getX(leftThumbDiv) ;
          var tmpLeftThumbValue = mapValue(x_mm + thumbWidth);
          // QQQQ xMapped = tmpLeftThumbValue ;
          cs.leftMoveCallBack( tmpLeftThumbValue, cs.containerDiv, bb ) ;

        } else if ( handleDragged === rightThumbDiv ) {

          x_mm = YAHOO.util.Dom.getX(rightThumbDiv) ;
          var tmpRightThumbValue = mapValue(x_mm) ;
          // QQQQ xMapped = tmpRightThumbValue ;
          cs.rightMoveCallBack( tmpRightThumbValue, cs.containerDiv, bb ) ;

        }
        // QQQQ console.log("mouseMove: event: %o\n\telem: %o\n\t(x: %o)", e, h, x_mm);
        // QQQQ console.log("mouseMove: (xMapped: %o)", xMapped);

      } ;

      if ( cs['leftMoveCallBack'] ) {
        leftThumb.onDrag  = mouseMove ;
      }
      if ( cs['rightMoveCallBack'] ) {
        rightThumb.onDrag = mouseMove ;
      }

    },


    reset: function(min, max, newStepCount) {
      cs.minValue = min;
      cs.maxValue = max;
      if ( newStepCount > 1 ) {
        tickCount = span / newStepCount ;
        tickCount = tickCount.toFixed(1) ;
        constraintAdjust = tickCount / 2;
        constraintAdjust = constraintAdjust.toFixed(0) ;
      } else {
        tickCount = 1; 
      }
      this.setValue(min, 'left') ;
      this.setValue(max, 'right') ;
    },

    setValue: function(x, whichHandle) {
      // QQQQ console.log("slider.setValue x: %o, handle: %o", x, whichHandle) ;
      if ( whichHandle === 'left' ) {

        if ( x < cs.minValue ) {
          x = cs.minValue ; 
        }
        var base = containerLeft + thumbWidth ;
        var ltx02 = (x - cs.minValue) * span / (cs.maxValue - cs.minValue)  + base - thumbWidth ;
        var rtx02 = YAHOO.util.Dom.getX(rightThumbDiv) ;
        // QQQQ console.log("ltx: %o, rtx: %o, delta: %o", ltx02, rtx02, rtx02 - ltx02) ;
        if ( ltx02 + thumbWidth > rtx02 ) {
          // cannot set the left handle before the right
          // QQQQ console.log("skipping...") ;
//          return ;
        }
        YAHOO.util.Dom.setX(leftThumbDiv, ltx02) ;
        leftThumbValue = x ;
        resetRightConstraint(ltx02, rtx02) ;
        resetLeftConstraint (ltx02, rtx02) ;  // Positions of the handles

        if ( cs['leftDisplay'] ) {
          bb.mm_cfg.leftDisplayElem.innerHTML = x ;
        }

      } else if ( whichHandle === 'right' ) {

        if ( x > cs.maxValue ) {
          x = cs.maxValue ; 
        }
        var base = containerLeft + thumbWidth ;
        var rtx03 = (x - cs.minValue) * span / (cs.maxValue - cs.minValue)  + base ;
        var ltx03 = YAHOO.util.Dom.getX(leftThumbDiv) ;  // getX() works in page coordinates
        // QQQQ console.log("ltx03: %o, rtx03: %o, delta: %o", ltx03, rtx03, rtx03 - ltx03) ;
        if ( ltx03 + thumbWidth > rtx03 ) {
          // cannot set the right handle past the left
          // QQQQ console.log("skipping...") ;
          return ;
        }
        YAHOO.util.Dom.setX(rightThumbDiv, rtx03) ;  // setX() works in page coordinates
        rightThumbValue = x ;

        resetLeftConstraint (ltx03, rtx03) ;  // Positions of the handles
        resetRightConstraint(ltx03, rtx03) ;  // Positions of the handles

        if ( cs['rightDisplay'] ) {
          bb.mm_cfg.rightDisplayElem.innerHTML = x ;
        }

      }

    },

    getValues: function() {
      return [leftThumbValue, rightThumbValue] ;
    },


    getRange: function() {
      return [cs.minValue, cs.maxValue] ;
    }


  };
}; 
