/**
 * Written by Eden Duthie - eduthie
 */
var MISSING = 'missing';

var isAboutDisplay = false;

/**
 * Function to load a javascript file dynamically
 * 
 * @param url -
 *            the relative or absolute path of the javascript file to load
 */
function loadScript(url) {
	var e = document.createElement("script");
	e.src = url;
	e.type = "text/javascript";
	document.getElementsByTagName("head")[0].appendChild(e);
}

/**
 * Create the Widget when the page is ready
 * 
 * @param parameters
 */
function Widget(params) {
	$(document).ready(function() {
		var widget = new BOM_AWRIS_Widget(params);
	});
}

/**
 * The Water Widget! Both the Controller and View combined together.
 * 
 * @param parameters -
 *            parameters to initialise Parameters object, see parameters.js
 */
function BOM_AWRIS_Widget(params) {
	// constructor start
	this.parameters = new BOM_AWRIS_Parameters(params);
	// the initial and current display parameters
	this.regionId = this.parameters.get('defaultRegionId');
	this.widgetStyle = this.parameters.get('widgetStyle');
	this.map_type = this.parameters.get('defaultBoundary');
	this.mapTabSelected = this.parameters.get('mapTabSelected');

	this.model = new BOM_AWRIS_Model(this.parameters.get('baseURL'),
			this.parameters.get('serverURL'),
			this.parameters.get('widgetStyle'), 
			this.parameters.get('customerKey'));
	this.model.localDomain = this.parameters.get('localDomain');
	this.model.load(function(widget) {
		// check if the model holds an error code
			var errorCode = widget.model.getErrorCode();
			if (typeof errorCode != 'undefined') {
				// an error exists, construct and render the error display
				widget.constructErrorPage();
				widget.renderErrorPage();
			} else {
				// construct and render the widget
				widget.construct();
				widget.render();
				widget.showOrHide(widget.parameters);
			}
		}, this);
	// constructor end

	// the main divs of the widget
	this.elements = new Array();
	this.elements['header'] = MISSING;
	this.elements['goToLink'] = MISSING;
	this.elements['title'] = MISSING;
	this.elements['regionName'] = MISSING;
	this.elements['regionMeasurement'] = MISSING;
	this.elements['clearWidget'] = MISSING;
	this.elements['controls'] = MISSING;
	this.elements['radio'] = MISSING;
	this.elements['state'] = MISSING;
	this.elements['city'] = MISSING;
	this.elements['drainage'] = MISSING;
	this.elements['dropDown'] = MISSING;
	this.elements['select'] = MISSING;
	this.elements['storages'] = MISSING;
	this.elements['leftPadder'] = MISSING;
	this.elements['map'] = MISSING;
	this.elements['backToNational'] = MISSING;
	this.elements['leftFooter'] = MISSING;
	this.elements['rightFooter'] = MISSING;
	this.elements['tabs'] = MISSING;
	this.elements['mapLabel'] = MISSING;
	this.elements['chartLabel'] = MISSING;
	this.elements['clearWidget'] = MISSING;
	this.elements['errorDisplay'] = MISSING;
	this.elements['about-close'] = MISSING;
	this.elements['about-panel-shadow'] = MISSING;
	this.elements['about'] = MISSING;
	this.elements['lastRecorded'] = MISSING;
	this.elements['missingContentHtml'] = MISSING;
	this.elements['aboutLink'] = MISSING;

	// element names that can be toggled
	this.toggleElements = [ 'goToLink', 'title', 'regionName',
			'regionMeasurement', 'dropDown' ];

	this.styleAttribute = 'id';
	this.classAttribute = 'class';
	this.className = 'waterWidget'; // top level css name
	this.styleElement = '<div></div>'; // type of element

	// the css div id names that are set for each element
	this.styleNames = new Array();
	for (elementName in this.elements) {
		this.styleNames[elementName] = this.className + "-" + elementName;
	}
	this.styleNames['regionName'] = this.className + "-" + 'location';
	this.styleNames['regionMeasurement'] = this.className + "-" + 'totalWater';
	this.styleNames['clearWidget'] = this.className + "-" + 'clearWidget';
	this.styleNames['dropDown'] = this.className + "-" + 'form';
	this.styleNames['select'] = this.className + "-" + 'controlsArea';
	this.styleNames['map'] = this.className + "-" + 'display';
	this.styleNames['state'] = this.className + "-" + 'states';
	this.styleNames['city'] = this.className + "-" + 'cities';
	this.styleNames['mapLabel'] = this.className + "-" + 'map';
	this.styleNames['chartLabel'] = this.className + "-" + 'chart';
	this.styleNames['clearWidget'] = 'clearWidget';

	this.styleNames['state-selected'] = this.className + "-"
			+ 'states-selected';
	this.styleNames['city-selected'] = this.className + "-" + 'cities-selected';
	this.styleNames['drainage-selected'] = this.className + "-"
			+ 'drainage-selected';
	this.styleNames['about-close'] = this.className + "-" + 'about-close';
	this.styleNames['about-panel-shadow'] = this.className + "-"
			+ 'about-panel-shadow';
	this.styleNames['lastRecorded'] = this.className + "-" + 'lastRecorded';

	this.classNames = new Array();
	for (elementName in this.elements) {
		this.classNames[elementName] = this.className + "-" + elementName;
	}
	this.classNames['header'] = this.className + "-header";
	this.classNames['title'] = this.className + "-" + 'heading';
	this.classNames['goToLink'] = this.className + "-" + 'sizeBigger';
	this.classNames['regionName'] = this.className + "-" + 'locationDetails';
	this.classNames['regionMeasurement'] = this.className + "-"
			+ 'locationDetails';
	this.classNames['clearWidget'] = 'clearWidget';
	this.classNames['state'] = this.className + "-" + 'selectorImage';
	this.classNames['city'] = this.className + "-" + 'selectorImage';
	this.classNames['drainage'] = this.className + "-" + 'selectorImage';
	this.classNames['dropDown'] = this.className + "-" + 'form';
	this.classNames['select'] = this.className + "-" + 'select';
	this.classNames['map'] = this.className + "-" + 'display';
	this.classNames['tabOn'] = this.className + "-" + 'tabOn';
	this.classNames['tabOff'] = this.className + "-" + 'tabOff';
	this.classNames['tab'] = this.className + "-" + 'tab';
	this.classNames['clearWidget'] = 'clearWidget';
	this.classNames['errorLabel'] = this.className + "-" + 'errorlabel';
	this.classNames['about-close'] = this.className + "-" + 'about-close';
	this.classNames['about-panel-shadow'] = this.className + "-"
			+ 'about-panel-shadow';
	this.classNames['lastRecorded'] = this.className + "-" + 'lastRecorded';

	// the css class names for the map labesl
	this.mapLabelClasses = new Array();
	this.mapLabelClasses[this.parameters.get('regionTypeState')] = this.className
			+ '-maplabel-' + this.parameters.get('regionTypeState');
	this.mapLabelClasses[this.parameters.get('regionTypeDD')] = this.className
			+ '-maplabel-' + this.parameters.get('regionTypeDD');
	this.mapLabelClasses[this.parameters.get('regionTypeCC')] = this.className
			+ '-maplabel-' + this.parameters.get('regionTypeCC');

	// label for missing label measurement
	this.MISSING_LABEL_VOLUME = '_';

	this.setRegionId = function(id) {
		this.regionId = id;
	};

	this.setMapType = function(map_type) {
		this.map_type = map_type;
	};

	/**
	 * Render functions assume the containing elements divs are already on the
	 * page. They then retrieve the data from the model and draw it on the page.
	 */
	this.render = function() {
		this.renderGoToLink();
		this.renderRegionName();
		this.renderRegionMeasurement();
		this.renderDropDown();
		this.renderMapChartImage();
		this.renderMapLabels();
		this.renderBackToNational();
		this.renderTabs();
	};

	this.renderAbout = function() {
		var aboutText = this.parameters.get('aboutText');
		if (typeof aboutText == 'undefined') {
			aboutText = 'About';
		}

		// creating divs
		var abouts = this.drawDiv('about', this.classNames['about'],
				this.styleNames['about'], this.elements['map']);
		var aboutClose = this.drawDiv('about-close',
				this.classNames['about-close'], this.styleNames['about-close'],
				abouts);
		var aboutImage = this.drawDiv('about-panel-shadow',
				this.classNames['about-panel-shadow'],
				this.styleNames['about-panel-shadow'], abouts);
		

		// positioning
		this.elements['about'].css('position', 'absolute');
		this.elements['about-close'].css('position', 'absolute');
		this.elements['about-panel-shadow'].css('position', 'absolute');
		
		// Adding close button binding
		this.elements['about-close'].bind("click", this, function(e) {
			e.data.hideAbout();
		});

		// Adding about content
		var aboutText = '<h2> About Water Storage </h2>';
		aboutText += '<p>This ';
		aboutText += '<a href="http://water.bom.gov.au/waterstorage/">water storage</a>';
		aboutText += ' information is produced by the ';
		aboutText += '<a href="http://www.bom.gov.au/water"> Bureau of Meteorology</a>';
		aboutText += ' and developed with the support of the ';
		aboutText += '<a href="http://www.nwc.gov.au"> National Water Commission</a>.</p>';
		aboutText += '<p> For further information please contact ';
		aboutText += '<a href="mailto:waterinfo@bom.gov.au?subject=Water Storage widget">waterinfo@bom.gov.au</a> </p>';
		this.elements['about-panel-shadow'].html(aboutText);

		this.hideAbout();
	};

	this.showAbout = function() {
		this.elements['about'].css('display', 'inline');
		isAboutDisplay = true;
	};

	this.hideAbout = function() {
		this.elements['about'].css('display', 'none');
		isAboutDisplay = false;
	};

	this.renderRegionName = function() {
		if (this.parameters.get('regionVisible') == false)
			return;
		var region = this.model.getRegionById(this.regionId);
		if (region == null)
			return; // TODO do we want to display an error here?
		this.elements['regionName'].html(region.long_name);
	};

	this.renderRegionMeasurement = function() {
		var measurement = this.model.getMeasurementById(this.regionId);

		var content = null;
		var measurementDate = null;

		if (measurement != null) {
			content = measurement.volume;
			content += " " + "(" + measurement.percentage;
			content += this.parameters.get('percentageFullText') + ")";

			// render the last recorded date for the region
			measurementDate = measurement.observation_date == null ? "_"
					: measurement.observation_date;

		} else {
			content = this.MISSING_LABEL_VOLUME + ""
					+ " ( "
					+ this.MISSING_LABEL_VOLUME + ""
					+ this.parameters.get('percentageFullText') + ")";

			measurementDate = this.MISSING_LABEL_VOLUME;
		}

		this.elements['regionMeasurement'].html(content);
		var lastRecHtml = "<p>" + this.parameters.get('lastRecordedText')
				+ ': ' + measurementDate + "</p>";
		this.elements['lastRecorded'].html(lastRecHtml);
		this.elements['lastRecorded'].append(this.elements['aboutLink']);
		this.elements['aboutLink'].bind("click", this, function(e) {
			e.data.showAbout();
			return false;
		});
	};

	this.renderGoToLink = function() {
		var goToText = this.parameters.get('goToLinkText');
		if (typeof goToText == 'undefined') {
			goToText = 'Change size';
		}

		var paramsText = this.parameters.get('goToLinkURL') + "#";
		var amp = "";
		if (this.regionId != 'UNDEFINED') {
			paramsText += "defaultRegionId=" + this.regionId;
			amp = "&";
		}
		if (this.map_type != 'UNDEFINED') {
			paramsText += amp + "defaultBoundary=" + this.map_type;
		}

		if (this.mapTabSelected != 'UNDEFINED') {
			paramsText += amp + "mapTabSelected=" + this.mapTabSelected;
		}
		var goToHTML = '<a href="' + paramsText + '" title="' + goToText
				+ '"></a>';
		this.elements['goToLink'].html(goToHTML);
	};

	function sortRegionFunction(region1, region2){
		return  (region1.long_name > region2.long_name ? 1 : -1);
	}
	
	this.renderDropDown = function() {

		var regions = this.model.getRegionsByType(this.map_type);
		if (regions == null || regions.length <= 0)
			return;
		
		// Sort regions
		regions.sort(sortRegionFunction);
		
		this.elements['dropDown'].empty();
		var dropDown = $('<select></select>');
		dropDown.attr('id', this.styleNames['select']);
		dropDown.attr('class', this.classNames['select']);
		dropDown.attr('name', this.styleNames['select']);

		// add the empty initial national option
		var option = $('<option></option>');
		option.attr('value', this.parameters.get('nationalRegionId'));
		option.html(this.parameters.get(this.map_type + 'SelectText'));
		dropDown.append(option);

		for ( var i = 0; i < regions.length; ++i) {
			option = $('<option></option>');
			option.attr('value', regions[i].region_id);
			option.html(regions[i].long_name);
			dropDown.append(option);
		}
		if (this.regionId != this.parameters.get('nationalRegionId')) {
			// if the current region is not national, we want to select it in
			// the drop down
			dropDown.val(this.regionId);
		}
		this.elements['dropDown'].append(dropDown);

		var button = $('<input name="Go" type="button" class="waterWidget-select" value="' + this.parameters
				.get('goButtonText') + '" />');
		button.bind("click", this, function(e) {
			if (isAboutDisplay == false) {
				e.data.changeRegion($(
						'#' + e.data.styleNames['dropDown'] + ' > select')
						.val());
				return false;
			}
		});
		this.elements['dropDown'].append(button);

	};

	/**
	 * Renders the main image. Displays a map if the map tab is selected.
	 * Displays a chart if the chart tab is selected.
	 */
	this.renderMapChartImage = function() {
		this.elements['map'].empty();
		this.renderAbout();
		var image = $('<img></img>');

		// check to see if we should render a map or a chart
		if (this.mapTabSelected == 'true') {
			var map;
			if (this.regionId == this.parameters.get('nationalRegionId')) {
				map = this.model.getMap(this.regionId, this.map_type);
			} else {
				map = this.model.getMap(this.regionId);
			}
			if (map == null)
				return; // TODO do we need to show a blank map?
			var url = this.parameters.getWithForwardSlash('baseURL') 
			+ this.parameters.getWithForwardSlash('mapBaseURL') + map.map;

			image.attr('src', url);
			image.attr('alt', 'Map');
			image.attr('width', map.image_width);
			image.attr('height', map.image_height);
		} else {

	    var chartName = this.regionId;	    
			var base = this.parameters.getWithForwardSlash('baseURL');
			
			// not canned if starts with 'http' or '/'
			var notCanned = base.indexOf('http')==0 || base.indexOf('/')==0;
			var canned = !notCanned;
			
			// if canned mode, change the colons and full stops in the identifier to be hyphens
			if (canned)
			{
				chartName = chartName.replace(/:/g,'-');
				chartName = chartName.replace(/\./g,'-');
				chartName += '-' + this.parameters.get('widgetStyle') + '.png';
			}	
			
			var url = base + this.parameters.getWithForwardSlash('chartBaseURL')
					+ this.parameters.get('widgetStyle') + '/' + chartName
					+ '?key=' + this.parameters.get('customerKey');

			image.attr('src', url);
			image.attr('alt', 'Chart');
		}

		// image.attr('border','0');
		// image.css('position','absolute');
		this.elements['map'].append(image);

		// a different way of doing it with a background image
		// var backgroundImage = "url("+url+")";
		// this.elements['map'].css('background-image',backgroundImage);
		// this.elements['map'].css('height',map.image_height);
		// this.elements['map'].css('width',map.image_width);
	};

	/**
	 * Renders the percentage full labels on the map
	 */
	this.renderMapLabels = function() {
		if (this.mapTabSelected == 'true') {
			var map = this.model.getMap(this.regionId, this.map_type);
			if (map == null)
				return;
			for ( var i = 0; i < map.labels.length; ++i) {
				this.renderLabel(map.labels[i]);
			}
		}
	};

	this.renderLabel = function(label) {

		var region = this.model.getRegionById(label.region_id);
		if (region == null)
			return;
		var measurement = this.model.getMeasurementById(label.region_id);
		var wrapperDiv = $('<div></div>');
		wrapperDiv.css('position', 'absolute');
		var labelDiv = $('<div></div>');
		labelDiv.attr('alt', region.long_name);
		labelDiv.attr('title', region.long_name);
		labelDiv.attr('class', this.mapLabelClasses[region.type]);
		if (measurement != null) {
			labelDiv.html(measurement.percentage);
		} else {
			labelDiv.html(this.MISSING_LABEL_VOLUME);
		}
		labelDiv.css('position', 'absolute');
		labelDiv.css('top', label.y + 'px');
		labelDiv.css('left', label.x + 'px');

		labelDiv.bind("click", this, function(e) {
			e.data.changeRegion(label.region_id);
			return false;
		});

		labelDiv.hover(function() {
			$(this).css('cursor', 'pointer');
			// $(this).fadeTo("fast",0.70);
			}, function() {
				// $(this).fadeTo("fast",1.00);
			});

		wrapperDiv.append(labelDiv);
		this.elements['map'].prepend(wrapperDiv);
	};

	/**
	 * Hides the back to national link if we are viewing the national view,
	 * shows it otherwise.
	 */
	this.renderBackToNational = function() {
		if (this.regionId == this.parameters.get('nationalRegionId')
				|| this.mapTabSelected != 'true') {
			this.elements['backToNational'].hide();
		} else {
			this.elements['backToNational'].show();
		}
	}

	/**
	 * 
	 */
	this.renderTabs = function() {
		if (this.mapTabSelected == 'true') {
			this.elements['mapLabel'].removeClass(this.classNames['tabOff']);
			this.elements['mapLabel'].addClass(this.classNames['tabOn']);
			this.elements['chartLabel'].addClass(this.classNames['tabOff']);
			this.elements['chartLabel'].removeClass(this.classNames['tabOn']);
		} else {
			this.elements['mapLabel'].removeClass(this.classNames['tabOn']);
			this.elements['mapLabel'].addClass(this.classNames['tabOff']);
			this.elements['chartLabel'].removeClass(this.classNames['tabOff']);
			this.elements['chartLabel'].addClass(this.classNames['tabOn']);
		}
	};

	/**
	 * Checks if an error exists in the model. If error exsits, checks the error
	 * code to display a message. If error code does not match then the error
	 * text from the model is displayed. If no error text exists a generic
	 * message is displayed.
	 * 
	 * @return
	 */
	this.renderErrorPage = function() {

		var errorCode = this.model.getErrorCode();

		var emailTo = '<br><a href="mailto:waterinfo@bom.gov.au?subject=widget error ' + errorCode + '">waterinfo@bom.gov.au</a>';

		var html = "<br>" + errorCode;

		if (errorCode == '403') {
			html += "<br>Unauthorised Access. <br>Please contact the Bureau of Meteorology at "
					+ emailTo;
		} else if (typeof this.model.getErrorText() != 'undefined') {
			html += "<br>" + this.model.getErrorText();
		} else {
			html += "<br>Unable to Load. <br>Please contact the Bureau of Meteorology at "
					+ emailTo;
		}

		this.elements['errorDisplay'].html(html);
	};

	/**
	 * The construct function creates and initialises the elements on the page,
	 * before they can be rendered.
	 */
	this.construct = function() {
		$('#' + this.parameters.get('location')).empty();
		$('#' + this.parameters.get('location')).attr(this.classAttribute,
				this.className);

		var widthValue = this.parameters.get('width');
		if (typeof widthValue != 'undefined') {
			$('#' + this.parameters.get('location')).css('width', widthValue);
		}

		var header = this.drawDiv('header', this.classNames['header']);
		this.drawDiv('title', this.classNames['title'],
				this.styleNames['title'], header);
		var goToLink = this.drawDiv('goToLink', this.classNames['goToLink'],
				this.styleNames['goToLink'], header);
		this.drawDiv('regionName', this.classNames['regionName'],
				this.styleNames['regionName'], header);
		this.drawDiv('regionMeasurement', this.classNames['regionMeasurement'],
				this.styleNames['regionMeasurement'], header);
		this.drawDiv('clearWidget', this.classNames['clearWidget'],
				this.styleNames['clearWidget'] + "-1", header);

		var missingContent = '<div id="waterWidget-lastRecorded"/>';
		var d = new Date();

		this.elements['lastRecorded'] = $(missingContent);
		this.elements['lastRecorded'].html("missingContent");
		header.append(this.elements['lastRecorded']);

		var aboutLink = $('<a href="#">' + this.parameters.get('aboutText') + '</a>');
		aboutLink.bind("click", this, function(e) {
			e.data.showAbout();
		});

		this.elements['aboutLink'] = aboutLink;

		this.elements['lastRecorded'].append(this.elements['aboutLink']);

		var controls = this.drawDiv('controls', this.classNames['controls']);
		var radio = this.drawDiv('radio', this.classNames['radio'],
				this.styleNames['radio'], controls);

		var cityImage = this.constructSelectorImage('city', radio);
		cityImage.attr('title', this.parameters.get('cityButtonAlt'));
		var stateImage = this.constructSelectorImage('state', radio);
		stateImage.attr('title', this.parameters.get('stateButtonAlt'));
		var drainageImage = this.constructSelectorImage('drainage', radio);
		drainageImage.attr('title', this.parameters.get('drainageButtonAlt'));
		// make sure the current map_type is selected
		$('#' + this.styleNames[this.map_type]).attr('id',
				this.styleNames[this.map_type + '-selected']);

		controls.append(radio);

		this.drawElement('dropDown', this.classNames['dropDown'],
				this.styleNames['dropDown'], controls, '<form></form>');

		var storages = this.drawDiv('storages', this.classNames['storages']);
		var leftPadder = this.drawDivNoId('leftPadder',
				this.classNames['leftPadder'], storages);
		this.drawDiv('map', this.classNames['map'], this.styleNames['map'],
				storages);

		var leftFooter = this.drawDiv('leftFooter',
				this.classNames['leftFooter']);
		var mapTab = this.constructTabImage('mapLabel',
				this.styleNames['mapLabel'], this.parameters.get('mapTabText'));
		var chartTab = this.constructTabImage('chartLabel',
				this.styleNames['chartLabel'], this.parameters
						.get('chartTabText'));
		leftFooter.append(mapTab);
		leftFooter.append(chartTab);
		this.drawDiv('clearWidget', this.classNames['clearWidget'],
				this.styleNames['clearWidget'], leftFooter);
		this.drawElement('backToNational', this.classNames['backToNational'],
				this.styleNames['backToNational'], leftFooter,
				'<a href="#"></a>');

		var rightFooter = this.drawDiv('rightFooter',
				this.classNames['rightFooter']);

		var logoImage = $('<img></img>');			
		var url = this.parameters.getWithForwardSlash('baseURL') + 'images/BM_stacked.png';
		logoImage.attr('alt',this.parameters.get('goToAWRISLinkText'));		
		logoImage.attr('src',url);		
		
		var logo = $('<a href="'+this.parameters.get('goToAWRISLink')+
		    '" onclick="window.open(this.href);return false;"></a>');
		logo.attr('title',this.parameters.get('goToAWRISLinkText'));			
		logo.append(logoImage);

		var infoLogo = $('<div class="waterWidget-infoLogo"></div>');
		infoLogo.append(logo);
		rightFooter.append(infoLogo);

		this.elements['title'].html(this.parameters.get('title'));
		this.elements['backToNational'].html(this.parameters
				.get('backToNational'));
		this.elements['backToNational'].bind("click", this, function(e) {
			if (isAboutDisplay == false) {
				e.data.backToNational(); // actually calls
				// widget.backToNational()
				return false;
			}
		});
	};

	this.constructSelectorImage = function(type, parent) {
		var selector = this.drawDiv(type, this.classNames[type],
				this.styleNames[type], parent);
		var href = $('<a href="#"></a>');
		selector.append(href);
		// selector.append(image);
		selector.bind("click", this, function(e) {
			if (isAboutDisplay == false) {
				e.data.changeType(type); // actually calls
				// widget.changeType()
				return false;
			}
		});
		selector.hover(function() {
			$(this).css('cursor', 'pointer');
			$(this).fadeTo("fast", 0.70);
		}, function() {
			$(this).fadeTo("fast", 1.00);
		});
		return href;
	};

	this.constructTabImage = function(elementName, divId, htmlText) {
		// all tabs should have define 'tab' class
		var tab = this.drawDiv(elementName, this.classNames['tab'], divId);
		var href = $('<a href="#">' + htmlText + '</a>');
		tab.append(href);
		tab.addClass(this.classNames['tabOff']);// set a default

		tab.bind("click", this, function(e) {
			if (isAboutDisplay == false) {
				if (e.data.elements[elementName]
						.hasClass(e.data.classNames['tabOn'])) {
					// do nothing, the tab is already selected
			} else {
				if (e.data.mapTabSelected == 'true') {
					e.data.mapTabSelected = 'false';
				} else {
					e.data.mapTabSelected = 'true';
				}
				e.data.render(); // actually calls widget.render()
			}
		}
		return false;
	}	);
		return tab;
	};

	/**
	 * Constructs the page to display an error.
	 */
	this.constructErrorPage = function() {
		$('#' + this.parameters.get('location')).empty();
		$('#' + this.parameters.get('location')).attr(this.classAttribute,
				this.className);

		var widthValue = this.parameters.get('width');
		if (typeof widthValue != 'undefined') {
			$('#' + this.parameters.get('location')).css('width', widthValue);
		}

		var header = this.drawDiv('header', this.classNames['header']);
		this.drawDiv('title', this.classNames['title'],
				this.styleNames['title'], header);
		this.elements['title'].html(this.parameters.get('title'));
		this.drawDiv('errorDisplay', this.classNames['errorLabel'],
				this.styleNames['errorDisplay'], header);
	};

	this.drawDiv = function(elementName, className, divId, parent) {
		var div = this.elements[elementName] = $(this.styleElement);
		if (typeof className != 'undefined') {
			this.elements[elementName].attr('class', className);
		}
		if (typeof divId != 'undefined') {
			this.elements[elementName].attr('id', divId);
		}
		if (typeof parent != 'undefined') {
			parent.append(this.elements[elementName]);
		} else {
			$('#' + this.parameters.get('location')).append(
					this.elements[elementName]);
		}
		return div;
	};

	this.drawElement = function(elementName, className, divId, parent,
			styleElement) {
		var div = this.elements[elementName] = $(styleElement);
		if (typeof className != 'undefined') {
			this.elements[elementName].attr('class', className);
		}
		if (typeof divId != 'undefined') {
			this.elements[elementName].attr('id', divId);
		}
		if (typeof parent != 'undefined') {
			parent.append(this.elements[elementName]);
		} else {
			$('#' + this.parameters.get('location')).append(
					this.elements[elementName]);
		}
		return div;
	};

	this.drawDivNoId = function(elementName, className, parent) {
		var div = this.elements[elementName] = $(this.styleElement);
		if (typeof className != 'undefined') {
			this.elements[elementName].attr('class', className);
		}
		if (typeof parent != 'undefined') {
			parent.append(this.elements[elementName]);
		} else {
			$('#' + this.parameters.get('location')).append(
					this.elements[elementName]);
		}
		return div;
	};

	this.appendElement = function(element) {
		$('#' + this.parameters.get('location')).append(element);
	};

	/**
	 * Displays or hides elements on the page depending on what is set in the
	 * given parameters. It assumes the elements are already drawn on the page.
	 */
	this.showOrHide = function(params) {

		for ( var i = 0; i < this.toggleElements.length; ++i) {
			var elementName = this.toggleElements[i];
			if (!params.get(elementName + 'Visible')) {
				this.elements[elementName].hide();
			} else {
				this.elements[elementName].show();
			}
		}
	};

	/**
	 * Changes the current region to the national region and redraws the widget.
	 */
	this.backToNational = function() {
		this.regionId = this.parameters.get('nationalRegionId');
		this.render();
	};

	/**
	 * Changes the type of map that is selected in the radio selection. This
	 * also changes the region back to the national region.
	 */
	this.changeType = function(type) {
		this.swapRadioImages(type);
		this.regionId = this.parameters.get('nationalRegionId');
		this.render();
	};

	/**
	 * Switches the images in the radio selection to select the given type and
	 * sets the current map_type to the given type.
	 */
	this.swapRadioImages = function(type) {
		// change the image of the current selected type back to the selector
		// image
		$('#' + this.styleNames[this.map_type + '-selected']).attr('id',
				this.styleNames[this.map_type]);
		// change the image of the type to be selected to the selected image
		var key = type + '-selected';
		$('#' + this.styleNames[type]).attr('id', this.styleNames[key]);
		this.map_type = type;
	}

	/**
	 * Changes the currently selected region to the region by the given
	 * region_id and displays it.
	 */
	this.changeRegion = function(regionId) {
		if (isAboutDisplay == false) {
			this.regionId = regionId;
			if (this.regionId != this.parameters.get('nationalRegionId')) {
				// we have to change the type if it is different
				var region = this.model.getRegionById(this.regionId);
				if (typeof region != 'undefined') {
					if (region.type != this.map_type) {
						this.swapRadioImages(region.type);
					}
				}
			}
		}
		this.render();
	};
}
