// ScrollControl adapted by Jürgen Müller-Lütken, 2009, www.p-six.de/sign/
// based on ScrollControl.js from http://blog.aplusmedia.de/moo-scrollcontrol/
// Michael Grundkötter, www.aplusmedia.de

//this is needed to catch mousewheel events
Element.Events.extend({
	'wheelup': {
		type: Element.Events.mousewheel.type,
		map: function(event) {
			event = new Event(event);
			if(event.wheel >= 0) {
				this.fireEvent('wheelup', event);
			}
		}
	},
	
	'wheeldown': {
		type: Element.Events.mousewheel.type,
		map: function(event) {
			event = new Event(event);
			if(event.wheel < 0) {
				this.fireEvent('wheeldown', event);
			}
		}
	}
});

var ScrollControl = new Class({
	options: {
		'createControls': 		false,	//if this is set to true, the default controls are added automatically.
										//if you set this to false (default), you have to define the controls in HTML
										//and give them as parameters to this control (see: this.initialize())
		'htmlElementPrefix': 	'',	//if you want to use several scroll controls on the same 
													//site but with different styles, give them another prefix here
		'wheelStepSize': 		15,	//amount of pixels that the content is scrolled on mousewheel event
		'scrollStepSize': 		10,	//amount of pixels that the content is scrolled on button click
		
		'scrollUpImage': 'images/all/scroll_up',
		'scrollDownImage': 'images/all/scroll_down'
	},
	/**
	 * Initializes a new scroll control.
	 * Usage: 
	 * var myScroller = new ScrollControl($('content'), {}, $('scrolltrack'), $('scrollknob'), $('scrollUpBtn'), $('scrollDownBtn'));
	 * or
	 * var myScroller = new ScrollControl($('content'), {'createControls': true});
	 * Example: see http://www.aplusmedia.de for an example
	 * 
	 * Known issues/TODOs:
	 * - vertical scrollbars are not yet possible
	 * 
	 * @param {HTMLelement} contentContainer which contains the content which shall be scrolled
	 * @param {Object}		optional, you can adjust different settings here
	 * @param {HTMLelement} optional, scrollTrack the container which limits the knob
	 * @param {HTMLelement} optional, scrollKnob the actual scroll handle knob
	 * @param {HTMLelement} optional, scrollUpBtn a button which can be clicked to scroll up
	 * @param {HTMLelement} optional, scrollDownBtn a button which can be clicked to scroll down
	 */
	initialize: function(contentContainer, options, maskElement, scrollContainer, scrollTrack, scrollKnob, scrollKnobCenter, scrollUpBtn, scrollDownBtn) {
		this.maskContainer = maskElement;
		this.contentContainer = contentContainer;
		this.scrollContainer = scrollContainer;
		this.scrollTrack = scrollTrack;
		this.scrollUpButton = scrollUpBtn;
		this.scrollDownButton = scrollDownBtn;
		this.scrollKnob = scrollKnob;
		this.scrollKnobCenter = scrollKnobCenter;
		this.scrollUpBtn = scrollUpBtn;
		this.scrollDownBtn = scrollDownBtn;
		
		this.setOptions(options);
		//fix opera wheel directions
		if(window.opera) {
			this.options.wheelStepSize *= -1;
		}
		//add scrollbar functionality
		
		//create controls or use user set controls
		if(this.options.createControls) {
			this.createControls();
		}
		
		//adjust width
		this.adjustWidth();
		//adjust scrollKnob in size, depending on content length
		this.adjustScroller();
		//add button functionality
		this.scrollKnob.addEvent('mousedown', this.stopScrolling.bind(this));
		if($defined(this.scrollUpBtn)) {
			this.scrollUpBtn.addEvents({	'mousedown': this.startScrolling.bind(this, 'up'),
									'mouseup': this.stopScrolling.bind(this),
									'mouseout': this.stopScrolling.bind(this),
									'mouseover': this.highlight.bind(this.scrollUpBtn, this.options.scrollUpImage),
									'mouseout': this.darken.bind(this.scrollUpBtn, this.options.scrollUpImage)
			}).setStyle('display', 'block');
		}
		if($defined(this.scrollDownBtn)) {
			this.scrollDownBtn.addEvents({	'mousedown': this.startScrolling.bind(this, 'down'),
										'mouseup': this.stopScrolling.bind(this),
										'mouseout': this.stopScrolling.bind(this),
										'mouseover': this.highlight.bind(this.scrollDownBtn, this.options.scrollDownImage),
										'mouseout': this.darken.bind(this.scrollDownBtn, this.options.scrollDownImage)
			}).setStyle('display', 'block');
		}
		//add mousewheel functionality
		this.contentContainer.addEvents({
			'wheelup': this.doWheelUp.bind(this),
			'wheeldown': this.doWheelDown.bind(this)
		});
	},
	
	/**
	 * Creates the controls, including scroll up and down buttons and a 
	 * scrolltrack with something to grab and drag around
	 */
	createControls: function() {
		this.scrollUpBtn = new Element('div', {
			'id': this.options.htmlElementPrefix + 'scrollUpBtn'
		}).injectInside(this.scrollContainer);
		this.scrollTrack = new Element('div', {
			'id': this.options.htmlElementPrefix + 'scrolltrack'
		}).injectAfter(this.scrollUpBtn);
		this.scrollKnob = new Element('div', {
			'id': this.options.htmlElementPrefix + 'scrollknob'
		}).injectInside(this.scrollTrack);
		this.scrollDownBtn = new Element('div', {
			'id': this.options.htmlElementPrefix + 'scrollDownBtn'
		}).injectAfter(this.scrollTrack);
	},
	
	adjustScroller: function() {
		this.contentContainer.setStyles({'top': 0, 'height': 'auto'});
		var contentHeight = this.contentContainer.getCoordinates().height;
		var maskHeight = coords.height;
		var trackHeight = maskHeight-42;
		if(contentHeight < maskHeight)
			coords.height = contentHeight+10;
		
		this.scrollContainer.setStyles({'display': 'block', 'height': maskHeight});
		this.scrollTrack.setStyle('height', trackHeight);
		var knobHeight;
		if((knobHeight = Math.round(Math.pow(trackHeight , 2) / contentHeight )) < 10)
			knobHeight = 10;
		this.scrollKnob.setStyle('height', knobHeight );
		this.scrollKnobCenter.setStyle ('height', knobHeight-4 );
		this.currentStep = 0;
		this.scrollHeight = contentHeight - maskHeight;
		this.scrollContainer.setStyle('visibility', 'hidden');
		//if content is too short, do not display the Scroll-Element and do nothing further
		if(contentHeight < trackHeight || this.scrollHeight < 20) {
			this.scrollContainer.setStyle('display', 'none');
			return;
		}
		this.initSlider()
	},
	
	adjustWidth: function() {
		this.contentContainer.setStyle( 'position', 'absolute' );
	},
	
	
	initSlider: function() {
		if(this.mySlide)
			delete(this.mySlide);
		if(this.scrollInterval)
			this.stopScrolling();
		this.mySlide = new Slider(this.scrollTrack, this.scrollKnob, {
			steps: this.scrollHeight,
			mode: 'vertical',
			onChange: this.refresh.bind(this)
		});
	},
	
	showScroller: function() {
		this.scrollContainer.setStyle('visibility', 'visible');
	},
	
	hideScroller: function() {
		this.scrollContainer.setStyle('visibility', 'hidden');
	},
	
	/**
	 * Handles mousewheel action upwards.
	 * Stops wheel event to avoid multiple scrollbars to be invoked.
	 * 
	 * @param {Event} e
	 */
	doWheelUp: function(e) {
		new Event(e).stop();
		this.scrollUp(this.options.wheelStepSize);
	},
	
	/**
	 * Handles mousewheel action downwards.
	 * Stops wheel event to avoid multiple scrollbars to be invoked.
	 * 
	 * @param {Event} e
	 */
	doWheelDown: function(e) {
		new Event(e).stop();
		this.scrollDown(this.options.wheelStepSize);
	},
	
	/**
	 * Starts scrolling on click on the buttons
	 * 
	 * @param {Object} mode
	 */
	startScrolling: function(mode) {
		if(this.scrollIntervall)
			this.stopScrolling();
		if(mode == 'up') {
			this.scrollIntervall = this.scrollUp.periodical(50, this, this.options.scrollStepSize);
		} else {
			this.scrollIntervall = this.scrollDown.periodical(50, this, this.options.scrollStepSize);
		}
	},
	
	/**
	 * Invoked when the scrolling buttons are released.
	 */
	stopScrolling: function() {
		$clear(this.scrollIntervall);
	},
	
	/**
	 * Refreshes the entire control.
	 * 
	 * @param {Integer} step
	 */
	refresh: function(step) {
		if(step == this.currentStep) {
			return;
		}
		step = Math.round(step.toInt().limit(0, this.scrollHeight));
		this.mySlide.set(step);
		this.currentStep = step;
		this.contentContainer.setStyle('top', -step);
	},
	
	/**
	 * Used to scroll the content upwards
	 * 
	 * @param {Integer} amount of pixels to scroll
	 */
	scrollUp: function(amount) {
		this.refresh(this.currentStep - amount);
	},
	
	/**
	 * Used to scroll the content downwards
	 * 
	 * @param {Integer} amount of pixels to scroll
	 */
	scrollDown: function(amount) {
		this.refresh(this.currentStep + amount);
	},
	
	highlight: function(image) {
		this.src = image + '_on.png';
	},
	
	darken: function( image) {
		this.src = image + '.png';
	}
		
});
ScrollControl.implement(new Options);