// ---------------------------------------------------------------------------
// (c) 2003 Geotalk, Hans Martin Mohn
// ---------------------------------------------------------------------------
//
// Add member variables and methods to the DOM element representing an IMG so
// that the IMG behaves like a slider knob.
//
// Dependencies:
//
gtAssert("addGTGlobalHandler != null");

//
// Parameters:
//
//    element       The DOM element representing the IMG for the knob
//    track         The DOM element representing the IMG for the slider track
//    image_down    The name of the image to display when the button is down.
//                  The image will be looked for in the same directory as the
//                  original image.
//    image_active  The name of the image to display when the mouse is inside the button.
//                  Or null if the button shouldn't change
//    tooltip       The string to display as a tooltip for the button.
//    disabled      Optional flag: if true, the button will start up in the disabled state
//
// Example:
//
//    gtBeSliderKnob(knob, "btnKnobDown.png", null, "Skyv for å zoome", false);

function gtBeSliderKnob(element, track, image_down, image_active, tooltip, disabled, min_value, max_value, horizontal)
{
  // When this function is called by the onload() method of an image, we will be called twice -
  // First when the "up"-image has loaded and then again for the "down"-image!
  if (typeof(element.m_image_up) != "undefined")
    return;

  // First, activate browser independent access and event handling so that they can be overridden later!
  addGTHandlers(element);
  addGTHandlers(track);
  element.style.cursor = (disabled) ? "auto" : "pointer";

  // Handle resizing of the track!
  // NB: This metod belongs to the track, not the knob as the rest of the methods here!
  track.onresize = function gtTrack_onresize()
  {
    var knob = this.m_knob;
    knob.m_origo_x = gtGetLeft(this);
    knob.m_origo_y = gtGetTop (this);

    // (P)recompute some positions
    if (knob.m_horizontal)
    {
      knob.m_slider_range  = gtGetWidth(this) - gtGetWidth(knob);
      knob.m_slider_min    = gtGetLeft(this);
      knob.m_slider_max    = knob.m_slider_min + knob.m_slider_range;
    }
    else
    {
      knob.m_slider_range  = gtGetHeight(this) - gtGetHeight(knob);
      knob.m_slider_min    = gtGetTop(this);
      knob.m_slider_max    = knob.m_slider_min + knob.m_slider_range;
    }
//    alert("track.onresize(" + this.id + ") max=" + knob.m_slider_max + ", min=" + knob.m_slider_min + ", range=" + knob.m_slider_range);
  }; // end track.onresize()
  
  
  
  track.gtMouseUp   = function gtTrack_gtMouseUp  (evt) { return this.m_knob.gtMouseUp  (evt); };
  track.gtMouseMove = function gtTrack_gtMouseMove(evt) { return this.m_knob.gtMouseMove(evt); };



  element.gtMouseDown = function gtSlider_gtMouseDown(evt)
  {
    if (this.m_disabled)
      return false;
      
    var x = evt.gtX - this.m_body_margin_x;
    var y = evt.gtY - this.m_body_margin_y;
    this.m_ref_pos = (this.m_horizontal) ? x - gtGetLeft(this) : y - gtGetTop(this); // Where on the knob did we click

//top.gtTraceLine("gtMouseDown(): x=" + x + ", y=" + y + ", m_ref_pos=" + this.m_ref_pos);
//window.status = "gtMouseDown(): x=" + x + ", y=" + y + ", m_ref_pos=" + this.m_ref_pos;
    this.m_button_down = true;
    this.src = this.m_image_down;
    if (this.onPress)
      this.onPress(this.getValue());

    gtSetMouseCapture(this, true);
    gtCancelBubble(evt);
    return gtPreventDefault(evt);
  }; // end gtMouseDown()



  element.gtMouseUp = function gtSlider_gtMouseUp(evt)
  {
    gtSetMouseCapture(this, false);
      
    if (this.m_disabled)
      return false;
      
    src = this.m_image_up;
    this.m_button_down = false;

    if (this.onRelease)
      this.onRelease(this.getValue());
      
    gtCancelBubble(evt);
    return gtPreventDefault(evt);
  }; // end gtMouseUp()


  
  element._snapTo = function gtSlider__snapTo(pos) // Snap to one of n steps
  {
    if (!this.m_snap_steps)
      return pos;

    var step_size = this.m_slider_range / this.m_snap_steps;
    var knob_pos  = this.getRelPos(pos);
    var step_pos  = Math.round(knob_pos / step_size);
    var distance  = Math.abs(step_size * step_pos - knob_pos);
    if (distance <= this.m_snap_dist)
    {
      var val = step_size * step_pos;
      var new_pos = this.getAbsPos(val);
//gtTraceLine("Returning snapped pos:" + new_pos + " was:" + pos + " step:" + step_size + " min:" + this.m_slider_min + " max: " + this.m_slider_max + " range:" + this.m_slider_range);
      return new_pos;
    }
    else
    {
//      status = "Returning original pos: " + pos;
      return pos;
    }
  }; // end _snapTo()
  
  
  
  element.gtMouseMove = function gtSlider_gtMouseMove(evt)
  {
    if (this.m_disabled)
      return false;

    if (!this.m_button_down)
      return gtPreventDefault(evt);

    var x = evt.gtX - this.m_body_margin_x;
    var y = evt.gtY - this.m_body_margin_y;
    var pos = (this.m_horizontal) ? x - this.m_ref_pos : y - this.m_ref_pos;
    
    // slider only works within bounds
    pos = Math.max(Math.min(pos, this.m_slider_max), this.m_slider_min);
//top.gtTraceLine("x=" + x + ", y=" + y + ", pos=" + pos + ", min=" + this.m_slider_min + ", max=" + this.m_slider_max);
//window.status = "x=" + x + ", y=" + y + ", pos=" + pos + ", min=" + this.m_slider_min + ", max=" + this.m_slider_max;

    pos = this._snapTo(pos); // Snap to one of m_snap_steps steps (or don't snap if m_snap_steps == 0)
    
    this.moveSliderToPos(pos);

    if (this.m_cur_pos != pos)
    {
      this.m_cur_pos = pos;
      if (this.onMove)
        this.onMove(this.getValue());
    }
    
    return gtPreventDefault(evt);
  }; // end gtMouseMove()
  


  element.gtMouseOver = function gtSlider_gtMouseOver()
  {
    if (this.m_disabled || this.m_busy || this.m_button_down)
      return false;
    if (this.m_image_active)
      this.src = this.m_image_active;
    return false; // no default action
  }; // end gtMouseOver()



  element.gtMouseOut = function gtSlider_gtMouseOut()
  {
    if (this.m_disabled || this.m_busy || this.m_button_down)
      return false;
    if (this.m_image_active !== null)
      this.src = this.m_image_up;
    return false; // no default action
  }; // end gtMouseOut()



  element.getValue = function gtSlider_getValue()
  {
    // min_value: bottom, max_value: top
    var knob_pos = this.getRelPos(this.m_cur_pos);
    var knob_pos_fraction = knob_pos / this.m_slider_range;

    var val = Math.round(this.m_min_value + knob_pos_fraction * this.m_range_value);
//gtTraceLine("getValue: knob_pos:" + knob_pos + " fraction:" + knob_pos_fraction + " value:" + val);
    return val;
  }; // end getValue()



  element.setValue = function gtSlider_setValue(value)
  {
    // set the knob's position on slider, based on value
    // min_value: bottom, max_value: top
    var value_diff = value - this.m_min_value;
    var fraction   = value_diff / this.m_range_value;
    var offset     = fraction * this.m_slider_range;

    var pos = this.getAbsPos(offset);
    pos = Math.max(Math.min(pos, this.m_slider_max), this.m_slider_min);
//top.gtTraceLine("setValue(" + value + ") max=" + this.m_slider_max + ", offset=" + offset);
    this.moveSliderToPos(pos);
    this.m_cur_pos = pos;
  }; // end setValue()



  element.moveSliderToPos = function gtSlider_moveSliderToPos(pos)
  {
    // Position is relative to the knob's container
//top.gtTraceLine("knob.moveSliderToPos(" + this.id + ", horizontal=" + this.m_horizontal + ") pos=" + pos);
    if (this.m_horizontal)
      this.style.left = pos - this.m_origo_x + "px"; // gtSetLeft(this, pos - this.m_origo_x);
    else
      this.style.top  = pos - this.m_origo_y + "px"; // gtSetTop (this, pos - this.m_origo_y);
//window.status = "knob.moveSliderToPos(" + this.id + ", horizontal=" + this.m_horizontal + ") pos=" + pos + ", coords: (" + gtGetLeft(this) + ", " + gtGetTop(this) + ")";
  }; // end moveSliderToPos()



  element.showDisabled = function gtSlider_showDisabled(disabled)
  {
    var transparency = (disabled) ? 0.3 : 0.0;
    gtSetTransparent(this, transparency);
    gtSetTransparent(this.m_track, transparency);
  }; // end showDisabled()



  element.setDisabled = function gtSlider_setDisabled(d)
  {
    this.m_disabled = d;
    this.showDisabled(this.m_busy || this.m_disabled);
  }; // end setDisabled()
  
  

  element.setBusy = function gtSlider_setBusy(b)
  {
    this.m_busy = b;
    this.showDisabled(this.m_busy || this.m_disabled);
  }; // end setBusy()

  

  element.setSnap = function gtSlider_setSnap(steps, distance)
  {
    this.m_snap_steps = steps;
    this.m_snap_dist  = distance;
  }; // end setSnap()
  


  if (horizontal)
  {
    element.getRelPos = function gtSlider_getRelPos(pos)
    {
      return pos - this.m_slider_min;
    }; // end getRelPos()

    element.getAbsPos = function gtSlider_getAbsPos(pos)
    {
      return pos + this.m_slider_min;
    }; // end getAbsPos()
  }
  else
  {
    element.getRelPos = function gtSlider_getRelPos(pos)
    {
      return this.m_slider_max - pos;
    }; // end getRelPos()

    element.getAbsPos = function gtSlider_getAbsPos(pos)
    {
      return this.m_slider_max - pos;
    }; // end getAbsPos()
  }
  

  // ==========================================================================
  // Constants
  // ==========================================================================

  gtBeSliderKnob.HORIZONTAL = true;
  gtBeSliderKnob.VERTICAL   = false;

  
  // ==========================================================================
  // Member attributes
  // ==========================================================================
//alert("gtBeSliderKnob(" + element.id + ", " + track.id + ")");
  track.m_knob            = element;
  element.m_horizontal    = (horizontal) ? true : false;
  element.m_min_value     = parseFloat(min_value);
  element.m_max_value     = parseFloat(max_value);
  element.m_range_value   = max_value - min_value;
  element.m_image_up      = element.src;
  element.m_track         = track;
  element.m_disabled      = false;  // Must be set explicitly (in order to be instantiated??)
  element.m_busy          = false;
  var s = top.document.body.style;
  element.m_body_margin_x = s.marginLeft.length > 0 ? parseInt(s.marginLeft) : 0;
  element.m_body_margin_y = s.marginTop.length  > 0 ? parseInt(s.marginTop)  : 0;
  element.m_body_margin_x = 0;  // DEBUG!!!
  element.m_body_margin_y = 0;  // DEBUG!!!
  element.title           = tooltip;
  element.alt             = tooltip;
  element.m_ref_pos       = 0;
  element.m_cur_pos       = 0;      // Previously reported value
  element.m_button_down   = false;
  element.m_snap_steps    = 0;
  element.m_snap_dist     = 3;

  var slash_pos = element.src.lastIndexOf("/");
  if (image_down !== null)
    element.m_image_down   = (slash_pos < 0) ? image_down   : element.src.substr(0, slash_pos + 1) + image_down;
  else
    element.m_image_down = null;
  if (image_active !== null)
    element.m_image_active = (slash_pos < 0) ? image_active : element.src.substr(0, slash_pos + 1) + image_active;
  else
    element.m_image_active = null;

  element.setDisabled((typeof(disabled) != "undefined") ? disabled : false);

  track.onresize();
  return element;
} // end gtBeSliderKnob()



// ===========================================================
// global helper functions
// ===========================================================

function double2Slider(v_min, v_max, value, s_min, s_max)
{
  value = Math.min(Math.max(value, v_min), v_max);
  return (Math.log(value) - Math.log(v_min)) / (Math.log(v_max) - Math.log(v_min)) * (s_max - s_min) + s_min;
} // end double2Slider()

function slider2Double(v_min, v_max, s_min, s_max, value)
{
  var lgMin = Math.log(v_min);
  var lgMax = Math.log(v_max);
  return Math.exp((value - s_min) / (s_max - s_min) * (lgMax - lgMin) + lgMin);
} // end slider2Double()


