/*

(C)2008 B-Lex IT

- slider
- knop
- progress (to show how much of a file is cached)
- cuepoints

TODO:
- event listeners instead of using DOM0 event support
- no use of globals?
*/

function blexScrollSlider(options)
{
  this.container     = options.container;
  this.scrollelement = options.scrollelement;     // parentelement which it will scroll
  this.orientation   = options.orientation;

  this.ishorizontal  = (this.orientation=='horizontal');

  this.dom = {};
  var container   = document.createElement('div');

  if (this.ishorizontal)
    container.className = 'blexscroller blexscrollerHorizontal';
  else
    container.className = 'blexscroller blexscrollerVertical';

  this.dom.container = container;

  var _ssinstance = this; // ref to scrollslider instance

  this.dom.btnup = document.createElement('a'); // we use <a> for IE6
  this.dom.btnup.className = 'btnscrollup';
  this.dom.btnup.onclick = function()
                           {
                             _ssinstance.slider.setValue(_ssinstance.slider.curVal-32);
                           }

  this.dom.btndown = document.createElement('a');
  this.dom.btndown.className = 'btnscrolldown';
  this.dom.btndown.onclick = function()
                           {
                             _ssinstance.slider.setValue(_ssinstance.slider.curVal+32);
                           }

  this.slider = new spcSlider(
      { cssclass:     'slider'
      , orientation:  this.orientation
      , isrange:      true

      , name:         'ToScroll' // for debugging
      , minVal:       0
      , curVal:       this.scrollelement.scrollTop
      , maxVal:       this.scrollelement.scrollHeight
      , rangeSize:    this.scrollelement.clientHeight

      , showcached:   false
      , cuepoints:    {}

      , dragimportant: true  // slider value cannot be overriden while the user is dragging

      , ondrag:       function()
                      {
//                      console.log('Slider '+this.name+' is being dragged.');
                        _ssinstance.scrollelement.scrollTop = this.curVal;//slider.curVal;
                      }

      , ondragend:    function()
                      {
                      }

      , onupdate:     function()
                      {
                        _ssinstance.scrollelement.scrollTop = this.curVal;
                      }
      });

  this.dom.slider = this.slider.create();

  container.appendChild(this.dom.btnup);
  container.appendChild(this.dom.slider);
  container.appendChild(this.dom.btndown);
//  this.container.appendChild(container); // (this.container != this.dom.container)
  this.container.insertBefore(container, this.container.firstChild);

  if (toddUserAgentIsIE)
  {
    this.IEfix();

    // prevent textual selection of the mediaitems while dragging
    this.container.onselectstart = function() { return false; } // FIXME: nicer way to do this?
  }

  this.slider.redraw(); // redraw now the slider can determine the height it gains from the CSS

  // Force updating the scrollbar when the scrollelement changes ------------------------------

  // IE
  this.scrollelement.onresize = function()
                                {
                                  _ssinstance.IEfix();
                                  _ssinstance.update();
                                };

  // Firefox (and also Opera 7.2+, Safari 1.3+, Konqueror 3.4+ ?)
  // http://developer.mozilla.org/En/DOM_Events
  if (window.addEventListener)
    this.scrollelement.addEventListener( 'DOMSubtreeModified'
                                       , function() { _ssinstance.update(); }
                                       , false);

  // Allow scrolling with the mousewheel ------------------------------------------------------

  // see http://www.quirksmode.org/dom/events/scroll.html

  // IE6+, SF, OP (not Firefox)
  this.container.onmousewheel = function(evt)
                                {
                                  _ssinstance.onwheel(evt);
                                  return false;
                                };

  // Firefox
  if (window.addEventListener)
    this.container.addEventListener( 'DOMMouseScroll'
                                   , function(evt)
                                     {
                                       _ssinstance.onwheel(evt);
                                       evt.preventDefault();
                                     }
                                   , false);
}

blexScrollSlider.prototype.IEfix = function blexScrollSlider_IEfix()
{
  var containerheight = this.dom.container.parentNode.clientHeight;
  this.dom.container.style.height = (containerheight-4)+'px';
  this.dom.slider.style.height    = (containerheight-4-44)+'px';
}

blexScrollSlider.prototype.update = function blexScrollSlider_update()
{
  this.slider.curVal = this.scrollelement.scrollTop
  this.slider.maxVal = this.scrollelement.scrollHeight
  this.slider.redraw();
}

