function getElementsByTagNames(list,obj) {
  if (!obj) obj = document;
  var tagNames = list.split(',');
  var resultArray = new Array();
  for (var i=0;i<tagNames.length;i++) {
    var tags = obj.getElementsByTagName(tagNames[i]);
    for (var j=0;j<tags.length;j++) {
      resultArray.push(tags[j]);
    }
  }
  return resultArray;
}

// Create scrolling variable if it doesn't exist
if (!Scrolling) var Scrolling = {};

//Scrollbar constructor
Scrolling.Scrollbar = function (o, s, t) {
  //private variables
  var self = this;
  var _components = {};
  var _dimensions = {};
  var _temporary  = {};
  var _hasTween   = t ? true : false;
  var _timer, _ratio;

  //public variables
  this.onMouseDown   = function (){};
  this.onMouseUp     = function (){};
  this.onScroll      = function (){};
  this.scrollAmount  = 5;
  this.scrollSpeed   = 30;
  this.disabled      = false;

  //private functions
  function initialize () {
    var c = _components;
    var d = _dimensions;
    var g = s.getDimensions();

    s.scrollBar = self;
    c.up     = findComponent("Scrollbar-Up", o);
    c.down   = findComponent("Scrollbar-Down", o);
    c.track  = findComponent("Scrollbar-Track", o);
    c.handle = findComponent("Scrollbar-Handle", c.track);
    c.spinner = findComponent("Scrollbar-Preloader", o) || false;

    d.trackTop     = findOffsetTop(c.track);
    d.trackHeight  = c.track.offsetHeight;
    d.handleHeight = c.handle.offsetHeight;
    d.x = 0;
    d.y = 0;

    if (_hasTween) t.apply(self);

    addEvent(s.getContent(), "mousewheel", scrollbarWheel);
    addEvent(o, "mousedown", scrollbarClickPrimer);

    self.reset();
  };

  function findOffsetTop (o) {
    var t = 0;
    if (o.offsetParent) {
      while (o.offsetParent) {
        t += o.offsetTop;
        o  = o.offsetParent;
      }
    }
    return t;
  };

  function addEvent (o, t, f) {
    if (o.attachEvent) o.attachEvent('on'+ t, f);
    else o.addEventListener(t, f, false);
  };

  function removeEvent (o, t, f) {
    if (o.detachEvent) o.detachEvent('on'+ t, f);
    else o.removeEventListener(t, f, false);
  };

  function findComponent (c, o) {
    var kids = o.childNodes;
    for (var i = 0; i < kids.length; i++) {
      if (kids[i].className && kids[i].className.indexOf(c) >= 0) {
        return kids[i];
      }
    }
  };

  function scroll (y) {
    if (y < 0) y = 0;
    if (y > _dimensions.trackHeight - _dimensions.handleHeight)
      y = _dimensions.trackHeight - _dimensions.handleHeight;

    _components.handle.style.top = y +"px";
    _dimensions.y = y;

    s.scrollTo(0, Math.round(_dimensions.y * _ratio));
    self.onScroll();
  };

  function scrollbarClickPrimer (e) {
    if (self.disabled) return false;

    e = e?e:event;
    if (!e.target) e.target = e.srcElement;

    scrollbarClick(e.target.className, e);
  };

  function scrollbarClick (c, e) {
    var d  = _dimensions;
    var t  = _temporary;
    var cy = e.clientY + document.body.scrollTop;

    if (c.indexOf("Scrollbar-Up") >= 0)
      startScroll(-self.scrollAmount);

    if (c.indexOf("Scrollbar-Down") >= 0)
      startScroll(self.scrollAmount);

    if (c.indexOf("Scrollbar-Track") >= 0)
      if (_hasTween) self.tweenTo((cy - d.trackTop - d.handleHeight / 2) * _ratio);
      else scroll(cy - d.trackTop - d.handleHeight / 2);

    if (c.indexOf("Scrollbar-Handle") >= 0) {
      t.grabPoint = cy - findOffsetTop(_components.handle);
      addEvent(document, "mousemove", scrollbarDrag, false);
    }

    t.target = e.target;
    t.select = document.onselectstart;

    document.onselectstart = function (){ return false; };
    self.onMouseDown(e.target, c, e);

    addEvent(document, "mouseup", stopScroll);
  };

  function scrollbarDrag (e) {
    e = e?e:event;
    var d = _dimensions;
    var t = parseInt(_components.handle.style.top);
    var v = e.clientY + document.body.scrollTop - d.trackTop;

    if (v >= d.trackHeight - d.handleHeight + _temporary.grabPoint)
      v = d.trackHeight - d.handleHeight;

    else if (v <= _temporary.grabPoint)
      v = 0;

    else v = v - _temporary.grabPoint;

    scroll(v);
  };

  function scrollbarWheel (e) {
    if (self.disabled) return false;

    e = e ? e : event;
    var dir = 0;
    if (e.wheelDelta >= 120) dir = -1;
    if (e.wheelDelta <= -120) dir = 1;

    self.scrollBy(dir * 20);
    e.returnValue = false;
  };

  function startScroll (y) {
    _temporary.disty = y;
    _timer = window.setInterval(function () {
      self.scrollBy(_temporary.disty);
    }, self.scrollSpeed);
  };

  function stopScroll (e) {
    e = e?e:event;

    removeEvent(document, "mousemove", scrollbarDrag);
    removeEvent(document, "mouseup", stopScroll);

    document.onselectstart = _temporary.select;
    if (_timer) window.clearInterval(_timer);

    self.onMouseUp(_temporary.target, _temporary.target.className, e);
  };

  //public functions

  this.reset = function () {
    var d = _dimensions;
    var c = _components;
    var g = s.getDimensions();

    _ratio = (g.theight - g.vheight) / (d.trackHeight - d.handleHeight);
    var pos = Math.round(g.y/_ratio);
    c.handle.style.top   = pos + "px";

    c.handle.ondragstart = function (){ return false; };
    c.handle.onmousedown = function (){ return false; };

//    d.y = 0;
    s.reset();
//    scroll(0);

    if (g.theight < g.vheight) {
      this.disabled = true;
      o.className  += " Scrollbar-Disabled";
    }
  };

  this.scrollTo = function (y) {
    scroll(y / _ratio);
  };

  this.scrollBy = function (y) {
    scroll((s.getDimensions().y + y) / _ratio);
  };

  this.swapContent = function (n, w, h) {
    o = n;
    s.swapContent(o, w, h);
    initialize();
  };

  this.disable = function () {
    this.disabled = true;
    o.className  += "Scrollbar-Disabled";
  };

  this.enable = function () {
    this.disabled = false;
    o.className = o.className.replace(/Scrollbar\-Disabled/, "");
  };

  this.getContent = function () {
    return s.getContent();
  };

  this.getComponents = function () {
    return _components;
  };

  this.getDimensions = function () {
    var d = s.getDimensions();
    d.trackHeight  = _dimensions.trackHeight;
    d.handleHeight = _dimensions.handleHeight;

    return d;
  };

  this.showSpinner = function(flag) {
    if(_components.spinner) {
      if(flag) {
	_components.spinner.style.display = 'block';
      } else {
	_components.spinner.style.display = 'none';
      }
    }
  };

  this.resetBar = function(){
    this.reset();
  };

  initialize();
};


