/**
 * Royalfish Dropdown class.
 * TODO:		option to to the positioning of the box relative to the viewport or the document.
 * 2007-05-13	Added method calculateBoxPosition, so the calculation of the position of the box can be overruled in a subclass.
 * 2007-05-08	Partial debug system created;
 				Fixed bug which caused strange timedclose behaviour in IE.
 * 2007-04-16:	Fixed bug which prevented the for loops from working properly.
 *				Fixed the viewport detection to work also in Firefox 2.0 with the scrollbar either on and off
 * 2007-01-21:	Added option to use collections of items. So the close others before opening option only closes the items within the same collection.
 * 2007-01-18:	added royalfish centered layout support.
 *				added option to close all other dropdowns before showing the current
 *				added automatic initialisation
 *				added option to display the dropdown box on multiple places
 * 2007-01-17:	first initial version
 */
	
/**
 * The main class definition
 */
	function RFDropDown(in_name, in_heading, in_box, in_collection) {
		this.name = in_name;
		this.heading = in_heading;
		this.heading._super = this;
		this.box = in_box;
		this.box._super = this;
		this.collection = in_collection;
		
		if(in_collection != null) {
			this.collection.add(this);
		}
		else {
			RFDropDown.add(this);
		}

		/**
		 * Variables needed for system use.
		 */
			this.toggle_open = false;
			this.drag_active = false;
			this.drag_old_onmousemove = null;
			this.drag_old_onmouseup = null;
			this.drag_old_ondrag = null;
			this.drag_old_onselectstart = null;
			this.drag_x_diff = 0;
			this.drag_y_diff = 0;
			this.active_timer = null;
			
		/**
		 * Initialize this dropdown combination
		 */
			this.initialize();
	}
	
	RFDropDown.prototype.timeout = 500;							//The delay of hiding a menu
	RFDropDown.prototype.use_layout_fix = true;					//If true: use the Royalfish centred layout hack
	RFDropDown.prototype.close_others_before_opening = true;	//If true: close all other dropdowns before opening a new one. If false: leave the normal timedClose functions running
	RFDropDown.prototype.box_position = 2;						//Relative to heading: 0 = north; 1 = north east; 2 = east; 3 = south east; 4 = south; 5 = south west; 6 = west; 7 = north west
																//Fixed, relative to document: 8
																//Fixed, relative to viewport: 9
	RFDropDown.prototype.pos_x = 0;								//X-position. The use depends on the value of box_position. 
																//	If relative to heading --> append this value to the automatically calculated one
																//	If fixed, relative to document or viewport --> the position
	RFDropDown.prototype.pos_y = 0;								//Same as pos_x but in Y direction
	RFDropDown.prototype.use_toggle = true;						//If true: the onclick event on the heading will let the box stay open.
	RFDropDown.prototype.use_drag = true;						//If true: will make the box draggable.	
	RFDropDown.prototype.use_hover = true;						//If true: will open the box when the mouse hovers the heading element.	
	RFDropDown.prototype.debug = false;
	RFDropDown.countdbg = 0;
	
	RFDropDown.prototype.initialize = function() {
		if(this.debug) { this.debugMessage('function initialize called') }
		
		if(this.use_hover) {
			this.heading.onmouseover = function() { this._super.open(); };
			this.heading.onmouseout = function() { this._super.timedClose(); };
			this.box.onmouseover = function() { this._super.killTimer(); };
			this.box.onmouseout = function() { this._super.timedClose(); };
		}
		
		if(this.use_toggle) {
			this.heading.onclick = function() { this._super.toggle(); };
		}
		if(this.use_drag) {
			this.box.onmousedown = function(e) { this._super.startDrag(e); };
		}
	}
	
	RFDropDown.prototype.open = function() {
		if(this.debug) { this.debugMessage('function open called') }
		
		if(this.toggle_open) {
			return;
		}
		
		this.killTimer();
		
		if(this.close_others_before_opening && this.collection != null) {
			this.collection.closeAll();
		}

		this.box.style.visibility = 'hidden'; 
		this.box.style.display = 'block';
		
		var boxposition = this.calculateBoxPosition();
		
		/**
		 * Display the box on the right position
		 */
			this.box.style.top = boxposition.top + 'px';
			this.box.style.left = boxposition.left + 'px';
			this.box.style.visibility = 'visible';
	}
	
	RFDropDown.prototype.close = function() {
		if(this.debug) { this.debugMessage('function close called') }
		
		this.killTimer();
		this.box.style.display = 'none';
		this.toggle_open = false;
	}
	
	RFDropDown.prototype.toggle = function() {
		if(this.debug) { this.debugMessage('function toggle called') }
		
		this.killTimer();
		if(this.toggle_open) {
			this.close();
		}
		else {
			this.open();
			this.toggle_open = true;
		}
	}
	
	RFDropDown.prototype.timedClose = function() {
		if(this.debug) { this.debugMessage('function timedClose called') }
		
		this.killTimer(); //solves a IE bug where multiple timedClose calls are generated while not necessary.
		if(this.toggle_open == false) {
			this.active_timer = setTimeout(this.name+'.close()', this.timeout);
		}
	}
	
	RFDropDown.prototype.killTimer = function() {
		if(this.debug) { this.debugMessage('function killTimer called') }
		
		if(this.active_timer != null) {
			clearTimeout(this.active_timer);
			this.active_timer = null;
		}
	}
	
	RFDropDown.prototype.startDrag = function(e) {
		if(this.debug) { this.debugMessage('function startDrag called') }
		
		this.drag_active = true;
		this.drag_old_onmousemove = document.onmousemove;
		this.drag_old_onmouseup = document.onmouseup;
		this.drag_old_ondrag = document.ondrag;
		this.drag_old_onselectstart = document.onselectstart;
		this.drag_x_diff = this.findXPos(this.box) - this.findMouseXPos(e);
		this.drag_y_diff = this.findYPos(this.box) - this.findMouseYPos(e);
		document.onselectstart = function() { return false; };
		document.ondrag = function() { return false; };
		addClassName(document.body, 'noSelectFF');
		
		document._super = this;
		document.onmousemove = function(e) { this._super.moveDrag(e); };
		document.onmouseup = function() { this._super.upDrag(); };
		
		return false;
	}
	
	RFDropDown.prototype.moveDrag = function(e) {
		if(this.debug) { this.debugMessage('function moveDrag called') }
		
		this.toggle_open = true;
		this.box.style.top = this.findMouseYPos(e) + this.drag_y_diff + 'px';
		this.box.style.left = this.findMouseXPos(e) + this.drag_x_diff - this.layoutFix() + 'px';
		
		return false;
	}
	
	RFDropDown.prototype.upDrag = function(e) {
		if(this.debug) { this.debugMessage('function upDrag called') }
		
		this.drag_active = false;
		document.onmousemove = this.drag_old_onmousemove;
		document.onmouseup = this.drag_old_onmouseup;
		document.ondrag = this.drag_old_ondrag;
		document.onselectstart = this.drag_old_onselectstart;
		removeClassName(document.body, 'noSelectFF');
	}
	
	RFDropDown.prototype.calculateBoxPosition = function() {
		var pos_top = this.pos_y;
		var pos_left = this.pos_x;
		
		if(this.box_position <= 7) { //If positioned relative to the heading
			pos_top += this.findYPos(this.heading);
			pos_left += this.findXPos(this.heading) - this.layoutFix();
		}
		if(this.box_position == 0) { //north side
			pos_top -= this.box.offsetHeight;
		}
		else if(this.box_position == 1) { //north east side
			pos_top -= this.box.offsetHeight;
			pos_left += this.heading.offsetWidth;
		}
		else if(this.box_position == 2) { //east side
			pos_left += this.heading.offsetWidth;
		}
		else if(this.box_position == 3) { //south east side
			pos_top += this.heading.offsetHeight;
			pos_left += this.heading.offsetWidth;
		}
		else if(this.box_position == 4) { //south side
			pos_top += this.heading.offsetHeight;
		}
		else if(this.box_position == 5) { //south west side
			pos_top += this.heading.offsetHeight;
			pos_left -= this.box.offsetWidth;
		}
		else if(this.box_position == 6) { //west side
			pos_left -= this.box.offsetWidth;
		}
		else if(this.box_position == 7) { //north west side
			pos_top -= this.box.offsetHeight;
			pos_left -= this.box.offsetWidth;
		}
		else if(this.box_position == 8) { //Relative to the document
			//nothing to do :D
		}
		else if(this.box_position == 9) { //Relative to the viewport
			pos_top += this.scrollPositionY();
			pos_left += this.scrollPositionX();
		}
		else {
			alert('Invalid box-position set.');
		}
		return {left: pos_left, top: pos_top};
	}
	
	RFDropDown.prototype.layoutFix = function() {
		if(this.debug) { this.debugMessage('function layoutFix called') }
		
		if(this.use_layout_fix && document.getElementById('main')) { 
			layout_width = document.getElementById('main').offsetWidth;
			viewport_width = this.viewportWidth();
			if(viewport_width > layout_width) {
				return (viewport_width - layout_width) / 2;
			}
			else {
				return 0;
			}
		}
		else {
			return 0;
		}
	}
	
	RFDropDown.prototype.findXPos = function(obj) {
		if(this.debug) { this.debugMessage('function findXPos called') }
		
		curleft = 0;
		if (obj.offsetParent) {
			curleft = obj.offsetLeft;
			while (obj = obj.offsetParent) {
				curleft += obj.offsetLeft;
			}
		}
		return curleft;
	}
	
	RFDropDown.prototype.findYPos = function(obj) {
		if(this.debug) { this.debugMessage('function findYPos called') }
		
		curtop = 0;
		if (obj.offsetParent) {
			curtop = obj.offsetTop;
			while (obj = obj.offsetParent) {
				curtop += obj.offsetTop;
			}
		}
		return curtop;
	}
	
	RFDropDown.prototype.findMouseXPos = function(e) {
		if(this.debug) { this.debugMessage('function FindMouseXPos called') }
		
		if(!e) e = window.event;
		var mX = (e.pageX) ? e.pageX : e.clientX;
		if(document.body && document.all) {
			if(document.documentElement && document.documentElement.scrollTop) {
				mX += document.documentElement.scrollLeft;
			}
			else {
				mX += document.body.scrollLeft;
			}
		}
		return mX;
	}
	
	RFDropDown.prototype.findMouseYPos = function(e) {
		if(this.debug) { this.debugMessage('function findMouseYPos called') }
		
		if(!e) e = window.event;
		var mY = (e.pageY) ? e.pageY : e.clientY;
		if(document.body && document.all) {
			if(document.documentElement && document.documentElement.scrollTop) {
				mY += document.documentElement.scrollTop;
			}
			else {
				mY += document.body.scrollTop;
			}
		}
		return mY;
	}
	
	RFDropDown.prototype.scrollPositionX = function() {
		if(this.debug) { this.debugMessage('function scrollPositionX called') }
		
		if (self.pageXOffset) { // all except Explorer
			return self.pageXOffset;
		}
		else if (document.documentElement && document.documentElement.scrollLeft) { // Explorer 6 Strict
			return document.documentElement.scrollLeft;
		}
		else if (document.body) { // all other Explorers
			return document.body.scrollLeft;
		}
		return false;
	}
	
	RFDropDown.prototype.scrollPositionY = function() {
		if(this.debug) { this.debugMessage('function scrollPositionY called') }
		
		if (self.pageYOffset) { // all except Explorer
			return self.pageYOffset;
		}
		else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict
			return document.documentElement.scrollTop;
		}
		else if (document.body) { // all other Explorers
			return document.body.scrollTop;
		}
		return false;
	}
	
	RFDropDown.prototype.viewportWidth = function() {
		if(this.debug) { this.debugMessage('function viewportWidth called') }
		
		if(typeof document.width != 'undefined') {
			viewport_width = document.width;
		}
		else if(typeof window.innerWidth != 'undefined') {
			viewport_width = window.innerWidth;
		}
		else if(typeof document.documentElement != 'undefined' && typeof document.documentElement.clientWidth != 'undefined' && document.documentElement.clientWidth != 0) {
			viewport_width = document.documentElement.clientWidth;
		}
		else {
			viewport_width = document.getElementsByTagName('body')[0].clientWidth;
		}
		return viewport_width;
	}
	
	RFDropDown.prototype.viewportHeight = function() {
		if(this.debug) { this.debugMessage('function viewportHeight called') }
		
		if(typeof document.height != 'undefined') {
			viewport_heigth = document.height;
		}
		else if(typeof window.innerHeight != 'undefined') {
			viewport_height = window.innerHeight;
		}
		else if(typeof document.documentElement != 'undefined' && typeof document.documentElement.clientHeight != 'undefined' && document.documentElement.clientHeight != 0) {
			viewport_height = document.documentElement.clientHeight;
		}
		else {
			viewport_height = document.getElementsByTagName('body')[0].clientHeight;
		}
		return viewport_height;
	}
	
	RFDropDown.prototype.debugMessage = function(message) {
		if(this.debug) {
			RFDropDown.countdbg += 1;
			var container = document.getElementById('rfddDebug');
			var oldcontent = container.innerHTML;
			container.innerHTML = RFDropDown.countdbg + ' ' +this.name + ' ' + message + ' <br />' + oldcontent;
		}
	}
	
