/* 
next step. want the object to create it's own scrollbars and position them correctly. also want object to resize the scrollbars
to accomodate width of container.
*/

/* ScrollingDiv 0.1
* by Peter Swan - pdswan-at-gmail.com
* Liscensed under Creative Commons: http://creativecommons.org/licenses/by-nc-sa/3.0/
* Please include original header in derivative work
*
* The ScrollingDiv class uses the prototype (http://prototypejs.org/) and scriptaculous (http://script.aculo.us/) 
* libraries to create cross browser custom scrollbars for div elements.
*
* Setting overflow: auto or overflow: scroll on the parent element will give default OS
* scrollbars in the case that javascript is not enabled.
*
* ScrollingDiv( <element to scroll>, <parent element>, <clip width>, <clip height>, <vertical scrollbar>, <horizontal scrollbar>)
*	- instantiates new scrolling div element.
*	- element to scroll will scroll within the parent element with using clip width and clip height to create the
*	  clipping window (defaults to parentElement.width, parentElement.height)
*
* ScrollingDiv.bindButtons( <up button element>, <down button element>, <left button element>, <right button element>, delta)
*	- on click of <up button element <element to scroll> will scroll up by delta = {x | 0 <= x <= 1}
*
* ScrollingDiv.resetContentDim()
*	- can theoretically be used to reset the width and height of the scrolling element in the case of an asynchronous
*	  update to the content.
*
* This class has been tested on the following browsers:
* Firefox 2.0.0.3 (PC)
* Opera 8.54
* IE 6.0.2900
* Safari
* Firefox
*/

var DEBUG = 0;
function debugMessage(msg){
	if( DEBUG ){
		console.log(msg);
	}
}

/* This is a small extension to the Control.Slider to allow for it to support and correctly treat
   tracks and handles with percentage based width/heights. */
Control.PercentageSlider = Class.create( Control.Slider, {
		initialize: function( $super, handle, track, options){
			$super( handle, track, options);
			debugMessage('init: ' + this.maximumOffset() + ', ' + this.minimumOffset() + ', ' + $(track).offsetWidth);
			this.eventResize = this.onResize.bind(this);
			Event.observe( window, 'resize', this.eventResize);	
		},
		
		onResize: function(e){
			debugMessage('caught Slider resize event!' + this.maximumOffset() + ', ' + this.minimumOffset());
			this.trackLength = this.maximumOffset() - this.minimumOffset();

			this.handleLength = this.isVertical() ? 
			(this.handles[0].offsetHeight != 0 ? 
				this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) : 
			(this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth : 
				this.handles[0].style.width.replace(/px$/,""));
			
			this.handles.each( function(h, i){
					this.setValue( this.values[i], i);
			}.bind(this));
		},
		
		dispose: function($super){
			Event.stopObserving(window, 'resize', this.eventResize);
			$super();
		}
});

