/*

Sliding Selector
   $CVSHeader: blexplayground/slidingselector/js/sliding_selector.js,v 1.7 2009/06/15 09:20:26 mark Exp $


   public api:

   slidingselector(settings)
     settings:
       thumbcontainer:     reference to element in which the thumbnails must be placed
       orientation:        'horizontal' or 'vertical'
       onselect(itemdata)  function to run when a thumbnail is selected
       wrap:               true/false
       defaultthumbwidth:
       defaultthumbheight:
       thumbs_autoresize:  (not recommended) thumbs will be resized, keeping their correct aspect ratio
                           only nessecary if given thumb images aren't resized serverside
                           and if the browser can't correctly resize keeping aspect ratio

       scrollduration:     total duration of scroll (from the last click)
       animationsteps:     in how many steps to animate a scroll action
       normalthumbclass:   'thumbcont'
       selectedthumbclass: 'thumbcont_selected'

   setImageList(imagelist)

   previtem()

   nextitem()

   selectitem()

   destroy()




CSS classes:
 .horizontalthumbselection
 .verticalthumbselection
a.thumbcont
a.thumbcont_selected

*/

function slidingselector(options)
{
  var config = { thumbcontainer:     null
               , orientation:        'vertical'
               , scrollduration:     800
               , animationsteps:     20
               , onselect:           null
               , wrap:               false
               , normalthumbclass:   'thumbcont'
               , selectedthumbclass: 'thumbcont_selected'

               , generatethumb:      null
               , thumbs_autoresize:  false
               , defaultthumbwidth:  null
               , defaultthumbheight: null

               // FIXME: default false atm until sure when we want to react to/take over arrow keys
               , allowkeyboardnav:   false
               };

  for(prop in config)
  {
//    if(typeof(options[prop])!='undefined')
//      config[prop] = options[prop];
    if(typeof(options[prop])=='undefined')
      this[prop] = config[prop];
    else
      this[prop] = options[prop];
  }

  // smooth scroll duration in milliseconds
  this.stepinterval = this.scrollduration / this.animationsteps;

  this.isvertical = this.orientation == 'vertical';

  var classname = this.isvertical?'verticalthumbselection':'horizontalthumbselection';
  addClass(this.thumbcontainer, classname);

  this.currentitem = null;

  // extra element added to a selected thumbnail to provide additional styling options
  // (like an overlay or inner border)
  this.selectionelement = document.createElement('div');
  this.selectionelement.className = 'thumbselection ';

  var self = this;
  if (this.allowkeyboardnav)
    addEvent(document, 'keydown', function(event) { self.handleKeyPress(event); });

  if (options.items)
    this.setImageList(options.items);
}

slidingselector.prototype.handleKeyPress = function(event)
{
  if (!event)
    event = window.event;

  // IE uses srcElement instead
  var targ = event.target ? event.target : event.srcElement;

  var ntagname = (targ.tagName ? targ.tagName.toLowerCase() : '');

  // don't mess with keypresses in normal input's
  if (ntagname == 'input' || ntagname == 'textarea')
          return;

  // only react to keypresses when no item has focus
  //if (document.activeElement != undefined &&
  //      document.activeElement != document.getElementsByTagName('body')[0])
  //  return;

  switch (event.keyCode)
  {
    case 37:
      this.previtem();
      break;

    case 39:
      this.nextitem();
      break;
  }

  // TODO: if key was handled preventDefault?
};

slidingselector.prototype.setImageList = function slidingselector_setImageList(imagelist)
{
  this.items = imagelist;
  this.createItemsDOM(this.items);

  this.currentitem = null;

  if (this.items.length > 0)
    this.selectitem(0);
}

