/*	
	floatbar.js 
	Release 1.0
	by George Coulouris
	
	The code in this file implements the floating transparent 'sidebar' used at maps.camdecyclists.org.uk 
	Documentation is available at:
		http://maps.camdencyclists.org.uk/documentation/FloatBarRef.html

	This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version, see <http://www.gnu.org/licenses/>.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
*/

/* 	We use an 'options' object to pass all the optional parameters: 
	{offset: CCCMenuPos, width: CCCMenuWidth, bottomMargin: CCCMenuBottomMargin, opacity: CCCMenuOpacity, stylesheet: 'floatBar', background: 'white', border: '1px solid black', hideable: true, hideButtonOffset: new GSize(15,0)}; 
*/

function FloatBarControl(id, options) {
	if(options == undefined) options = new Object();
	this.offset = options.offset?options.offset:new GSize(7, 32);	// offset from top right
	this.width = options.width?options.width:300;
	this.opacity = options.opacity?options.opacity:0.65;
	this.IEopacity = this.opacity*100;	// i.e. 65
	this.height = null;	// computed in initialize();
	this.container = null;
	this.hideButtonContainer = null;
	this.barVisible = true;
	var
		bottomMargin = 	options.bottomMargin?options.bottomMargin:40,
		bottomLimit =	options.bottomLimit?options.bottomLimit:null,
		stylesheet = 	options.stylesheet?options.stylesheet:id,
		border =		options.border?options.border:'1px solid black',
		background = 	options.background?options.background:'white',
		padding = 		options.padding?options.padding:5,
		hideable = 		options.hideable?options.hideable:true,
		hideButtonOffset = options.hideButtonOffset?options.hideButtonOffset: new GSize(15,0), 
				// default width is the width of scrollbar on MacOS
		visibleOffset = this.offset.width + 'px',
		hiddenOffset, // instantiated in this.initialize()
		me = this;
	
	this.initialize = function(map) {
		this.maxHeight = minimum(bottomLimit, map.getContainer().offsetHeight - bottomMargin);
		this.scrollWheelZoom = map.scrollWheelZoomEnabled();
		this.container = document.createElement(stylesheet);
		this.container.id = id;
		var mapContainer = map.getContainer();

		this.container.style.position = 'absolute';
		this.container.style.height = 'auto';
		this.container.style.overflow = 'auto';
		this.container.style.padding = padding + 'px';
		this.container.style.width = this.width + 'px';
		this.container.style.border = border;
		this.container.style.background = background;
		this.container.style.opacity = this.opacity;
		this.container.style.filter = 'alpha(opacity=' + this.IEopacity + ')';	// for IE
				
		map.getContainer().appendChild(this.container);
		this.container.innerHTML = '<div id =\'floatbar-wrapper\'></div>';
		

		if(hideable) {
			this.hideButton = new floatBarHideButton(this);
			this.hideButtonContainer = this.hideButton.initialize(map);
		}

		function hasParent(putativeParent, child) { 
			// return true if putativeParent is an ancestor of child
			var parent = child.parentNode;
			if(!parent) return false;
			if (putativeParent == parent) return true;
			else return hasParent(putativeParent, parent);	// recursive call
		}
		
		var IEevent = window.event;			// only defined for IE
				
		this.container.onmouseover = function(e) {
			var relTarget = IEevent ? window.event.fromElement : e.relatedTarget; // special-case for IE
			// relTarget is the element away from which the mouse has moved
			if(relTarget) {
				if(hasParent(mapContainer, relTarget) && !hasParent(me.container, relTarget)) {
					me.container.style.filter = 'alpha(opacity=100)'; 	// for IE only
					me.container.style.opacity = 1; 					// make the container opaque
					if(me.scrollWheelZoom) map.disableScrollWheelZoom(); 
				}
			}
		};
		
		this.container.onmouseout = function(e) {
			// relTarget is the element into which the mouse has moved
			var relTarget = IEevent ? window.event.toElement : e.relatedTarget; // special-case for IE
			if(relTarget) {
				if( relTarget.name != '*' && 	relTarget.parentNode.name != '*' &&	
														// special case because hidebutton has to a be child of the map.
					!hasParent(me.container, relTarget) && 		// and it isn't a child of our container
					relTarget.nodeName != me.container.nodeName) {	// and it isn't part of our container 
																// (e.g. a scrollbar)
					me.container.style.filter = 'alpha(opacity=' + me.IEopacity + ')'; 
					me.container.style.opacity = me.opacity; 
					if(me.scrollWheelZoom) map.enableScrollWheelZoom(); 
				}
			}
		};
		
		GEvent.addListener(map, "resize", function () { // recompute height on map resize
			me.maxHeight = minimum(bottomLimit, map.getContainer().offsetHeight - bottomMargin);
			me.changed();
		});
		
		hiddenOffset = (hideButtonOffset.width - this.container.offsetWidth + 3) + 'px';		

		return this.container;
	}
	
	this.load = function(theHTML) {
		this.container.firstChild.innerHTML = theHTML;
		this.changed();
	}
	
	this.append = function(theHTML) {
		this.container.firstChild.innerHTML += theHTML;
		this.changed();
	}
	
	this.getContainer = function() {
		return this.container.firstChild;
	}
	
	this.getBottomLeft = function() {
		return new GSize(this.offset.width + this.container.offsetWidth,
								this.offset.height + this.container.offsetHeight);
	}
	
	this.changed = function () {
		if (this.container.firstChild.scrollHeight > this.maxHeight) 
			this.container.style.height = this.maxHeight + 'px';
		else 
			this.container.style.height = 'auto';
	}
	
	this.getDefaultPosition = function() {
		return new GControlPosition(G_ANCHOR_TOP_RIGHT, this.offset);
	}
	
	this.show = function () {
		this.container.style.right = visibleOffset;
		this.hideButton.setVisibilityIcon(true);
		this.barVisible = true;
	}

	this.hide = function () {
		this.container.style.right = hiddenOffset;
		this.hideButton.setVisibilityIcon(false);
		this.barVisible = false;
	}
	
	function minimum(x, y) {
		return (x == null) ? y : ((x > y) ? y : x);
	}
	
	/* hide button */
	function floatBarHideButton(theBar) {
		this.container = null;
		var me = this;
	 
		this.initialize = function(map) {
			this.container = document.createElement("hidebutton");
			this.container.style.position = 'absolute';
			this.container.style.background = theBar.container.style.background;
			this.container.style.border = border;
			this.container.style.cursor = 'pointer';
			this.container.style.top = theBar.offset.height + hideButtonOffset.height + 'px';
			this.container.style.right = theBar.offset.width + hideButtonOffset.width + 'px';
			this.container.style.width = '18px';
			this.container.style.height = '22px';
			this.container.style.verticalAlign = 'middle';
			this.container.style.textAlign = 'center';
			this.container.style.fontSize = '18px';
			this.container.name = '*';

			var 	
				visibleContent = '<div unselectable=\'on\'><img src="' + MarkerIcons_path + 'right.png" ' + 
					' alt=">" title="hide route menu"/></div>',
				hiddenContent = '<div unselectable=\'on\'><img src="' + MarkerIcons_path + 'left.png" ' + 
					' alt="<" title="show route menu"/></div>';

			var me = this;
			
			this.container.innerHTML = visibleContent;
			
			this.setVisibilityIcon = function(visible) {
					this.container.innerHTML = visible ? visibleContent : hiddenContent;
			}
		
			GEvent.addDomListener(this.container, "click", clickHandler);
			
			function clickHandler() {
				if(theBar.barVisible) {
					theBar.hide();
				} else {
					theBar.show();
				}
			}
			map.getContainer().appendChild(this.container);
			return this.container;
		}
	}
}
FloatBarControl.prototype = new GControl();	
