// <![CDATA[

//common functions
function xn_finishEvent(e)
{
  if (e.preventDefault)
  {
   e.preventDefault();
   e.stopPropagation();
  }
  else
  {
   e.returnValue = false;
   e.cancelBubble = true;
  }
}

function xn_getEventElement(e)
{
  if (!e) e = window.event;
  if (e.originalTarget)
  {
    return e.originalTarget;
  }
  else
  {
    if (e.srcElement)
    {
      return e.srcElement;
    }
  }
}

/* new slider functions concept

handlesHeight and trackHeight must be stored in vars

sliders
[i] {
      track
        height
        x,y
        
      handle
        height
        
      handle
        height
    }

getEventCoordinates(event) -- return both x and y
getLocalEventCoordinates(event) -- return both x and y mapped from inside of track

handleGetRealCoordinates(slider, index) -- return both: x and y
handleGetVirtualCoordinates(slider, index) -- return controlPoints of handles
                                               they could be in center of handle or at it's bottom or top
handleGetRealSizes() -- return real width and height

trackGetRealCoordinates() -- return real both x and y

dragHandle


when setting position of handle we should use delta... for making pointer to be in center of handle

////////*/



// initializtion + global variables
var sliders = new Array();
var isDragging = 0;
var currentDragInfo = new Object();
//


function getHandlesCPCoords(slider)
{
  var currCoordName = "offset" + slider.direction[1];
  var newCoords = new Array(2);
  var coordMod = (slider.controlPoint == "outside")? 1 : 0;
  
  //get property name part like height or width for use later with client+Height = clientHeight
  var prop = slider.direction[2];
  for(i = 0; i < 2; i++)
  {
    var coord = new Object();
    coord.x = slider.handles[i][currCoordName]; //realcoordinate
    coord.mx = coord.x; //mapped coordinate
    
    if (i != coordMod)
    {
      coord.mx = coord.x + (slider.handles[i]["client" + prop]*(coordMod-i));
    }
    
    newCoords[i] = coord;
  }
  
  return newCoords;
}

function getTrackCPCoords(slider)
{
  var currCoordName = "offset" + slider.direction[1];
  var newCoords = new Array(2);
  var coordMod = (slider.controlPoint == "outside")? 1 : 0;
  //get property name part like height or width for use later with client+Height = clientHeight
  var prop = slider.direction[2];
  
  i = 0; //top
  var coord = new Object();
  coord.x = slider.track[currCoordName]; //realcoordinate
  coord.mx = coord.x; //mapped coordinate
  if (i != coordMod)
  {
    coord.mx = coord.x + (slider.handles[i]["client" + prop]*(i-coordMod));
  }
  newCoords[i] = coord;
  
  i = 1; //bottom
  var coord = new Object();
  coord.x = slider.track[currCoordName]+slider.track["client"+slider.direction[2]]; //realcoordinate
  coord.mx = coord.x; //mapped coordinate
  if (i != coordMod)
  {
    coord.mx = coord.x - slider.handles[i]["client" + prop];
  }
  newCoords[i] = coord;
  
  return newCoords;
}

function xn_setSliderValueByHandle(slider, hIndex, value)
{
  var rangeStart = slider.range[0];
  var rangeEnd = slider.range[1];
  
  var trackCPCoords = getTrackCPCoords(slider);
  var maxX = trackCPCoords[1].x - trackCPCoords[0].x;
  
  checkValueByRange(value, [rangeStart, rangeEnd], true);
  
  var rangeLength = rangeEnd-rangeStart;
  var coeffX = maxX/rangeLength;
  var newX = (value-rangeStart)*coeffX;
  if (hIndex == 0) newX -= slider.handles[hIndex]["client" + slider.direction[2]];
  setHandleX(slider, [hIndex], newX);
  slider.handles[hIndex].sliderValue = value;
}

function checkHandlesRange(slider, x, indexes, fix)
{
  //check if x is in allowed area for movement
  // according to second handle position
  // if fix = true then return nearest allowed value
  var trackCPRange = getTrackCPCoords(slider);
  
  if (indexes[0] == 0)
  {
    // current is an upper handle
    //so we should calculate range from top of track
    //to x of bottom handle
    var rangeStart = trackCPRange[0].mx;
    var rangeEnd   = trackCPRange[0].x + slider.handles[1]["offset"+slider.direction[1]] - slider.handles[1]["client"+slider.direction[2]];
    if (slider.distance)
    {
      rangeEnd -= slider.distance;
    }
  }
  else
  {
    //else active handle is bottom one
    //so range is from upper_handle bottom point to track bottom
    var rangeStart   = trackCPRange[0].x + slider.handles[0]["offset"+slider.direction[1]]+slider.handles[0]["client"+slider.direction[2]];
    var rangeEnd     = trackCPRange[1].mx;
    if (slider.distance)
    {
      rangeStart += slider.distance;
    }
  }
  
  return (checkValueByRange (x, [rangeStart, rangeEnd], fix) - trackCPRange[0].x);
}