// Create scrolling variable if it doesn't exist
if (!Scrolling) var Scrolling = {};

//Scroller constructor
Scrolling.Scroller = function (o, w, h, t) {
  //get the container
  var list = getElementsByTagNames("div,ul",o);
  for (var i = 0; i < list.length; i++) {
    if (list[i].className.indexOf("Scroller-Container") > -1) {
      o = list[i];
    }
  }

  //private variables
  var self  = this;
  var _vwidth   = w;
  var _vheight  = h;
  var _o        = o;
  var _twidth   = o.offsetWidth;
  var _theight  = o.offsetHeight;
  var _hasTween = t ? true : false;
  var _requesting = false;
  var _timer, _x, _y;

  //public variables
  this.onScrollStart = function (){};
  this.onScrollStop  = function (){};
  this.onScroll      = function (){};
  this.scrollBar     = false;
  this.scrollSpeed   = 30;
  this.preloadDistance   = 100;
  this.fetchUrl      = false;
  this.preloadElements = 10;
  this.totalElements = 0;
  this.actualElements = getNumOfItems();
  this.resetScrollBar = false;

  //private functions
  function setPosition (x, y) {
    if (x < _vwidth - _twidth)
      x = _vwidth - _twidth;
    if (x > 0) x = 0;
    if (y < _vheight - _theight)
      y = _vheight - _theight;
    if (y > 0) y = 0;

    if(!_requesting && self.fetchUrl && self.actualElements < self.totalElements && (_theight - _vheight + y) < self.preloadDistance) {
      _requesting = true;
      self.scrollBar.showSpinner(true);
      _fetchItemsRequest();
    }

    _x = x;
    _y = y;
    o.style.left = _x +"px";
    o.style.top  = _y +"px";
  };

  function _fetchItemsRequest() {
    new Ajax.Request(self.fetchUrl, {
		       method: 'get',
		       parameters: 'offset='+self.actualElements,
		       requestHeaders:{Accept:'text/html'},
		       onComplete: function(response){_fetchNewItemsResponse(response);}
	});
  }

  function _fetchNewItemsResponse(response) {
    o.innerHTML += response.responseText;
    self.actualElements = getNumOfItems();
    _twidth = o.offsetWidth;
    _theight = o.offsetHeight;
    self.scrollBar.resetBar();
    _requesting = false;
    self.scrollBar.showSpinner(false);
  }

  function getNumOfItems () {
    var num = 0;
    for(var i=0; i<o.childNodes.length; i++) {
      if(o.childNodes[i].nodeName.search(/.*(div|li).*/i) != -1) num++;
    }
    return num;
  }

  //public functions
  this.setAjaxPreloader = function (url, preloadNum, totalNum, preloadDistance) {
    this.preloadDistance   = preloadDistance;
    this.fetchUrl      = url;
    this.preloadElements = preloadNum;
    this.totalElements = totalNum;
  };

  this.scrollBy = function (x, y) {
    setPosition(_x - x, _y - y);
    this.onScroll();
  };

  this.scrollTo = function (x, y) {
    setPosition(-x, -y);
    this.onScroll();
  };

  this.startScroll = function (x, y) {
    this.stopScroll();
    this.onScrollStart();
    _timer = window.setInterval(
      function () { self.scrollBy(x, y); }, this.scrollSpeed
    );
  };

  this.stopScroll  = function () {
    if (_timer) window.clearInterval(_timer);
    this.onScrollStop();
  };

  this.reset = function () {
    _twidth  = o.offsetWidth;
    _theight = o.offsetHeight;
    _x = 0;
    _y = 0;

//    o.style.left = "0px";
//    o.style.top  = "0px";

    if (_hasTween) t.apply(this);
  };

  this.swapContent = function (c, w, h) {
    o = c;
    var list = getElementsByTagNames("div,ul",o);
    for (var i = 0; i < list.length; i++) {
      if (list[i].className.indexOf("Scroller-Container") > -1) {
        o = list[i];
      }
    }

    if (w) _vwidth  = w;
    if (h) _vheight = h;
    reset();
  };

  this.getDimensions = function () {
    return {
      vwidth  : _vwidth,
      vheight : _vheight,
      twidth  : _twidth,
      theight : _theight,
      x : -_x, y : -_y
    };
  };

  this.getContent = function () {
    return o;
  };

  this.reset();
};