blexScrollSlider.prototype.onwheel = function blexScrollSlider_onwheel(evt)
{
  if (!evt) evt = window.event;
//if(!e) e = this.parentWindow.event;

  var scrinfo = toddExplainScrollWheel(evt)

  this.slider.setValue(this.slider.curVal+(scrinfo.delta * 8));
}


function blexSlider(options) {

  this.options    = options;

  this.name       = 'blexSlider'+(options.name!=undefined)?options.name:'';

  this.cssclass   = options.cssclass;

  this.orientation = options.orientation;
  this.ishorizontal = (this.orientation=='horizontal');
  this.minVal     = options.minVal;
  this.curVal     = options.curVal;
  this.maxVal     = options.maxVal;

  this.showcached = options.showcached ? options.showcached : false;
  this.amountcached = 0;

  this.isrange    = options.isrange    ? options.isrange : false;
  this.rangeSize  = options.rangeSize;
  this.sizecompensation = null; // will be filled with size of padding+border


  this.cuepoints  = options.cuepoints  ? options.cuepoints : [];
  this.cuepointsvisible = true;

  this.disabled   = false;
  this.isdragging = false;
  this.showsteps  = false;

  this.onupdate   = options.onupdate;
  this.ondrag     = options.ondrag;

  this.dragimportant = options.dragimportant; // does a drag override any setValue() calls?
}

blexSlider.prototype.updateoptions = function blexSlider_updateoptions(options)
{
  // TODO: check existance of properties
  for(var propname in options)
    this[propname] = options[propname];

  this.setCachedPercentage(0); // FIXME: is this the best way?

  this.drawCuepoints();
  this.redraw();
}

blexSlider.prototype.create = function blexSlider_create()
{
  this.slider = document.createElement('div');
  this.knob   = document.createElement('div');

  this.slider.className = "toddSliderBar"+(this.cssclass ? ' '+this.cssclass:'');

  this.handle_class = this.isrange?'toddSliderRange':'toddSliderKnob';

  this.knob.className = this.handle_class;

  this.slider.appendChild( this.knob );

  if (this.showcached)
  {
    this.cachebar = document.createElement('div');
    this.cachebar.className = "toddSliderCacheProgress";
    this.slider.appendChild(this.cachebar);
  }

  this.slider.instance = this;
  this.slider.onmousedown = this.dragStart;       // drag when click on an empty spot in the slider

  // prevent textual selection of the mediaitems while dragging (in IE)
  this.slider.onselectstart = function() { return false; } // FIXME: nicer way to do this?

  if (this.showsteps)
    this.drawParts();

  this.redraw();

  return this.slider;
}

blexSlider.prototype.destroy = function blexSlider_destroy()
{
  this.slider.parentNode.removeChild( this.slider );
  this.slider.instance = null;
  this.slider = null;
  this.knob = null;
}

blexSlider.prototype.onmouseover = function blexSlider_onmouseover()
{
  if (slider._instance) return;
  this.className = "toddSliderBar toddSliderBarHover"+(si.cssclass ? ' '+si.cssclass:'');
  this.instance.knob.className = this.handle_class+" hover";
}

blexSlider.prototype.onmouseout  = function blexSlider_onmouseout()
{
  if (slider._instance) return;
  this.className = "toddSliderBar"+(si.cssclass ? ' '+si.cssclass:'');;
  this.instance.knob.className = this.handle_class;
}

blexSlider.prototype.setValue = function blexScrollSlider_setValue(newvalue)
{
  if((this.dragimportant && this.isdragging) || this.curVal == newvalue)
    return;

  this.curVal = newvalue

  this.update();
}

blexSlider.prototype.setCachedPercentage = function blexSlider_setCachedPercentage(percentage)
{
  var si = this;

  if (percentage < 0)
    percentage=0;

  if (percentage > 100)
    percentage=100;

  /*
        var slWidth = Math.round(percentage * this.slider.clientWidth / 100);
        console.log('Slider cache %'+percentage+ '('+slWidth+' pixels)');
        this.cachebar.style.width = slWidth + 'px';
  */

  if (this.ishorizontal)
    this.cachebar.style.width = percentage+'%';
  else
    this.cachebar.style.height = percentage+'%';
}

blexSlider.prototype.update = function blexSlider_update()
{
  this.redraw();

  if (this.onupdate)
    this.onupdate();

}