/**
 * A few functions which make dropdown combinations usable
 * This is only for boxes, which don't belong to a collection.
 */
	RFDropDown.collection = new Array();
	
	RFDropDown.add = function(item) {
		RFDropDown.collection.push(item);
	}
	
	RFDropDown.closeAll = function() {
		for(var i = 0; i < RFDropDown.collection.length; i++) {
			if(RFDropDown.collection[i].toggle_open == false) {
				RFDropDown.collection[i].close();
			}
		}
	}
	
	RFDropDown.getItem = function(in_name) {
		for(var i = 0; i < RFDropDown.collection.length; i++) {
			if(RFDropDown.collection[i].name == in_name) {
				return RFDropDown.collection[i];
			}
		}
		return false;
	}

/**
 * A new RFDD collection class
 */
	function RFDDCollection(in_name) {
		this.collection = new Array();
		this.name = in_name;
	}
	
	RFDDCollection.prototype.add = function(item) {
		this.collection.push(item);
	}
	
	RFDDCollection.prototype.openAll = function() {
		for(var i = 0; i < this.collection.length; i++) {
			this.collection[i].open();
		}
	}
	
	RFDDCollection.prototype.closeAll = function(forced) {
		for(var i = 0; i < this.collection.length; i++) {
			if(this.collection[i].toggle_open == false || forced == true) {
				this.collection[i].close();
			}
		}
	}
	
	RFDDCollection.prototype.getItem = function(in_name) {
		for(var i = 0; i < this.collection.length; i++) {
			if(this.collection[i].name == in_name) {
				return this.collection[i];
			}
		}
		return false;
	}

/**
 * The inheritance of the objects.
 */
	Object.prototype.inherit = function(superClass) {
		var tmpClass = function() {};
		tmpClass.prototype = superClass.prototype;
		this.prototype = new tmpClass;
		var className = superClass.getClassName();
		this.prototype[className] = superClass;
	}
	
	Object.prototype.getClassName = function() {
		matches = this.toString().match(/function\s*(\w+)/);
		return (matches == null) ? '' : matches[1];
	}
	
/**
 * Providing a way to easily add and remove class names from html elements.
 */
	String.prototype.trim = function() {
	    return this.replace( /^\s+|\s+$/, "" );
	}
	
	function addClassName(elem, className) {
		removeClassName (elem, className);
		elem.className = (elem.className + " " + className).trim();
	}
		
	function removeClassName(elem, className) {
		elem.className = elem.className.replace(className, "").trim();
	}

