/*

  Carousel widget
  Horizontal rotation of a set of elements

*/


var Carousel = function(element, params) {this.init(element, params);}
Carousel.prototype = {
  init: function(element, params) {
    this.fps = 100;
    this.frame = 0;
    this.mouse = {x:0, y:0};
    this.idle_width = 50;
    this.velocity_factor = 0;
    this.velocity_max = 10; //px/second
    this.velocity_actual = 0;
    this.velocity = 0;
    this.interval_id = 0;

    // wait until page has loaded before initializing
    var onload = function(e) {
      if (typeof(element) == 'string')
        this.element = document.getElementById(element);
      else this.element = element;
      this.config_dom();
      this.shift();

      // INTERNET EXPLORER SUCKS, BTW
      var netscape = false;
      if (document.layers) {
        // nestscape
        netscape = true;
        document.captureEvents(Event.MOUSEMOVE);
      } else if (document.all) {
        // internet explorer
        document.attachEvent('onmousemove', function(e) {
          this.mouse.x = window.event.x +
            (document.body.scrollLeft || document.documentElement.scrollLeft);
          this.mouse.y = window.event.y +
            (document.body.scrollTop || document.documentElement.scrollTop);
          this.onmousemove(e);
        }.bind(this));
      } else if (document.getElementById)
        // netscape 6
        netscape = true;
      if (netscape) {
        document.addEventListener('mousemove', function(e) {
          this.mouse.x = e.pageX; this.mouse.y = e.pageY;
          this.onmousemove(e);
        }.bind(this), false);
      }
      
      // start velocity stepper
      this.interval_id = setInterval(this.do_scroll.bind(this), 1000/this.fps);
    };

    if ('attachEvent' in window)
      window.attachEvent('onload', onload.bind(this));
    else
      window.addEventListener('load', onload.bind(this), false);
  },

  config_dom: function() {
    // collect items specified by css class
    // and separate them to prevent float wrap
    var o = 0; var h = 0; var items = [];
    var e = document.createElement('div');
    for (var x=0; x<this.element.childNodes.length; x++) {
      var child = this.element.childNodes[x];
      if (child.nodeName != '#text')
        if (child.className)
          if (child.className.match(/item/g))
            items.push(child);
    }
    for (var x=0; x<items.length; x++) {
      var child = items[x];
      o += child.clientWidth;
      if (child.clientHeight > h) h = child.clientHeight;
      e.appendChild(child);
      with (child.style) {
        //visibility = 'hidden';
        margin = '0';
        marginLeft = '0px';
        height = this.element.clientHeight+'px';
      }
    }      
    e.style.width = o+'px';
    this.element.items = e;
    if (this.element.clientHeight < h)
      this.element.style.height = h+'px';
    this.element.appendChild(e);
    if (this.element.items.childNodes.length)
      this.element.style.visibility = 'visible';
    else this.element.style.display = 'none';
  },

  shift: function() {
    // shift elements
    if (this.element.items.childNodes.length
        && this.element.items.clientWidth > this.element.clientWidth) {
      var margin = parseInt(this.element.items.firstChild.style.marginLeft);
      var width = parseInt(this.element.items.firstChild.clientWidth);
      if (margin < 0) {
        if (Math.abs(margin) >= width) {
          var offset = Math.abs(margin)-width;
          this.element.items.firstChild.nextSibling.style.marginLeft = '-'+offset+'px';
          this.element.items.appendChild(this.element.items.firstChild);
          this.element.items.lastChild.style.marginLeft = '0';
        }
      } else {
        var first = this.element.items.firstChild;
        var last = this.element.items.lastChild;
        this.element.items.firstChild.style.marginLeft = '0';
        this.element.items.insertBefore(last, first);
        this.element.items.firstChild.style.marginLeft = -this.element.items.firstChild.clientWidth-margin+'px';
      }
      // set visibility
      for (var x=0; x<this.element.items.childNodes.length; x++) {
        this.toggle_visibility(this.element.items.childNodes[x]);
      };
    }
  },

  toggle_visibility: function(child) {
    var o = this.get_position(this.element);
    var offset = this.get_position(child)[0] - o[0];
    var right_side = offset+child.clientWidth;
    var rot_width = this.element.clientWidth;
    if (right_side >= 0 && offset <= rot_width) {
      child.style.visibility = 'visible';
    } else {
      child.style.visibility = 'hidden';
    }
  },

  test_scroll: function() {
    setInterval(function() {
      var m = parseInt(this.element.items.childNodes[0].style.marginLeft);
      this.element.items.firstChild.style.marginLeft = m + 1 +'px';
      this.shift();
    }.bind(this), 10);
  },

  scroll_contents: function(offset) {
    var m = parseInt(this.element.items.childNodes[0].style.marginLeft);
    this.element.items.firstChild.style.marginLeft = m + offset + 'px';
    this.shift();
  },

  onmousemove: function(e) {
    if (this.element.items.clientWidth > this.element.clientWidth) {
      var p = this.get_position(this.element);
      var x = this.mouse.x; var y = this.mouse.y;
      var top = p[1]; var left = p[0];
      var right = this.element.clientWidth+p[0];
      var bottom = this.element.clientHeight+p[1];
      if (x >= left && x <= right && y >= top && y <= bottom)
        this.set_velocity(this.get_velocity_factor());
      else this.set_velocity(0);
    }
  },

  get_velocity_factor: function() {
    var x =  (this.mouse.x - this.get_position(this.element)[0]) - this.element.clientWidth / 2;
    var y = (this.element.clientWidth-this.idle_width) / 2;
    if (x < 0) {
      x += this.idle_width/2;
      if (x > 0) x = 0;
    } else {
      x -= this.idle_width/2;
      if (x < 0) x = 0;
    }
    return x/y;
  },

  set_velocity: function(value) {
    this.velocity_factor = value;
    this.velocity = parseInt(value*this.velocity_max);
  },

  adjust_velocity: function() {
    if (this.velocity_actual != this.velocity) {
      if (this.velocity_actual < this.velocity)
        this.velocity_actual += 1;
      else this.velocity_actual -= 1;
    }
  },

  do_scroll: function() {
    if (this.frame % 2 == 0) this.adjust_velocity();
    this.frame++; if (this.frame == this.fps) this.frame = 0;
    if (this.velocity_actual != 0)
      this.scroll_contents(-this.velocity_actual);
  },

  get_position: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
    } while (element);
    return [valueL, valueT];
  }
};


// Prototype.js extensions

Array.from = function(iterable) {
  if (!iterable) return [];
  if (iterable.toArray) {
    return iterable.toArray();
  } else {
    var results = [];
    for (var i=0, length = iterable.length; i<length; i++)
      results.push(iterable[i]);
    return results;
  }
};

Function.prototype.bind = function() {
  var __method = this;
  var args = Array.from(arguments);
  var object = args.shift();
  return function() {
    return __method.apply(
      object, args.concat(Array.from(arguments)));
  }
};