blexSlider.prototype.redraw = function blexSlider_redraw()
{
  var si = this;

  var barlength = (this.ishorizontal?this.slider.clientWidth:this.slider.clientHeight);

  // (als document.readyState 'loaded' is maar nog niet 'complete'
  if (barlength == 0)
  {
    /* Browsers based on the following engines
       won't be able to give back dimensions & positions
       before having reached 'complete' state on the document.

       - Webkit (Safari and Google Chrome)
       - Trident (IE)
       - maybe KHTML? (Konquerer) */
    if (document.readyState != 'complete')
    {
      var _this = this;
          this.redrawretry = setTimeout( function() { _this.redraw(); }, 250);
    }
    return;
  }
  else if (this.redrawretry)
  {
    clearTimeout(this.redrawretry);
    delete this.redrawretry;
  }

//  console.log('Redrawing for '+si.curVal+' of '+si.maxVal);

  if (this.isrange && this.sizecompensation == null)
  {
    if (this.ishorizontal)
      this.sizecompensation = this.knob.offsetWidth-this.knob.clientWidth;
    else
      this.sizecompensation = this.knob.offsetHeight-this.knob.clientHeight;
  }

/*
  if (si.isrange)
    console.log('minVal: %i\ncurVal: %i\nmaxVal: %i', si.minVal, si.curVal, si.maxVal);
*/

  var tempmax = si.maxVal-si.rangeSize;

  if (si.isrange)
    si.curVal = Math.max(si.minVal, Math.min(si.curVal, si.maxVal-si.rangeSize));
  else
    si.curVal = Math.max(si.minVal, Math.min(si.curVal, si.maxVal));

  if (si.maxVal == si.minVal)
  {
    var knoboffset = 0;
  }
  else
  {
    if (this.isrange)
    {
      var availspace = barlength - this.sizecompensation;
    }
    else
    {
      if (this.ishorizontal)
        var knobsize = this.knob.clientWidth;
      else
        var knobsize = this.knob.clientHeight;

      var availspace = barlength - knobsize;
    }

    var knoboffset = availspace * (si.curVal-si.minVal) / (si.maxVal-si.minVal);

    if (!this.isrange)
      knoboffset += knobsize/2;
  }

  knoboffset = Math.round(knoboffset);

  if (this.ishorizontal)
    this.knob.style.cssText = 'left: '+knoboffset + 'px';
  else
    this.knob.style.cssText = 'top: '+knoboffset + 'px';

  // update size of range
  if (this.isrange)
  {
    var handlesize = (si.rangeSize / (si.maxVal-si.minVal) * barlength) - this.sizecompensation;

    // keep size visible and big enough to easily drag
    if (handlesize < 1)
      handlesize = 1;

    //console.log('min: '+si.minVal+' / max: '+si.maxVal+' / range: '+si.rangeSize);

    // FIXME: check if calculation checks out
    // this fixes IE making the bar longer than the available barlength
    if (si.isrange && si.maxVal <= si.minVal+si.rangeSize)
    {
      handlesize = availspace - this.sizecompensation;;
    }

    handlesize = Math.round(handlesize);

    if (this.ishorizontal)
      this.knob.style.width = handlesize+'px';
    else
      this.knob.style.height = handlesize+'px';
  }
}

blexSlider.prototype.hideCuepoints = function blexSlider_hideCuepoints()
{
  this.cuepointsvisible = false;
  this.elemcuepoints.style.display = 'none';
}

blexSlider.prototype.showCuepoints = function blexSlider_showCuepoints()
{
  this.cuepointsvisible = true;
  this.elemcuepoints.style.display = 'block';
}

blexSlider.prototype.drawCuepoints = function blexSlider_drawCuepoints()
{
  // create cuepoints container, for quick showing/hiding/replacing all cuepoints
  var elemcuepoints = document.createElement('div');
  elemcuepoints.className = 'cuepoints';
  elemcuepoints.style.display = this.cuepointsvisible?'block':'none';

  var cuepoints = this.cuepoints;
  var qpcount = cuepoints.length;

  while(qpcount--)
  {
    var cpelem = document.createElement('div');
    cpelem.className = 'cuepoint';

    // set position in percentage so when resizing the slider,
    // all cuepoints are repositioned by the browser
    var perc = 100 * cuepoints[qpcount].starttime / this.maxVal;

    if (this.ishorizontal)
      // part.style.left = perc+'%'; <-- IE (at least 6) doesn't allow this when it's not in the DOM yet
      cpelem.style.cssText = 'left: '+perc+'%';
    else
      cpelem.style.cssText = 'top: '+perc+'%';

    var title = cuepoints[qpcount].data.title;

    if (title != '')
      cpelem.alt = title;
      //cpelem.setAttribute('alt', title);

    elemcuepoints.appendChild(cpelem);
  }

  if (this.elemcuepoints == undefined)
    this.slider.appendChild(elemcuepoints);
  else
  {
    // cuepoints where allready drawn, so replace them
    this.slider.replaceChild(elemcuepoints, this.elemcuepoints);
  }

  this.elemcuepoints = elemcuepoints;

  return qpcount;
}