// Create scrolling variable if it doesn't exist
if (!Scrolling) var Scrolling = {};

//ScrollTween constructor
Scrolling.ScrollTween = function () {
  //private variables
  var self    = this;
  var _steps  = [0,25,50,70,85,95,97,99,100];
  var _values = [];
  var _idle   = true;
  var o, _inc, _timer;

  //private functions
  function tweenTo (y) {
    if (!_idle) return false;

    var d = o.getDimensions();
    if (y < 0) y = 0;
    if (y > d.theight - d.vheight)
      y = d.theight - d.vheight;

    var dist = y - d.y;
    _inc     = 0;
    _timer   = null;
    _values  = [];
    _idle    = false;

    for (var i = 0; i < _steps.length; i++) {
      _values[i] = Math.round(d.y + dist * (_steps[i] / 100));
    }

    _timer = window.setInterval(function () {
      o.scrollTo(_values[_inc]);
      if (_inc == _steps.length - 1) {
        window.clearInterval(_timer);
        _idle = true;
      } else _inc++;
    }, o.stepSpeed);
  };

  function tweenBy (y) {
    o.tweenTo(o.getDimensions().y + y);
  };

  function setSteps (s) {
    _steps = s;
  };

  //public functions
  this.apply = function (p) {
    o = p;
    o.tweenTo   = tweenTo;
    o.tweenBy   = tweenBy;
    o.setSteps  = setSteps;
    o.stepSpeed = 30;
  };
};