var ScrollingDiv = Class.create();
ScrollingDiv.prototype = {
	initialize: function( e, vs, hs){
		this.scrollElement = $(e);
		this.scrollElement.makePositioned();
		this.id = this.scrollElement.readAttribute('id');
		this.hs = hs;
		this.vs = vs;
		
		this.ubtn = null;
		this.dbtn = null;
		this.lbtn = null;
		this.rbtn = null;
		this.delta = 0;
		
		this.anchors = $H();
		this.anchor_elements = $H();
		this.current_anchor = null;
		
		this.eventResize = this.resetContentDim.bind(this);
		
		// Get content width and height
		this.resetContentDim();
		this.scrollElement.scrollLeft = this.scrollElement.scrollTop = 0;
		Event.observe(window, 'resize', this.eventResize);
		
		/* If there is a horizontal scrollbar set up
		onslide and onchange functions
		*/
		if( this.hs ){
			this.hs = hs;
			this.hs.options.onChange = this.hs.options.onSlide = this.scrollHorizontal.bind(this);
		}
		
		if( this.vs ){
			this.vs = vs
			this.vs.options.onChange = this.vs.options.onSlide = this.scrollVertical.bind(this);
		}
		
		this.addAnchors();
		this.bindAnchors();
		
		/* Functions to manage a periodical executer that
		manages up/down/left/right button functionality
		*/
		this.startUp = function(){
			this.stop();
			this.vs.setValueBy(-this.delta);
			this.btnPe = new PeriodicalExecuter(
				function(){
					this.vs.setValueBy(-this.delta);
				}.bind(this), 0.1);
		};
		this.startDown = function(){
			this.stop();
			this.vs.setValueBy(this.delta);
			this.btnPe = new PeriodicalExecuter(
				function(){
					this.vs.setValueBy(this.delta);
				}.bind(this), 0.1);
		};
		this.startLeft = function(){
			this.stop();
			this.hs.setValueBy(-this.delta);
			this.btnPe = new PeriodicalExecuter(
				function(){
					this.hs.setValueBy(-this.delta);
				}.bind(this), 0.1);
		};
		this.startRight = function(){
			this.stop();
			this.hs.setValueBy(this.delta);
			this.btnPe = new PeriodicalExecuter(
				function(){
					this.hs.setValueBy(this.delta);
				}.bind(this), 0.1);
		};
		this.stop = function(){
			if( this.btnPe ){
				this.btnPe.stop();
				delete this.btnPe;
			}
		};
		
		this.eventStop = this.stop.bind(this);
		this.eventStartUp = this.startUp.bind(this);
		this.eventStartDown = this.startUp.bind(this);
		this.eventStartLeft = this.startLeft.bind(this);
		this.eventStartRight = this.startRight.bind(this);
	},
	
	bindAnchors: function(){
		$$('a.' + this.id + '_anchor').each( function(e){
			var href = e.readAttribute('href').split('#')[1];
			if( href && this.anchors.get(href)){
				this.anchor_elements.set(href, e);
				e.observe('click', this.goToAnchor.bindAsEventListener(this, href));
			}
		}, this);
	},
	
	addAnchors: function( s ){
		var select = s || '.anchor';
		this.scrollElement.select(select).each( function(e){
			this.addAnchor(e);
		}, this);
	},
	
	addAnchor: function(elm){
		if( Object.isElement(elm) && this.scrollElement.descendants().member(elm) ){
			var id = arguments[1] || elm.readAttribute('id');
			if( id ){
				var o = elm.positionedOffset();
				debugMessage('positioned offset: ' + o[0] + ', ' + o[1]);
				this.anchors.set(id, o);
			}else{
				return false;
			}
		}else{
			return false;
		}
	},
	
	goToAnchor: function(){
		var name;
		if( arguments.length > 1){
			try{
				arguments[0].stop();
			}catch(e){}
			name = arguments[1];
		}else{
			name = arguments[0];
		}
		
		var anchor = this.anchors.get(name);
		if( anchor ){
			if( this.hs && !this.hs.disabled ){
				var left = anchor[0] * this.hs.maximum / (this.contentWidth - this.clipWidth);
				this.hs.setValue(left);
			}
			if( this.vs && !this.vs.disabled ){
				var top = anchor[1] * this.vs.maximum / (this.contentHeight - this.clipHeight);
			}
			if( this.current_anchor ){
				//try{
					this.anchor_elements.get(this.current_anchor).removeClassName('active');
				//}catch(e){}
			}
			this.current_anchor = name;
			//try{
				this.anchor_elements.get(this.current_anchor).addClassName('active');
			//}catch(e){}
		}
	},
	
	scrollHorizontal: function(v){
		/*var left = Math.round((v / this.hs.maximum) * (this.clipWidth - this.contentWidth));
		this.scrollElement.setStyle({left: left + 'px'});*/
		var left = Math.round((v / this.hs.maximum) * -(this.clipWidth - this.contentWidth));
		this.scrollElement.scrollLeft = left;
	},
	
	scrollVertical: function(v){
		/*var top = Math.round((v / this.vs.maximum) * (this.clipHeight - this.contentHeight));
		this.scrollElement.setStyle({top: top + 'px'});*/
		var top = Math.round((v / this.vs.maximum) * -(this.clipHeight - this.contentHeight));
		this.scrollElement.scrollTop = top;
	},
	
	resetScrollbarDims: function(){
		if( this.hs ){
			this.hs.onResize();
		}
		if( this.vs ){
			this.vs.onResize();
		}
	},
	
	/* Get dimensions of content element, if dimensions are less than
	the clipping dimensions, disable the scrollbars */
	resetContentDim: function(){
		debugMessage('element: ' + this.scrollElement.readAttribute('id'));
		this.scrollElement.setStyle({'overflow': 'auto'});
		this.contentWidth = this.scrollElement.scrollWidth;
		this.contentHeight = this.scrollElement.scrollHeight;
		this.scrollElement.setStyle({'overflow': 'hidden'});
		this.clipWidth = this.scrollElement.getWidth();
		this.clipHeight = this.scrollElement.getHeight();
		
		debugMessage('clip dimensions: ' + this.clipWidth + ', ' + this.clipHeight);
		debugMessage('content dimensions: ' + this.contentWidth + ', ' + this.contentHeight);
		
		/*if( this.vs ){
			if( this.contentHeight <= this.clipHeight ){
				this.vs.setDisabled();
				this.vs.track.setStyle({visibility: 'hidden'});
				if( this.ubtn ){
					this.ubtn.setStyle({visibility: 'hidden'});
				}
				if( this.dbtn ){
					this.dbtn.setStyle({visibility: 'hidden'});
				}
			}else{
				this.vs.setEnabled();
				this.vs.track.setStyle({visibility: 'visible'});
				if( this.ubtn ){
					this.ubtn.setStyle({visibility: 'visible'});
				}
				if( this.dbtn ){
					this.dbtn.setStyle({visibility: 'visible'});
				}
			}
		}
		if( this.hs ){
			if( this.contentWidth <= this.clipWidth ){
				this.hs.setDisabled();
				this.hs.track.setStyle({visibility: 'hidden'});
				if( this.lbtn ){
					this.lbtn.setStyle({visibility: 'hidden'});
				}
				if( this.rbtn ){
					this.rbtn.setStyle({visibility: 'hidden'});
				}
			}else{
				this.hs.setEnabled();
				this.hs.track.setStyle({visibility: 'visible'});
				if( this.lbtn ){
					this.lbtn.setStyle({visibility: 'visible'});
				}
				if( this.rbtn ){
					this.rbtn.setStyle({visibility: 'visible'});
				}
			}
		}*/
		if( this.vs ){
			if( this.contentHeight <= this.clipHeight ){
				this.vs.setDisabled();
				this.vs.track.hide();
				if( this.ubtn ){
					this.ubtn.hide();
				}
				if( this.dbtn ){
					this.dbtn.hide();
				}
			}else{
				this.vs.setEnabled();
				this.vs.track.show();
				if( this.ubtn ){
					this.ubtn.show();
				}
				if( this.dbtn ){
					this.dbtn.show();
				}
			}
		}
		if( this.hs ){
			if( this.contentWidth <= this.clipWidth ){
				this.hs.setDisabled();
				this.hs.track.hide();
				if( this.lbtn ){
					this.lbtn.hide();
				}
				if( this.rbtn ){
					this.rbtn.hide();
				}
			}else{
				this.hs.setEnabled();
				this.hs.track.show();
				if( this.lbtn ){
					this.lbtn.show();
				}
				if( this.rbtn ){
					this.rbtn.show();
				}
			}
		}
	},
	
	/* Takes elements that will act as up/down/left/right buttons as well
	as a delta value which controls rate of scrolling */
	bindButtons: function( ubtn, dbtn, lbtn, rbtn, delta){
		this.ubtn = $(ubtn);
		this.dbtn = $(dbtn);
		this.lbtn = $(lbtn);
		this.rbtn = $(rbtn);
		this.delta = delta;
		var t = 0;
		
		if( this.ubtn ){
			if( this.vs ){
				Event.observe( this.ubtn, 'mousedown', this.eventStartUp);
				Event.observe( this.ubtn, 'mouseout', this.eventStop);
				t++;
			}
		}
		if( this.dbtn ){
			if( this.vs ){
				Event.observe( this.dbtn, 'mousedown', this.eventStartDown);
				Event.observe( this.dbtn, 'mouseout', this.eventStop);
				t++;
			}
		}
		if( this.lbtn ){
			if( this.hs ){
				Event.observe( this.lbtn, 'mousedown', this.eventStartLeft);
				Event.observe( this.lbtn, 'mouseout', this.eventStop);
				t++;
			}
		}
		if( this.rbtn ){
			if( this.hs ){
				Event.observe( this.rbtn, 'mousedown', this.eventStartRight);
				Event.observe( this.rbtn, 'mouseout', this.eventStop);
				t++;
			}
		}
		if( t > 0){
			Event.observe(document, 'mouseup', this.eventStop);
		}
		this.resetContentDim();
	},
	
	destroy: function(){
		var t = 0;
		if( this.rbtn){
			this.rbtn.stopObserving('mousedown', this.eventStartRight);
			this.rbtn.stopObserving('mouseout', this.eventStop);
		}
		if( this.lbtn){
			this.lbtn.stopObserving('mousedown', this.eventStartLeft);
			this.lbtn.stopObserving('mouseout', this.eventStop);
		}
		if( this.ubtn){
			this.ubtn.stopObserving('mousedown', this.eventStartUp);
			this.ubtn.stopObserving('mouseout', this.eventStop);
		}
		if( this.dbtn){
			this.dbtn.stopObserving('mousedown', this.eventStartDown);
			this.dbtn.stopObserving('mouseout', this.eventStop);
		}
		if( this.rbtn || this.lbtn || this.ubtn || this.dbtn){
			Event.stopObserving(document, 'mouseup', this.eventStop);
		}
	}
};