slidingselector.prototype.createItemsDOM = function slidingselector_createlist()
{
  var self = this;

  if (!this.thumbcontainer)
    return;

  var newthumbscontainer = this.thumbcontainer.cloneNode(false);
  //var newthumbscontainer = document.createElement('div');
  //newthumbscontainer.className = 'thumblistcontainer';


  // generate the DOM for each item
  for(var itemid=0; itemid < this.items.length; itemid++)
  {
    var item = this.items[itemid];
	var thumbcontainer;

	if (this.generatethumb) // is there an override for creating thumbnails?
	{
		thumbcontainer = this.generatethumb(item);
		item.node = thumbcontainer;
	}
	else
	{
	    // encase each item in a <a> so we can use :hover in IE6
	    thumbcontainer = document.createElement('a');
	    thumbcontainer.className = this.normalthumbclass;
	
	    item.node = thumbcontainer; // keep reference for determining y position of an item
	
	    var thumbimg = document.createElement('img');
	
	    if (this.thumbs_autoresize)
	    {
	      thumbimg.setAttribute('itemid', itemid);
	      thumbimg.onload = function() { self.resizethumb(this); };
	    }
	    else
	    {
	      if (item.thumb.width)
	        thumbimg.style.width = item.thumb.width+'px'; // use width given in imagelist
	      else if (this.defaultthumbwidth)
	        thumbimg.width = this.defaultthumbwidth;
	
	      if (item.thumb.height)
	        thumbimg.style.height = item.thumb.height+'px';
	      else if (this.defaultthumbheight)
	        thumbimg.width = this.defaultthumbheight;
	    }

      thumbimg.src   = item.thumb.src
      thumbimg.title = item.title; // mouseover tooltip
      thumbimg.alt   = item.title; // text in case image can't be displayed

      thumbcontainer.appendChild(thumbimg);
      newthumbscontainer.appendChild(thumbcontainer);
	}

    thumbcontainer.thumbid = itemid;
    thumbcontainer.onclick = function() { self.selectitem(this.thumbid); };
	newthumbscontainer.appendChild(thumbcontainer);
  }

  this.thumbcontainer.parentNode.replaceChild(newthumbscontainer, this.thumbcontainer);
  this.thumbcontainer = newthumbscontainer;
}

slidingselector.prototype.resizethumb = function(image)
{
  var itemid = image.getAttribute('itemid');
  var item = this.items[itemid];

  if (!item.thumb.width && !item.thumb.height)
    return;

  // determine max zoom before hitting max width/height
  if (item.thumb.width && item.thumb.height)
  {
    var zoomw = item.thumb.width / this.width;
    var zoomh = item.thumb.height / this.height;
    var zoom = zoomw < zoomh ? zoomw : zoomh;
  }
  else if (item.thumb.width)
  {
    var zoomw = item.thumb.width / image.width;
    var zoom = zoomw;
  }
  else
  {
    var zoomh = item.thumb.height / image.height;
    var zoom = zoomh;
  }

  var imgwidth = image.width * zoom;
  var imgheight = image.height * zoom;
  //console.log('width: '+imgwidth+'px; height: '+imgheight+'px');
  image.style.cssText = 'width: '+imgwidth+'px; height: '+imgheight+'px';
}

slidingselector.prototype.previtem = function slidingselector_previtem()
{
  var prev = this.currentitem - 1;

  if (prev < 0)
    if (this.wrap)
      prev = this.items.length-1;
    else
      return;

  this.selectitem(prev);
}

slidingselector.prototype.nextitem = function slidingselector_nextitem()
{
  var next = this.currentitem + 1;

  if (next > this.items.length-1)
    if (this.wrap)
      next = 0;
    else
      return;

  this.selectitem(next);
}

