/*

Class: Scroller

	Adds a scrollbar to a specific div. The scrollbar is implemented using a Script.aculo.us slider.

	The class reparents the original div, creates a slider and ties the reparented div to the slider,

	setting any properties necessary on the divs to make it all work. The scrollbar can be styled using

	CSS. The track of the scrollbar has class 'scroll-track', 'scroll-track-top' and 'scroll-track-bot',

	the thumb has class 'scroll-handle', 'scroll-handle-top' and 'scroll-handle-bot'.

	

properties:

	myIndex - an integer used to generate a unique ID for use in, for example, div ids.

	outerBox - the div that holds the scrollpane + scrollbar

	innerBox - the div that holds the scrollpane

	innerHeight - the height of the inner box.

	viewportHeight - the height of the view onto the scrolled div.

	track - a div that holds the script.aculo.us slider (the scrollbar)

	trackHeight - the height of the slider

	handle - the div for the 'thumb' of the scrollbar

	handleHeight - the height of the thumb

	slider - the script.aculo.us slider itself

	ieDecreaseBy - a fudge factor used when calculating the width of innerBox

	

*/

var Scroller = Class.create();



/*

property: Scroller.ids

	A cache of Scrollers indexed by the ID of the original div.

 */

Scroller.ids = new Object();



/*

property: Scroller.i

	A unique ID generator.

 */

Scroller.i = 0;



