function CategorySlider(){
	var categoryPositionsChangedToLeft = false;
	var categoryPositionsChangedToRight = false;

	this.getCurrentCategory = function(){
		obj = categoryContainer.down('.currentCategory');
		// IE HACK:
		if(! obj.isLastCategory){			
			Object.extend(obj, Element.prototype); 
		}
		return obj;
	};

	this.moveSliderBarTo = function (element, mainGroupCategory) {
		category = this.getCurrentCategory();
		if (element == category || sliderCatalog.hasClassName('intransition')) {
			return false;
		}

	    var moveLeft = true;
	    if (category.id > element.id) {
	        moveLeft = false;
	    }
	    this.toggleCurrentCategory(category);
	    category = element;
	    this.toggleCurrentCategory(category);
	    this.updateComponents();

	    new Effect.Move(sliderBar, {
			beforeSetup: 
				function(){
					sliderCatalog.addClassName('intransition'); 
					return false;
				},
	        mode: 'absolute',
	        x: element.newPosition,
	        duration: 0.7,
	        queue: {
	            position: 'end',
	            scope: 'sliderBarMove'
	        }
	    });

	    new Effect.Morph(sliderBar, {
	        style: 'width: ' + category.getWidth() + 'px;',
	        duration: 0.7,
	        queue: {
	            position: 'end',
	            scope: 'sliderBarMorph'
	        }
	    });

	    mainGroupSlider.slideOutGroupCategory(mainGroupSlider.getCurrentMainGroupCategory() , !moveLeft);
		setTimeout(function(){mainGroupSlider.slideInGroupCategory(mainGroupCategory, moveLeft);}, betweenAnimationsTimeout);
	    return false;
	};

	this.moveSliderBarToNext = function () {
		var next = this.getCurrentCategory().next();
	    if (next) {
	        this.moveSliderBarTo(next, mainGroupSlider.getCurrentMainGroupCategory().up().next().down());
	    }
	    return false;
	};

	this.moveSliderBarToPrevious = function () {
		var previous = this.getCurrentCategory().previous();
	    if (previous) {
	        this.moveSliderBarTo(previous, mainGroupSlider.getCurrentMainGroupCategory().up().previous().down());
	    }
	    return false;
	};

	this.prepare = function () {
		Effect.Transitions.bump = function(pos) {
      		return 1 - (Math.cos(pos * 4 * Math.PI) * Math.exp(-pos * 12));
    	};
		this.initializeCategories();
		this.updateArrowVisibility();
		this.prepareSliderLayout();
	    return false;
	};
	
	this.initializeCategories = function() {
		this.prepareTemplateLayout();
		this.initializeCategoryVisibility();
		this.shiftCategories();
		this.setOffsetLeft(this.getCategoryDistance());
		return false;
	};
	
	this.prepareTemplateLayout = function(){
		this.setOffsetLeft(minCategoryDistance);
		return false;
	};
	
	this.setOffsetLeft = function(categoryDistance){
		this.setPositions(categoryDistance,
			function(category, position){
				category.setStyle({
					left: position + 'px'
				});
				category.newPosition = position;
			});
	};
	
	this.setNewPositions = function(categoryDistance){
		this.setPositions(
			categoryDistance, 
			function(category, position){
				category.newPosition = position;
			});
	};
	
	this.setPositions = function(categoryDistance, setter){
		var lastVisibleLeft = this.findLastVisibleCategoryOnLeft();
		var lastVisibleLeftIndex = categories.indexOf(lastVisibleLeft);
		var cumulatedWidth = 0;
		var lastCategory, position;
		
		setter(lastVisibleLeft, Math.floor(categoryDistance / 2));
		for(var i = lastVisibleLeftIndex - 1; i >= 0; i--){
			cumulatedWidth += categories[i].getWidth();
			cumulatedCategoryDistance = (lastVisibleLeftIndex - i + 1) * categoryDistance;
			position = lastVisibleLeft.newPosition - cumulatedWidth - cumulatedCategoryDistance;
			setter(categories[i], position);
		}
		
		for(var j = lastVisibleLeftIndex + 1; j < categories.length; j++){
			lastCategory = categories[j - 1];
			position = lastCategory.newPosition + lastCategory.getWidth() + categoryDistance;
			setter(categories[j], position);
		}
	}
	
	this.shiftCategories = function(){
		/* 
		rules for currentCategory:
			-	if currentCategory is not in viewport shift all categories so that
				currentCategory is in the viewport
			-	if currentCategory is not the last category on the right
				shift all categories to the left so that currentCategory is
				the second last visible category on the right	
		*/

		var currentCategoryPosition = categories.indexOf(this.getCurrentCategory());
		var lastVisibleLeftPosition = categories.indexOf(this.findLastVisibleCategoryOnLeft());
		var lastVisibleRightPosition = categories.indexOf(this.findLastVisibleCategoryOnRight());
		var shiftFactor, newLastVisibleRightPosition;
		
		//if currentCategory is last element on the left or is outside
		//of the viewport on the left
		if(currentCategoryPosition <= lastVisibleLeftPosition){
			this.setAllCategoriesInvisible();

			//now shift all categories to the right by <shiftFactor> categories
			shiftFactor = lastVisibleLeftPosition - currentCategoryPosition;
			for(i = currentCategoryPosition; i <= lastVisibleRightPosition - shiftFactor; i++){
				categories[i].isCategoryVisible = true;
			}
		}
		//if currentCategory is last element on the right or is outside
		//of the viewport on the right
		else if(currentCategoryPosition >= lastVisibleRightPosition){
			this.setAllCategoriesInvisible();
			
			//now shift all categories to the left by <shiftFactor> categories
			shiftFactor = currentCategoryPosition - lastVisibleRightPosition;
			newLastVisibleRightPosition = currentCategoryPosition;
			if(!this.getCurrentCategory().isLastCategory()){
				newLastVisibleRightPosition += 1;
			}
			
			for(i = newLastVisibleRightPosition; i >= lastVisibleLeftPosition + shiftFactor; i--){
				categories[i].isCategoryVisible = true;
			}
		}
	};
	
	this.setAllCategoriesInvisible = function(){
		categories.each(function(category){
			category.isCategoryVisible = false;
		});
		return false;
	}

	this.getCategoryDistance = function () {
	    var spaceAvailable;
		var visibleCategories;
	
		visibleCategories = this.getVisibleCategories();
		spaceAvailable = categorySliderWidth - 2 * arrowSpacing;
		spaceAvailable -= this.getTotalWidthOfVisibleCategories(visibleCategories);
		return Math.floor(spaceAvailable / visibleCategories.length);
	};

	this.prepareSliderLayout = function () {
	    sliderBar.setStyle({
			width: this.getCurrentCategory().getWidth() + 'px',
			left: this.getCurrentCategory().offsetLeft + 'px'
		});
	    return false;
	};

	this.updateComponents = function () {
	    this.updateArrowVisibility()
	    this.updateCategories()
	};

	this.updateArrowVisibility = function () {
		var currentCategory = this.getCurrentCategory();
	    if (currentCategory.isLastCategory(true)) {
	      categoryArrowLeft.style.visibility = "hidden";
	    } else {
	       categoryArrowLeft.style.visibility = "visible";
	    }

	    if (currentCategory.isLastCategory(false)) {
	       categoryArrowRight.style.visibility = "hidden";
	    } else {
	       categoryArrowRight.style.visibility = "visible";
	    }
	};

	this.updateCategories = function () {
		var currentCategory = this.getCurrentCategory();
	    categoryPositionsChangedToLeft = false;
	    categoryPositionsChangedToRight = false;

	    if (currentCategory.boundaryReached(true) && !currentCategory.isLastCategory(true)) {
			categoryPositionsChangedToRight = true;
			do{
				this.findFirstInvisibleCategoryOnLeft().showCategory();
			} while(this.getCategoryDistance() > maxCategoryDistance
					&& this.findFirstInvisibleCategoryOnLeft());
			do{
				this.findLastVisibleCategoryOnRight().hideCategory();
			} while(this.getCategoryDistance() < minCategoryDistance
					&&this.findLastVisibleCategoryOnRight());
	    }else if (currentCategory.boundaryReached(false) && !currentCategory.isLastCategory(false)) {
			categoryPositionsChangedToLeft = true;
	        do {
				this.findFirstInvisibleCategoryOnRight().showCategory();
			} while(this.getCategoryDistance() > maxCategoryDistance
					&& this.findFirstInvisibleCategoryOnRight());
			do {
				this.findLastVisibleCategoryOnLeft().hideCategory();
			} while(this.getCategoryDistance() < minCategoryDistance
					&& this.findLastVisibleCategoryOnLeft());
	    }
	
		this.setNewPositions(this.getCategoryDistance());
        categories.each(function(s) {
        	new Effect.Move(s, {
                x: s.newPosition,
                y: 0,
                mode: 'absolute',
                queue: {
                    position: 'end',
                    scope: s.id + 'move'
                }
            });
        });
		return false;
	};

	this.findFirstInvisibleCategoryOnRight = function(){
		return categories.reverse(false).detect(function(e){
			return !e.isCategoryVisible && 
				(e.previous() == undefined || e.previous().isCategoryVisible);
		});
	};
	
	this.findLastVisibleCategoryOnRight = function(){
		return categories.reverse(false).detect(function(e){
			return e.isCategoryVisible;
		});
	};
	
	this.findFirstInvisibleCategoryOnLeft = function(){
		return categories.detect(function(e){
			return !e.isCategoryVisible && 
				(e.next() == undefined || e.next().isCategoryVisible);
		});
	};
	
	this.findLastVisibleCategoryOnLeft = function(){
		return categories.detect(function(e){
			return e.isCategoryVisible;
		});
	};

	this.initializeCategoryVisibility = function (){
		var visibleCategories = this.getVisibleCategoriesForDistance(minCategoryDistance);
		var invisibleCategories = categories.subtract(visibleCategories);
		visibleCategories.each(function(e){
			e.isCategoryVisible = true;
		});
	
		invisibleCategories.each(function(e){
			e.isCategoryVisible = false;
		});
	};

	this.getTotalWidthOfVisibleCategories = function (visibleCategories) {
		var total = 0;
		visibleCategories.each(function(e){
			total += e.getWidth();
		});
		return total;
	};

	this.getVisibleCategoriesForDistance = function (categoryDistance) {
		var margin = Math.floor(categoryDistance / 2);
	    return categories.select(function(s) {
	        var leftBoundary = s.offsetLeft - margin;
	        var rightBoundary = s.offsetLeft + s.getWidth() + margin;
	        return this.isVisible(leftBoundary) && this.isVisible(rightBoundary);
	    }, this);
	};

	this.getVisibleCategories = function (){
		return categories.select(function(e){
			return e.isCategoryVisible;
		});
	};

	this.isVisible = function (position) {
	    return position >= 0
	 		&& position <= categorySliderWidth - arrowSpacing;
	};

	this.toggleCurrentCategory = function (element){
		var name = 'currentCategory';
		if(element.hasClassName(name)){
			element.removeClassName(name);
		}else{
			element.addClassName(name);
		}
		return false;
	};
};