function changeValueByRange(value, range)
{
  return checkValueByRange(value, range, true);
}

function checkValueByRange(value, range, fix)
{
  var minValue = range[0];
  var maxValue = range[(range.length-1)];
  
  if (value < minValue)
  {
    if (fix) value = minValue;
    else value = false;
  }
  if (value > maxValue)
  {
    if (fix) value = maxValue;
    else value = false;
  }
  
  return value;
}

function xn_setSliderValue(slider, value)
{
  if (!slider) return false;
  currentCoord = getHandlesCPCoords(slider);
  
  var hCount = slider.handles.length;
  
  for (i2 = 0; i2<hCount; i2++)
  {
    if (!isNaN(value[i2]))
    {
      value[i2] = changeValueByRange(value[i2], slider.range); //check if value is higher than or otherwise lower
      xn_setSliderValueByHandle(slider, [i2], value[i2]);
    }
  }
}

function getNearestHandle(slider, x) //return index of handle
{
  var coords = getHandlesCPCoords(slider);
  var cx1 = coords[0].mx;
  var cx2 = coords[1].mx;
  var handleIndex = 0;
  
  var x1delta = Math.abs(x-cx1);
  var x2delta = Math.abs(x-cx2);
  
  if (x1delta < x2delta)
  {
    handleIndex = [0,1]; //active, inactive
  }
  else
  {
    handleIndex = [1,0]; //active, inactive
  }
  
  return handleIndex;
}

function setHandleX(slider, handleIndex, x)
{
  var localHandle = slider.handles[handleIndex[0]];
  
//  var trackX = slider.track["offset" + slider.direction[1]];
//  var trackLength = slider.track["client" + slider.direction[2]];
  
  var newX = Math.round(x);//checkHandlesRange(slider, x, handleIndex, true);
  
  localHandle.style[slider.direction[1].toLowerCase()] = newX+"px";
  
  return newX;
}

function convertSliderXtoValue(slider, x)
{
  var rangeStart = slider.range[0];
  var rangeEnd = slider.range[1];
  
  var trackCPCoords = getTrackCPCoords(slider);
  var maxX = trackCPCoords[1].x - trackCPCoords[0].x; 
  
//  document.getElementById('debug2').innerHTML = "<br>maxX:" + maxX;
  
  var coeffValue = 0;
  if (maxX != 0)
  {
    coeffValue = x / maxX;
  }
  else
  {
    coeffValue = 0;
  }
  
  var result = rangeStart + ((rangeEnd-rangeStart)*coeffValue);
  
  return result;
}

function slideIntHandleFromX(slider, handleIndex, x)
{
  x = setHandleX(slider, handleIndex, x);
  //slide to nearest integer value
  var mappedX = getHandlesCPCoords(slider);
  var value = Math.round(convertSliderXtoValue(slider, mappedX[handleIndex[0]].mx));
  var value2 = Math.round(convertSliderXtoValue(slider, mappedX[handleIndex[1]].mx));
  
  if (value == value2)
  {
    if (handleIndex[0] == 0)
    {
      value--;
    }
    else
    {
      value++;
    }
  }
  //document.getElementById("debug1").innerHTML = "val: "+value;
  xn_setSliderValueByHandle(slider, handleIndex[0], value);
}

function getEventX(slider, e)
{
	if (e.pageX || e.pageY)
	{
		eventX = e["page" + slider.direction[0]];
	}
	else if (e.clientX || e.clientY)
	{
		eventX = e["client" + slider.direction[0]] + document.body["scroll" + slider.direction[1]] + document.documentElement["scroll" + slider.direction[1]];
	}
	return eventX;
}

function setHandleXByEvent(e)
{
  var slider = sliders[currentDragInfo.sliderIndex];
  if (slider.disabled == true) return;
  var handleIndex = currentDragInfo.handleIndex;
  
  eventX = getEventX(slider, e);
	
  eventX = checkHandlesRange(slider, eventX, currentDragInfo.handleIndex, true);
  
  if (slider.behavior == "slideInteger" && e.type == "mouseup")
  {
    slideIntHandleFromX(slider, currentDragInfo.handleIndex, eventX);
  }
  if (slider.behavior == "none" || e.type != "mouseup")
  {
    setHandleX(slider, currentDragInfo.handleIndex, eventX);
    
    var mappedX = getHandlesCPCoords(slider);
    var value = convertSliderXtoValue(slider, mappedX[handleIndex[0]].mx);
    
    slider.handles[handleIndex[0]].sliderValue = value;
  }
  if (e.type == "mouseup")
  {
    var mappedX = getHandlesCPCoords(slider);
    var value = convertSliderXtoValue(slider, mappedX[handleIndex[0]].mx);
    if (slider.onchange) slider.onchange(slider.handles[handleIndex[0]].sliderValue, slider, handleIndex[0]);
  }
  if (e.type == "mousemove")
  {
    if (slider.onslide && !isNaN(value))
    {
      slider.onslide(slider.handles[handleIndex[0]].sliderValue, slider, handleIndex[0]);
    }
  }
}