slidingselector.prototype.selectitem = function slidingselector_selectitem(itemid)
{
  // find vertical position of the item within the list
  // (don't assume all images share the same height or use a fixed height)
  var item = this.items[itemid];

  if (this.isvertical)
  {
    var itemy = item.node.offsetTop;
    var itemmiddley = itemy + item.node.clientHeight/2;
    var viewheight = this.thumbcontainer.clientHeight;
    var listheight = this.thumbcontainer.scrollHeight;

    // center list of thumbnails around the middle of the selected item
    var scrolly = itemmiddley - viewheight/2;

    if (scrolly + viewheight > listheight)
      scrolly = listheight - viewheight;

    if (scrolly < 0)
      scrolly = 0;

    this.SmoothScrollTo(null, scrolly);
  }
  else
  {
    var itemx = item.node.offsetLeft;
    var itemmiddlex = itemx + item.node.clientWidth/2;
    var viewwidth = this.thumbcontainer.clientWidth;
    var listwidth = this.thumbcontainer.scrollWidth;

    // center list of thumbnails around the middle of the selected item
    var scrollx = itemmiddlex - viewwidth/2;

    if (scrollx + viewwidth > listwidth)
      scrollx = listwidth - viewwidth;

    if (scrollx < 0)
      scrollx = 0;

    this.SmoothScrollTo(scrollx, null);
  }

  // remove selection state from previous selection
  if (this.currentitem != null)
  {
    var itemcontainer = this.items[this.currentitem].node;
    itemcontainer.className = this.normalthumbclass;
    itemcontainer.removeChild(this.selectionelement);
  }

  this.currentitem = itemid;

  item.node.className = this.normalthumbclass+' '+this.selectedthumbclass;

  item.node.appendChild(this.selectionelement);

  if (this.onselect)
    this.onselect(item, { itemid:    itemid+1
                        , itemcount: this.items.length
                        });
}

slidingselector.prototype.getinfo = function slidingselector_getinfo()
{
  return { currentitem: this.currentitem+1
         , itemcount:   this.items.length
         };
}

slidingselector.prototype.SmoothScrollTo = function slidingselector_SmoothScrollTo(destinationx, destinationy)
{
  var currentscrollx = this.thumbcontainer.scrollLeft;
  var currentscrolly = this.thumbcontainer.scrollTop;

  // already at the destination?
  if (this.isvertical && destinationy == currentscrolly)
    return;
  if (!this.isvertical && destinationx == currentscrollx)
    return;

  var self = this;

  // set info for animation
  // (if still scrolling, then just start a new animation from the current position)
  this.startx = currentscrollx;
  this.starty = currentscrolly;

  this.endx   = (destinationx==null?currentscrollx:destinationx);
  this.endy   = (destinationy==null?currentscrolly:destinationy);
  this.anim   = 0;

  if (!this.scrolltimer)
    this.scrolltimer = setTimeout( function() { self.DoScroll() } , this.stepinterval );
}

slidingselector.prototype.DoScroll = function slidingselector_DoScroll()
{
  this.anim++;

  if (this.anim == this.animationsteps)
  {
    this.thumbcontainer.scrollTop = this.endy;
    this.thumbcontainer.scrollLeft = this.endx;
    this.scrolltimer = null; // scroll animation has finished
    return;
  }

  var self = this;

  var runtime = this.stepinterval * this.anim;
  var effectstrength = Math.sin(Math.PI / 2 * runtime / this.scrollduration);

  var newvalue = this.startx + (this.endx - this.startx) * effectstrength;
  this.thumbcontainer.scrollLeft = newvalue;

  var newvalue = this.starty + (this.endy - this.starty) * effectstrength;
  this.thumbcontainer.scrollTop = newvalue;

  this.scrolltimer = setTimeout( function() { self.DoScroll() } , this.stepinterval );
}

slidingselector.prototype.destroy = function slidingselector_Destroy()
{
  if (this.scrolltimer)
    clearTimeout(this.scrolltimer);

  this.thumbcontainer = null;

  // destroy references between DOM elements
  for(var tel=0; tel<this.items.length; tel++)
  {
    this.items[tel].node.onclick = null; // remove reference back to our slidingselector instance
    this.items[tel].node = null;         // remove reference to DOM element
  }
}