Scroller.prototype = {

	/*

	constructor: initialize	

		Wrap the passed div in a scrollpane.

	

	parameters:	

		el - the div to add a scrollbar to.

	 */

  initialize: function(el) {

    this.outerBox = el;

    this.decorate();

  },

  

  /*

  function: decorate  

  	create the necessary elements to implement the scrollbar and wire up events.

   */

  decorate: function() {

	$(this.outerBox).makePositioned(); // Fix IE

	

	// Seed a unique ID

	Scroller.i = Scroller.i + 1;

	this.myIndex = Scroller.i;

	

	//wrap the existing content in an intermediate inner box

	this.innerBox = document.createElement("DIV");

	this.innerBox.className="scroll-innerBox";

	$(this.innerBox).makePositioned();	// Fix IE

	this.innerBox.style.cssFloat=this.innerBox.style.styleFloat='left';	// Need the scrollbar to appear next to the scrollpane

	this.innerBox.id="scroll-innerBox-"+Scroller.i;

	this.innerBox.style.top = "0px";

	

	//Transfer the contents of Outer Box to Inner Box

	while (this.outerBox.hasChildNodes()) {

		this.innerBox.appendChild(this.outerBox.firstChild);

	}

	this.innerBox.style.overflow="hidden";

	//turn off scrolling on the outer div

	this.outerBox.style.overflow="hidden";



	// create a track

	this.track=document.createElement('div');

	this.track.className="scroll-track";

	$(this.track).makePositioned();

	this.track.style.cssFloat=this.track.style.styleFloat='left';

	this.track.id="scroll-track-"+Scroller.i;

	// Fix IE line-height bug. Sigh.

	this.track.appendChild(document.createComment(''));



	// Create the top button

	this.tracktop=document.createElement('div');

	this.tracktop.className="scroll-track-top";

	$(this.tracktop).makePositioned();

	this.tracktop.style.cssFloat=this.tracktop.style.styleFloat='left';

	this.tracktop.id="scroll-track-top-"+Scroller.i;

	// Fix IE line-height bug. Sigh.

	this.tracktop.appendChild(document.createComment(''));

	

	// Create the bottom button

	this.trackbot=document.createElement('div');

	this.trackbot.className="scroll-track-bot";

	$(this.trackbot).makePositioned();

	this.trackbot.style.cssFloat=this.trackbot.style.styleFloat='left';

	this.trackbot.id="scroll-track-bot-"+Scroller.i;

	// Fix IE line-height bug. Sigh.

	this.trackbot.appendChild(document.createComment(''));



	// Create the handle

	this.handle=document.createElement('div');

	this.handle.className="scroll-handle-container";

	this.handle.id="scroll-handle-container"+Scroller.i;



	// Create the handle middle

	this.handle_middle=document.createElement('div');

	this.handle_middle.className="scroll-handle";

	$(this.handle_middle).makePositioned();

	this.handle_middle.id="scroll-handle-"+Scroller.i;

	// Fix IE line-height bug. Sigh.

	this.handle_middle.appendChild(document.createComment(''));



	// Create the handle top cap

	this.handletop=document.createElement('div');

	this.handletop.className="scroll-handle-top";

	$(this.handletop).makePositioned();

	this.handletop.id="scroll-handle-top-"+Scroller.i;

	// Fix IE line-height bug. Sigh.

	this.handletop.appendChild(document.createComment(''));



	// Create the handle bottom cap

	this.handlebot=document.createElement('div');

	this.handlebot.className="scroll-handle-bot";

	$(this.handlebot).makePositioned();

	this.handlebot.id="scroll-handle-bot-"+Scroller.i;

	// Fix IE line-height bug. Sigh.

	this.handlebot.appendChild(document.createComment(''));



	this.track.hide();

	this.tracktop.hide();

	this.trackbot.hide();



	this.outerBox.appendChild(this.innerBox);

	this.outerBox.appendChild(this.tracktop);

	this.handle.appendChild(this.handletop);

	this.handle.appendChild(this.handle_middle);

	this.handle.appendChild(this.handlebot);

	this.track.appendChild(this.handle);

	this.outerBox.appendChild(this.track);

	this.outerBox.appendChild(this.trackbot);



	this.slider = new Control.Slider($(this.handle).id, $(this.track).id, {axis:'vertical',

									 minimum: 0,
									
									maximum: $(this.outerBox).clientHeight});

	this.slider.options.onSlide = this.slider.options.onChange = this.onChange.bind(this);

	setTimeout(this.resetScrollbar.bind(this, false), 10);



    this.domMouseCB = this.MouseWheelEvent.bindAsEventListener(this, this.slider);

    this.mouseWheelCB = this.MouseWheelEvent.bindAsEventListener(this, this.slider);

    this.trackTopCB = this.tracktopEvent.bindAsEventListener(this, this.slider);

    this.trackBotCB = this.trackbotEvent.bindAsEventListener(this, this.slider);

    

	//Events control

	$(this.outerBox).observe('DOMMouseScroll', this.domMouseCB); // Mozilla

	$(this.outerBox).observe('mousewheel', this.mouseWheelCB);// IE/Opera

	$(this.tracktop).observe('mousedown', this.trackTopCB);

	$(this.trackbot).observe('mousedown', this.trackBotCB);

  },

  

  release: function() {

    $(this.outerBox).stopObserving('DOMMouseScroll', this.domMouseCB);

	$(this.outerBox).stopObserving('mousewheel', this.mouseWheelCB);// IE/Opera

	$(this.tracktop).stopObserving('mousedown', this.trackTopCB);

	$(this.trackbot).stopObserving('mousedown', this.trackBotCB);

  },

  

  /*

  function: resetScrollbar  

  	Re-calculate the geometry of the scrollbar. Typically called from an event handler.

	

	args:	

		repeat - if true, set timer to re-calculate to fix IE bug on resize window.

   */

  resetScrollbar: function(repeat) {

		this.track.hide();

		this.tracktop.hide();

		this.trackbot.hide();

		this.enableScroll = false;

		this.innerHeight = $(this.outerBox).clientHeight;

		this.innerBox.style.height = this.innerHeight + "px";

		var newWidth = $(this.outerBox).clientWidth;



    	var tth = Element.getStyle(this.tracktop,"height");

    	if (tth)

    	   tth = tth.replace("px","");

    	else

    	   tth = 0;

    

        var hth = Element.getStyle(this.handletop,"height");

        if (hth)

    	   hth = hth.replace("px","");

    	else

    	   hth = 0;

    

		if (this.innerHeight < this.innerBox.scrollHeight) {

			this.viewportHeight = this.innerHeight - tth*2;

			this.slider.trackLength = this.viewportHeight;

			this.track.style.height = this.viewportHeight + "px";		

			this.handleHeight = Math.round(this.viewportHeight * this.innerHeight / this.innerBox.scrollHeight)/3;
			// intervention : c'est sur la ligne de dessus que se décide la hauteur du bouton gris... ici j'ai divisé par 3 pour qu'il soit plus discret... héhé...

			if(this.handleHeight < (hth*2))

				this.handleHeight = (hth*2);

			if (this.handleHeight < 10)

			     this.handleHeight = 10;


			this.handle.style.height = this.handleHeight + "px";

			this.handle_middle.style.height = this.handleHeight - hth*2 + "px";

			this.handletop.style.height = hth + "px";

			this.slider.handleLength = this.handleHeight;

			this.track.style.display = 'inline';

			this.tracktop.style.display = 'inline';

			this.trackbot.style.display = 'inline';

			this.ieDecreaseBy = 1;   // Firefox seems to have an off-by one error, so allow for it.

			if (this.outerBox.currentStyle) {

				var borderWidth = this.outerBox.currentStyle["borderWidth"].replace("px","");

				if(!isNaN(borderWidth)) {

					this.ieDecreaseBy = (borderWidth) * 2;

				}

			}

			newWidth = ($(this.outerBox).clientWidth - $(this.track).clientWidth - this.ieDecreaseBy);

			this.enableScroll = true;

		}

		//Set the width of of the scrollpane (aka innerBox).

		this.innerBox.style.width = newWidth + "px";

		//Fix IE resize event Bug 

		if(repeat) {

			setTimeout(this.resetScrollbar.bind(this, false), 10);

		}

  },

  

	//Mouse wheel code from http://adomas.org/javascript-mouse-wheel/

	MouseWheelEvent: function(event, slider) {

		var delta = 0;

		if (!event) //For IE.

			event = window.event;

		if (event.wheelDelta) { //IE/Opera.

			delta = event.wheelDelta / 120;

			/*if (window.opera) //In Opera 9, delta differs in sign as compared to IE

				delta = -delta;   But it isn't necessary with Opera v9.51*/

		} else if (event.detail) { //Mozilla case

			delta = -event.detail / 3;

		}

		if (delta)

			slider.setValueBy(-delta / 10);

		Event.stop(event);

	},



	trackbotEvent: function(event, slider) {

		if (Event.isLeftClick(event)) {	

			slider.setValueBy(0.2);

			Event.stop(event);

		}

	},



	tracktopEvent: function(event, slider) {

		if (Event.isLeftClick(event)) {

			slider.setValueBy(-0.2);

			Event.stop(event);

		}

	},



  /*

  function: onChange  

  	Called when the script.aculo.us slider has changed (i.e. when it has been dragged). Scroll the inner box.

	

	args:	

		val - not used.

   */

	onChange: function(val) {

		if(this.enableScroll)

			this.innerBox.scrollTop = Math.round (val * (this.innerBox.scrollHeight-this.innerBox.offsetHeight));

	}

}