function createScrollingDiv( elm ){
	debugMessage('createScrollingDiv');
	elm = $(elm);
	if( elm ){
		var id = elm.readAttribute('id');
		if( !id ){
			debugMessage('no id!');
			return null;
		}
		var content = elm;
		if( content ){
			debugMessage('content: ' + content.inspect());
			var vs_contain = $$('.' + id + '_vertical_scroll_bar')[0];
			var hs_contain = $$('.' + id + '_horizontal_scroll_bar')[0];
			var vs_components = {};
			var hs_components = {};
			var vs;
			var hs;
			if( vs_contain ){
				vs_components = getScrollControls(vs_contain);
			}
			if( hs_contain ){
				hs_components = getScrollControls(hs_contain);
			}
			if( vs_components.track && vs_components.handle ){
				vs = new Control.PercentageSlider( vs_components.handle, vs_components.track, {axis: 'vertical'});
			}
			if( hs_components.track && hs_components.handle ){
				hs = new Control.PercentageSlider( hs_components.handle, hs_components.track, {axis: 'horizontal'});
			}
			if( (!hs && !vs)){
				return null;
			}else{
				var temp = new ScrollingDiv( content, vs, hs);
				temp.bindButtons( vs_components.up_button, vs_components.down_button, hs_components.left_button, hs_components.right_button, 0.05);
				return temp;
			}
		}else{
			debugMessage('no content!');
			return null;
		}
	}else{
		debugMessage('no element!');
		return null;
	}
}

function getScrollControls( elm ){
	var ret = {
		down_button: null,
		up_button: null,
		right_button: null,
		left_button: null,
		track: null,
		handle: null
	};
	elm = $(elm);
	if( elm ){
		ret.down_button = elm.down('.down_button');
		ret.up_button = elm.down('.up_button');
		ret.right_button = elm.down('.right_button');
		ret.left_button = elm.down('.left_button');
		ret.track = elm.down('.track');
		ret.handle = elm.down('.handle');
	}
	return ret;
}