blexSlider.prototype.dragStart = function blexSlider_dragStart(e)
{
  if (!e)
    var e = window.event;

  var si = this.instance;

  blexSlider._instance = this.instance;            // global variable

  si.slider.className = "toddSliderBar active"+(si.cssclass ? ' '+si.cssclass:'');;
  si.knob.className = si.handle_class+" active";
  si.isdragging = true;

  toddAddEvent(document, 'mousemove',  si.drag);
  toddAddEvent(document, 'mouseup',    si.dragEnd);
  toddAddEvent(window,   'onmouseout', si.ffchromecheck); // FF2 can't register mouseup outside webpage/window

  si.drag(e);             // a single click should work too

  if (!toddUserAgentIsIE)
    /* prevent Firefox from displaying a context menu
       after holding down the left mousebutton for 1 sec */
    e.preventDefault();
  else
    window.event.cancelBubble = true; // TODO: werkt nog niet goed in IE!!!
}

blexSlider.prototype.ffchromecheck = function blexSlider_ffchromecheck(evt)
{
  // no need to check in IE
  if (!evt)
    return;

  if (!evt.relatedTarget)
    blexSlider._instance.dragEnd(e);
}

blexSlider.prototype.dragEnd = function blexSlider_dragEnd(e)
{
  var si = blexSlider._instance;

  toddRemoveEvent(document, 'mousemove',  si.drag);
  toddRemoveEvent(document, 'mouseup',    si.dragEnd);
  toddRemoveEvent(window,   'onmouseout', si.ffchromecheck);

  si.slider.className = "toddSliderBar"+(si.cssclass ? ' '+si.cssclass:'');
  si.knob.className = si.handle_class;
  si.isdragging = false;

  blexSlider._instance = null;                     // global variable

  if (si.ondragend) si.ondragend();
}

blexSlider.prototype.drag = function blexSlider_drag(e)
{
  if (!e) var e = window.event;

  si = blexSlider._instance;
  if (si.disabled) return;

  // get mouseposition relative to the slider
  var pos = GetViewportPositionOfElement(si.slider);

//  console.log(getPointerX(e)+'-'+pos.x+' x '+getPointerY(e)+"-"+pos.y);

  var barlength = (si.ishorizontal?si.slider.clientWidth:si.slider.clientHeight);

  if (si.isrange)
  {
    var availspace = barlength - si.sizecompensation;
  }
  else
  {
    if (si.ishorizontal)
      var knobsize = si.knob.clientWidth;
    else
      var knobsize = si.knob.clientHeight;

    var availspace = barlength - knobsize;
  }

  if (si.ishorizontal)
  {
    var spos = getPointerX(e) - pos.x;
//  var availspace = si.slider.clientWidth - si.knob.clientWidth;
  }
  else
  {
    var spos = getPointerY(e) - pos.y;
//  var availspace = si.slider.clientHeight - si.knob.clientHeight;
  }

  if (!si.isrange)
    spos -= knobsize/2;

//    console.log('Pointerpos '+getPointerY(e)+' - elementposinviewport '+pos.y+' = '+spos);

  spos = spos < 0          ? 0          : spos;
  spos = spos > availspace ? availspace : spos;

  var newVal = Math.round((spos / availspace * (si.maxVal-si.minVal)) + si.minVal);

//    console.log('PointerX: %i\nScrollbarX: %i\nnewVal: %i', getPointerX(e), pos.x, newVal);

  /* check for changes. Opera 9 will go crazy reload-/draw-/flowing stuff,
     even though values stay the same */
  if (newVal != si.curVal) {
    si.curVal = newVal;

    si.redraw();
    if (si.ondrag)
      si.ondrag();
  }

  if (!toddUserAgentIsIE)
    e.stopPropagation();
}

if (typeof toddCurrentLoads != 'undefined') toddCurrentLoads["blexmediaplayer/slider.js"]=true;