function addSlider(trackId, handleId, options)
{
  var handles = new Array();
  var slider = new Object();
  
  var newSliderIndex = sliders.length;
  var track = document.getElementById(trackId);
  
  options.behavior = options.behavior || "none";
  options.distance = options.distance || 0;
  options.align    = options.align || "vertical";
  options.range    = options.range || [1,1];
//  options.parking  = options.parking || "outside"; //"inside" is not working properly yet
  options.parking  = "outside"; //"inside" is not working properly yet
  options.values   = options.values || [];
  
  if (typeof(handleId) == "object")
  {
    for (i = 0; i<2; i++) //for now only two slider handles allowed
    {
      var tempHandle = document.getElementById(handleId[i]);
      
      options.values[i] = options.values[i] || options.range[0];
      options.values[i] = changeValueByRange(options.values[i], options.range);
      
      tempHandle.sliderValue = options.values[i];
      
      tempHandle.handleIndex = i;
      
      handles[i] = tempHandle;
    }
  }
  else
  {
    handles[0] = document.getElementById(handleId);
    handles[0].sliderValue = options.range[0];
    handles[0].handleIndex = 0;
  }
  
  if (!newSliderIndex)
  {
    // in future should be replaced by some kind of global add Listener not just assign
    // coz there could be a listener before.. we should not clear the one that belongs not to us
    document.onmouseup = listenMouseOnTrack;
    document.onmousemove = listenMouseOnTrack;
  }
  
  track.onmouseout = listenMouseOnTrack;
  track.onmouseover = listenMouseOnTrack;
  track.onmousedown = listenMouseOnTrack;
  
  track.sliderIndex = newSliderIndex;
  
  //fill slider object
  slider.onchange = options.onchange;
  slider.onslide = options.onslide;
  slider.behavior = options.behavior;
  slider.details = options.details || new Object();
  slider.distance = options.distance;
  slider.range = options.range;
  slider.track = track;
  slider.handles = handles;
  slider.controlPoint = options.parking || "inside";
  slider.direction = (options.align == "vertical")? ["Y","Top", "Height"] : ["X","Left","Width"];
  //use here FirstCapped names coz it easier to lowerCase all word than upperCase just first letter
  
  sliders[newSliderIndex] = slider;
  
  xn_setSliderValue(slider, options.values);
  
  return slider;
}

function listenMouseOnTrack(e)
{
  if (!e)
  {
    e = event;
  }
  var element = xn_getEventElement(e);
  var eType = e.type;
  
  if (eType == "mouseover")
  {
    
  }
  else
  {
    if (eType == "mouseout")
    {
      
    }
  }
  
  if (eType == "mousedown"  && e.button <= 1 )
  {
    var handleIndex = new Array;
    if (isNaN(element.sliderIndex)) //if hitted element is not a slider
    {
      //dig until will find a sliderTrack and work further.... or find body and exit
      if (!isNaN(element.handleIndex))
      {
        handleIndex[0] = element.handleIndex;
        handleIndex[1] = (element.handleIndex)^1;
      }
      
      x = true;
      for (i=0; x; i=0)
      {
        if (!element.parentNode) return;
        if (element.tagName == "BODY") return;
        
        element = element.parentNode;
        if (!isNaN(element.sliderIndex)) x = false;
      }
    }
    
    var slider = sliders[element.sliderIndex];
    
    if (isNaN(handleIndex[0]))
    {
      var eventX = getEventX(slider, e);
      var trackX = slider.track["offset" + slider.direction[1]];
      handleIndex = getNearestHandle(slider, (eventX-trackX));
    }
    
    isDragging = 1;
    currentDragInfo.sliderIndex = element.sliderIndex;
    currentDragInfo.handleIndex = handleIndex;
    
    setHandleXByEvent(e);
    xn_finishEvent(e);
  }
  if (eType == "mouseup" && e.button <= 1)
  {
    if (isDragging == 1)
    {
      isDragging = 0;
      setHandleXByEvent(e);
    }
    isDragging = 0;
  }
  if (eType == "mousemove")
  {
    if (isDragging == 1)
    {
      setHandleXByEvent(e);
      xn_finishEvent(e);
    }
  }
}

// ]]>