/*

function: Scroller.setAll

	Search for divs of the class 'makeScroll' and wrap them in a Scroller.

 */

Scroller.setAll = function () {

	$$('.makeScroll').each(function(item) {

		Scroller.ids[item.id] = new Scroller(item);

	});

}



/*

function: Scroller.reset

	If the passed element has class 'makeScroll', wrap it in a Scroller.

 */

Scroller.reset = function (body_id) {

	if ($(body_id).className.match(new RegExp("(^|\\s)makeScroll(\\s|$)"))) {

	   if (Scroller.ids[body_id])

	       Scroller.ids[body_id].release();

	       

		Scroller.ids[body_id] = new Scroller($(body_id));

	}

}



/*

property: Scroller.updateAll

	Reset all of the scrollbars.

 */

Scroller.updateAll = function () {

	$H(Scroller.ids).each(function(pair) {

		Scroller.ids[pair.key].resetScrollbar(true);

	});

}



/*

	Hook up some global event handlers.

 */

Event.observe(window, "load", Scroller.setAll);

Event.observe(window, "resize", Scroller.updateAll);

http://www.jools.net/javascripts/splitpane.js

/*

Class: SplitPane

	Separates two divs with a draggable divider that you can use to resize the divs, kind of like

	a frame but without using frames! The two divs should be siblings, that is they should both

	have the same parent. You can have an arbitrary number of such siblings separated using different

	instances of this class, i.e. you can have any number of columns separated by a draggable divider

	that alows you to resize them at will.

	

	You can ask to be notified when the following events occur

	

	- A drag starts.

	- A drag occurs.

	- A drag ends.

	

	This allows you to perform any housekeepng not already performed for you.

	

	You can disable the	resizing.

	

	You can ask an instance to serialize itself as an HTTP POST argument list, this is useful in

	combination with onEnd hooking to save the current div sizes on the server.

	

NOTE:

	In IE all parent divs must have a height other than 'auto'.

	div1 and div2 should probably have overflow=hidden set.

*/

var SplitPane = Class.create();



/*

property: SplitPane.cache

	Holds all instances of SplitPane. Used to delay intialization until Window.onLoad().

*/

SplitPane.cache = new Array();

SplitPane.cacheIndex = 0;

SplitPane.handleWidth = 10;	// Width of the handle



SplitPane.prototype = {

	/*

	Constructor: intialize

	

	parameters:

		div1_id - a div, or the ID of a div notionally on the 'left' of the divider.

		div1_width - the initial width of div1 as a percentage of its parent's width

		div2_id - a div, or the ID of a div notionally on the 'right' of the divider.

		div2_left - the coordinate of the left edge of div2 relative to the parent div as a percentage.

		div2_width - the initial width of div2 as a percentage of its parent's width.

		options - an associative array of optional arguments which include

		

	options:

		onStart - a function to be called when a drag of the divider starts.

		onDrag - a function to be called when a drag occurs.

		onEnd - a function to call when a drag ends.

		active - if true then resizing can occur. If false then the two divs are set to

			the specified widths and that is that. Defaults to false.

	*/

	initialize: function(div1_id, div1_width, div2_id, div2_left, div2_width, options) {

		this.options = { 

			onStart:    Prototype.emptyFunction,

			onDrag:     Prototype.emptyFunction,

			onEnd:      Prototype.emptyFunction,

			active:		false

		}

		

		Object.extend(this.options, options || {});

		

		this.div1 = $(div1_id);

		this.div2 = $(div2_id);

		this.container = this.div1.parentNode;	// This had better be the same for both divs

		this.div1_width = div1_width;	// as a percentage

		this.div2_left = div2_left;		// as a percentage

		this.div2_width = div2_width;	// as a percentage

		

		SplitPane.cache[SplitPane.cacheIndex] = this;

		SplitPane.cacheIndex = SplitPane.cacheIndex+1;

	},



	/*

		function: set

			create a divider. If its marked as 'active' then wire it up for events.

	*/

	set: function() {

		Element.makePositioned(this.container);	// Fix IE

		

		// Change widths to percents so that window resizing works

		this.div1.style.width = this.div1_width + "%";

		this.div2.style.width = this.div2_width + "%";

		this.div2.style.left  = this.div2_left + "%";

		

		// Create a divider and make it a child of container

		this.divider = document.createElement("DIV");

		this.container.appendChild(this.divider);

		this.divider.className="splitpane-divider";

		this.divider.style.position="absolute";

		this.divider.style.width=SplitPane.handleWidth + "px";

		this.divider.style.top="0px";

		this.divider.style.zIndex=1000;



		this.containerWidth = this.getWidth(this.container);

		

		this.setDividerX();

		this.setDividerHeight();



		if (this.options.active) {

			this.eventMouseDown = this.startDrag.bindAsEventListener(this);

			this.eventMouseUp   = this.endDrag.bindAsEventListener(this);

			this.eventChangeCursor = this.cursor.bindAsEventListener(this);

			this.eventMouseMove = this.update.bindAsEventListener(this);

	

			Event.observe(this.divider, "mousedown", this.eventMouseDown);

			Event.observe(document, "mouseup", this.eventMouseUp);

			Event.observe(this.divider, "mousemove", this.eventChangeCursor);

			Event.observe(document, "mousemove", this.eventMouseMove);

		}

	},

	

	/*

		function: serialize

			serialize the splitpane in a form suitable to be used in an HTTP request.

		

		serialized values:

			div1 - the id of div1

			div1_left - the left edge of div1 expressed as a percentage of the parent width

			div1_width - the width of div1 expressed as a percentage of the parent width

			div2 - the id of div2

			div1_left - the left edge of div2 expressed as a percentage of the parent width

			div1_width - the width of div2 expressed as a percentage of the parent width

	*/

	serialize: function() {

		return "div1=" + this.div1.id

		+ "&div1_left=" + this.getXPercent(this.div1)

		+ "&div1_width=" + this.getWidthPercent(this.div1)

		+ "&div2=" + this.div2.id

		+ "&div2_left=" + this.getXPercent(this.div2)

		+ "&div2_width=" + this.getWidthPercent(this.div2);

	},

	

	/*

		function: dispose

			unhook from events

	*/

	dispose: function() {

		Event.stopObserving(this.divider, "mousedown", this.eventMouseDown);

		Event.stopObserving(document, "mouseup", this.eventMouseUp);

		Event.stopObserving(this.divider, "mousemove", this.eventChangeCursor);

		Event.stopObserving(document, "mousemove", this.eventMouseMove);

	},

	

	cursor: function(event) {

		this.divider.style.cursor = "e-resize";		

	},



	startDrag: function(event) {

	    if(Event.isLeftClick(event)) {

			this.active = true;

			var offsets = Position.cumulativeOffset(this.divider); 

			

	        this.start_pointer  = [Event.pointerX(event), Event.pointerY(event)];

			this.inset = this.start_pointer[0] - offsets[0];

			this.containerWidth = this.getWidth(this.container);

			this.start_div1_width = this.getWidth(this.div1);

			this.start_div2_left = this.getX(this.div2);

			this.start_div2_width = this.getWidth(this.div2);

			this.start_divider_x = this.getX(this.divider);

			

			Event.stop(event);

			

			this.options.onStart(this, event);

		}

	},



	endDrag: function(event) {

		if (this.active) {

			this.active = false;

			Event.stop(event);

			this.setDividerX();

			this.setDividerHeight();

			this.options.onEnd(this, event);

		}

	},



	update: function(event) {

		if (this.active) {

	        var pointer  = [Event.pointerX(event), Event.pointerY(event)];

			var delta = pointer[0] - this.start_pointer[0];

			var delta_percent = delta * 100.0 / this.containerWidth;

			

			// Calculate new div1 width

			var new_div1_width = this.start_div1_width + delta;

			

			// Limit width of div1

			if (new_div1_width < 0.0) {

				new_div1_width = 0.0;

				delta = -this.start_div1_width;

			}

						

			// Calculate new div2 width (in %)

			var new_div2_width = this.start_div2_width - delta;

			

			// Limit width of div2

			if (new_div2_width < 0.0) {

				new_div2_width = 0.0;

				delta = this.start_div2_width;

				new_div1_width = this.start_div1_width + delta;

			}



			// resize/position the divs

			this.div1.style.width = (new_div1_width * 100.0 / this.containerWidth) + "%";			

			this.div2.style.left  = ((this.start_div2_left + delta) * 100.0 / this.containerWidth) + "%";

			this.div2.style.width = (new_div2_width * 100.0 / this.containerWidth) + "%";

			

			// Set absolute position of divider - fix it up to be a % in endDrag().

			this.divider.style.left = (this.start_divider_x + delta) + "px";

			

			Event.stop(event);

			

			this.options.onDrag(this, event);

		}

	},



	setDividerX: function() {

		// Place the center of 'divider' half way between div1 and div2

		var div1_right = this.getX(this.div1) + this.getWidth(this.div1);

		var l = (((this.getX(this.div2)- div1_right - SplitPane.handleWidth)/2 + div1_right) * 100.0 / this.containerWidth) + "%";

		this.divider.style.left = l;

	},



	setDividerHeight: function() {

		// Set the divider height to the greater of the heights of the two divs

		var h = Math.max(this.getHeight(this.div1), this.getHeight(this.div2));

		this.divider.style.height = h + "px";

	},



	getX: function(el) {

    	return el.x ? el.x : el.offsetLeft;

	},

  

	getXPercent: function(el) {

		var x = "0";

		x = Element.getStyle(el,"left");

		if (x) {

			x = x.replace("%","");	//moz

		}

		

		return x ? parseFloat(x) : 0.0;

	},

  

	getWidthPercent: function(el) {

		var w = "0";

		w = Element.getStyle(el,"width");

		if (w) {

			w = w.replace("%","");	//moz

		}

		

		return w ? parseFloat(w) : 0.0;

	},

  

	getWidth: function(el) {

    	return el.offsetWidth;

	},

  

	getHeight: function(el) {

		if (el.currentStyle){

			return el.offsetHeight;									//ie

		} else {

			return Element.getStyle(el,"height").replace("px","");	//moz

		}

	}

}



SplitPane.setAll = function () {

	for(i=0; i<SplitPane.cache.length; i++){

		SplitPane.cache[i].set();

	}

}



Event.observe(window, "load", SplitPane.setAll);


