/*global Fusion, OpenLayers, Jx, $, alert, initTempLayers, sneMunicipalityConfig, window, sneLog, sneShowFeatureInfo */
/*jslint bitwise: true, browser: true, eqeqeq: true, immed: true, newcap: true, nomen: true, regexp: true, undef: true, white: true, indent: 4*/

/* This file was created by Snertill and contains various javascript
 functions (constructors) that are used to create web user interface components */

/* Added by Ari Þórðarson on 27/1/2009
 * Contructor for a drop down list object
 * parentElement: object: The DOM element that this element will be appended to.
 * Options: Object with options
 */
function SneDropDownList(parentElement, options)
{
	var isVisible; /* boolean: Tells weather the drop down list is shown or not */
	var listDiv; /* object: The DOM element for the div with all the rows in the list. */
	var strings; /* string array: Array of strings in the drop down list. */
	var rows; /* object array: Array of the div element within the listDiv element. */
	var selectedRow; /* number:  Number of the row that is selected in the drop down list.  
	                                            * The first row has the number 0.  
				              * If less than 0 then no row is selected.  */
	var onRowSelected; /* function: Function to call when a row gets selected */
	var updateDOM; /* function:  Function that updates the drop down list (content, color of selected row etc) writes the drop down list to the dom */
	var rowClicked; /* function:  Function that is called when a row gets clicked. */
	var onRowClicked; /* function: A function to call after a row gets clicked. */
	
	onRowSelected = function ()
	{
		alert('row selected');
	};
	
	onRowClicked = function ()
	{
	
	};
	
	/* This function is called when a row gets clicked. 
	 * rowNumber: number: Number of the row.
	 */
	rowClicked = function (rowNumber)
	{
		selectedRow = rowNumber;
		updateDOM();
		onRowSelected();
		
		/* Call this function last. */
		onRowClicked();
	};
	
	/* set method for onRowClicked */
	this.setOnRowClicked = function (onRowClickedAction)
	{
		onRowClicked = onRowClickedAction;
	};
	
	/* This function makes an ajax request to get the content of the drop down list. 
	 * searchString: string: A string to search for. 
	 * The content of the drop down list will have the results from the search.
	 */
	this.requestContent = function (searchString)
	{
		var a = new OpenLayers.Ajax.Request("../../../widgets/SneSearch/dropDownListContent.php" +
		"?inputString=" + encodeURIComponent(searchString) +
		"&configDir=" + encodeURIComponent(sneMunicipalityConfig.directory),
		{
			method: 'get', 
			onSuccess: this.requestComplete.bind(this),
			onFailure : function ()
			{
				alert('Ajax request failed.');
			}
		});
	};
	
	this.requestComplete = function (transport)
	{
		var stringArray = transport.responseText.split(';');
		var stringArrayNew = [];
		
		/* Copy all the values from stringArray to stringArrayNew except the first one.
		 * This is because of a strange bug. 
		 * If there was a value before the first semicolon 
		 * the value would have strange characters in it.
		 */
		var i;
		for (i = 1; i < stringArray.length; i++)
		{
			
			stringArrayNew[i - 1] = stringArray[i];
		}
		
		this.setContent(stringArrayNew);
	};
	
	/* Set the function to call when a row gets selected.
	 * rowSelectedAction: function: The function to call when a row gets selected.
	 * Use the function bind from the Prototype framework to bind the function to a specific object.
	 * Example:
	 * dropDownList.setRowSelectedAction(dropDownRowSelected.bind(this));
	 */
	this.setRowSelectedAction = function (rowSelectedAction)
	{
		onRowSelected = rowSelectedAction;
	};
	
	/* Update the html of the listDiv element. */
	updateDOM = function ()
	{
		if (isVisible)
		{
			if (strings.length > 10)
			{
				listDiv.setAttribute('class', 'sneSearchDropDownListWithScroll');
				listDiv.setAttribute('className', 'sneSearchDropDownListWithScroll');
			}
			else
			{
				listDiv.setAttribute('class', 'sneSearchDropDownList');
				listDiv.setAttribute('className', 'sneSearchDropDownList');
			}
		}
		else
		{
			listDiv.setAttribute('class', 'sneSearchDropDownListHidden');
			listDiv.setAttribute('className', 'sneSearchDropDownListHidden');
		}
		listDiv.innerHTML = '';

		rows = [];
		var i;
		for (i = 0; i < strings.length; i++)
		{
			rows[i] = document.createElement('div');
			listDiv.appendChild(rows[i]);			
			rows[i].innerHTML = strings[i];
			if (i === selectedRow)
			{
				rows[i].setAttribute('class', 'sneSearchDropDownListSelectedRow');
				rows[i].setAttribute('className', 'sneSearchDropDownListSelectedRow');
			}
			else
			{
				rows[i].setAttribute('class', 'sneSearchDropDownListUnselectedRow');
				rows[i].setAttribute('className', 'sneSearchDropDownListUnselectedRow');
			}
			OpenLayers.Event.observe(rows[i], 'click', rowClicked.bind(this, i));
		}
		
	};
	
	/* get method for listDiv */
	this.getListDiv = function ()
	{
		return listDiv;
	};
	
	/* Initialize this object */
	this.initialize	= function ()
	{
		isVisible = false;
		selectedRow = -1;
		strings = [];
		listDiv = document.createElement('span');
		parentElement.appendChild(listDiv);
		this.hide();
	};
	
	/* Set the content of the drop down list 
	 * stringArray: string array: The strings that will be put into the rows of the drop down list.
	 */
	this.setContent = function (stringArray)
	{
		strings = stringArray;
		selectedRow = 0;
		updateDOM();
	};
	
	/* Try to increment the selected row.
	 * If the currently selected row is the last one selectedRow will not be incremented.
	 */
	this.tryToIncrementSelectedRow = function ()
	{
		if (selectedRow < (strings.length - 1))
		{
			selectedRow++;
			updateDOM();
			onRowSelected();
		}
	};
	
	/* Try to decrement the selected row.
	 * If the currently selected row is less than 0 it will not be decremented.
	 */
	this.tryToDecrementSelectedRow = function ()
	{
		if (selectedRow >= 0)
		{
			selectedRow--;
			updateDOM();
			onRowSelected();
		}
	};
	
	/* Returns the the string in the selected row. */
	this.getSelectedRowContent = function ()
	{
		if (selectedRow >= 0)
		{
			return strings[selectedRow];
		}
		else
		{
			return '';
		}
	};
	
	/* Show the drop down list. */
	this.show = function ()
	{
		isVisible = true;
		updateDOM();
	};
	
	/* Hide the drop down list. */
	this.hide = function (message)
	{
		isVisible = false;
		updateDOM();
	};
	
	this.initialize();
}


/* Added by Ari Þórðarson on 22/1/2009
 * Constructor for a search input object
 * parentElement: object: Parent DOM element to append the search input to
 * Options: Object with options:
 * options.label: string: Label to display by search input box.
 * options.buttonValue: string: The text that is displayed on the button.
 *
 * Example of usage:
		this.sneSearchInput = new SneSearchInput(document.getElementById('sneSearchSecond'),
		{
			label: 'Heimilisfang',
			buttonValue: 'Leita'
		});
		this.sneSearchInput.setSubmitAction(this.sayHello.bind(this));
		
		then in the function sayHello
	sayHello : function ()
	{
		alert(this.sneSearchInput.getTextboxValue());
	},
 */
function SneSearchInput(parentElement, options)
{
	var minStringLengthForDropDownList = 3; /* The minimum length of the string in the textbox for the drop down list to be shown. */
	var textboxElement; /* object: The DOM element for the textbox. */
	var buttonElement; /* object: The DOM element for the submit button. */
	var dropDownList; /* object: The drop down list object. */
	var onKeyDown; /* function: Funtion to call when a key is pressed in the textbox. */
	var onButtonClicked; /* function: Function to call when the button is clicked */
	var submit; /* function: This function submits the search. */
	var onSubmit; /* function: Function to call when a string is submitted */
	var dropDownRowSelected; /* function: Function to call when a row in the drop down list has been selected. */
	var textboxKeyUp; /* function: A function that is called when the value in the textbox changes */
	var dropDownRowClicked; /* function: This function is called when a row in the drop down list gets clicked. */
	
	dropDownRowClicked = function ()
	{
		submit();
	};
	
	textboxKeyUp = function (event)
	{
		if (event.keyCode !== OpenLayers.Event.KEY_DOWN && event.keyCode !== OpenLayers.Event.KEY_UP && event.keyCode !== OpenLayers.Event.KEY_RETURN)
		{
			this.updateDropDownList();
		}
		if (event.keyCode !== OpenLayers.Event.KEY_RETURN)
		{
			if (textboxElement.value)
			{
				if (textboxElement.value.length >= minStringLengthForDropDownList)
				{
					dropDownList.show();
				}
				else
				{
					dropDownList.hide();
				}
			}
		}
	};
	
	dropDownRowSelected = function ()
	{
		textboxElement.value = dropDownList.getSelectedRowContent();
	};

	onKeyDown = function (event)
	{
		switch (event.keyCode)
		{
		case OpenLayers.Event.KEY_DOWN:
			dropDownList.tryToIncrementSelectedRow();
			break;
		case OpenLayers.Event.KEY_UP:
			dropDownList.tryToDecrementSelectedRow();
			break;
		case OpenLayers.Event.KEY_RETURN:
			submit();
			break;
		default:
			/* Do nothing */
			break;
		}
		
	};
	
	/* Make an ajax reqest to set the content of the drop down list
	 * Only do it if the length of the string in the textbox is at least 2 characters long.
	 */
	this.updateDropDownList = function ()
	{
		if (textboxElement.value)
		{
			if (textboxElement.value.length >= minStringLengthForDropDownList)
			{
				dropDownList.requestContent(textboxElement.value);
			}
		}
	};
	
	onButtonClicked = function ()
	{
		submit();
	};	
	
	submit = function ()
	{
		dropDownList.hide();
		onSubmit(textboxElement.value);
	};
	
	onSubmit = function (searchString)
	{
		alert('search submitted');
	};
	
	
	/* Initialize this object */
	this.initialize	= function ()
	{
		this.writeToDom();
	};
	
	/* Write the search input to the DOM */
	this.writeToDom = function ()
	{
		/* the label */
		if (options.label)
		{
			parentElement.innerHTML += options.label + '&nbsp&nbsp&nbsp';
		}
		
		var span = document.createElement('span');
		span.setAttribute('class', 'sneSearchTextboxSpan');
		span.setAttribute('className', 'sneSearchTextboxSpan');
		parentElement.appendChild(span);
		
		/* the textbox */
		textboxElement = document.createElement('input');
		textboxElement.setAttribute('type', 'textbox');
		textboxElement.setAttribute('id', 'sneSearchTextbox');
		span.appendChild(textboxElement);
		OpenLayers.Event.observe(textboxElement, 'keydown', onKeyDown.bind(this));
		OpenLayers.Event.observe(textboxElement, 'keyup', textboxKeyUp.bind(this));
		
		/* the drop down list */
		dropDownList = new SneDropDownList(span, {});
		dropDownList.setRowSelectedAction(dropDownRowSelected.bind(this));
		dropDownList.setOnRowClicked(dropDownRowClicked.bind(this));
		
		/* the button */
		buttonElement = document.createElement('input');
		buttonElement.setAttribute('type', 'button');
		buttonElement.setAttribute('class', 'sneSearchButton');
		buttonElement.setAttribute('className', 'sneSearchButton');
		if (options.buttonValue)
		{
			buttonElement.setAttribute('value', options.buttonValue);
		}
		parentElement.appendChild(buttonElement);
		OpenLayers.Event.observe(buttonElement, 'click', onButtonClicked.bind(this));
	

	};
	
	/* Set the submit action. 
	 * submitAction: function: Function to call when a search string has been submitted
	 */
	this.setSubmitAction = function (submitAction)
	{
		onSubmit = submitAction;
	};
	
	/* Get the value in the textbox. 
	 * Return value: string: the value in the textbox
	 */
	this.getTextboxValue = function ()
	{
		return textboxElement.value;
	};
	
	/* Set the value in the textbox 
	 * value: string: The value to put in the textbox
	 */
	this.setTextboxValue = function (value)
	{
		textboxElement.value = value;
	};
	
	/* get method for textboxElement */
	this.getTextboxElement = function ()
	{
		return textboxElement;
	};
	
	/* get method for dropDownList */
	this.getDropDownList = function ()
	{
		return dropDownList;
	};
	
	/* Initializing */
	this.initialize();
}


/* Added by Ari Þórðarson on 2/11/2009
 * Constructor for a temporary layer controler
 * object mapObj: A map for the controller to interact with.  If the controller
 * is beeing contstructed in a widget use this.getMap() to get the map.
 * object parentElement: A DOM element that the html code for this component will
 * be appended to.
 * string array layerNames: A string array with the names of the layers
 * that the controller interacts with.
 * string array layerLabels: The text that apears next to the checkbox for each layer.
 * string title: The title that apears in the legend in the fieldset.
 * string group: The name of the group of layers. It should be one of the following:
 * {'length', 'area', 'labels', 'coordinates'}
 * string helpMessage: A help message to display for the corresponding widget
 */
function SneTempLayerController(mapObj, parentElement, layerNames, layerLabels, title, group, helpMessage)
{
	/* object array checkboxes: The DOM elements of the checkboxes for the temporary layers */
	var checkboxes = [];
	
	/* object clearButton: The button that is used to delete the content of the layers */
	var clearButton = null;
	
	/* object surroundingFieldset: The HTML fieldset that surrounds the rest of the html code for this component */
	var surroundingFieldset = null;
	
	/* function checkboxClicked: A function that is called when a checkbox is clicked */
	var checkboxClicked;
	
	/* function mapReloaded: A function that is called when the map is reloaded. */
	var mapReloaded;
	
	/* function initLayerControls: This function does some initialization 
	for the user inputs that control the layers.  It should only be executed when
	the layers are ready */
	var initLayerControls;
	
	/* function registerLayerEvents: Register some events for the layers */
	var registerLayerEvents;
	
	/* function clearButtonClicked:  Executed when the clear button is clicked */
	var clearButtonClicked;
	
	/* function layerPropertyChanged: Called when a property of one of the layers changes */
	var layerPropertyChanged;
	
	/* function clearSuccess: Called when a layer has been cleared successfully */
	var clearSuccess;
	
	clearButtonClicked = function ()
	{
		var a = new OpenLayers.Ajax.Request("../../../layers/MapGuide/php/SneClearTempLayers.php" +
		'?session=' + encodeURIComponent(mapObj.getAllMaps()[0].getSessionID()) +
		'&mapname=' + encodeURIComponent(mapObj.getAllMaps()[0].getMapName()) +
		'&group=' + encodeURIComponent(group),
		{
			method: 'get',
			onSuccess: clearSuccess.bind(this),
			onFailure : function ()
			{
				alert('SneTempLayerDialog: Error clearing a layer!');
			}
		});
	};
	
	clearSuccess = function (transport)
	{
        mapObj.getAllMaps()[0].drawMap();
	};
	
	checkboxClicked = function (index)
	{
		var layerObject = mapObj.layerRoot.groups[0].findLayerByAttribute('layerName', layerNames[index]);
		if (checkboxes[index].checked)
		{
			layerObject.show();
		}
		else
		{
			layerObject.hide();
		}
	};
	
	mapReloaded = function ()
	{
		/* initTempLayers is a global variable */
		if (initTempLayers)
		{
			initLayerControls();
			
			/* This needs to be done each time the map is reloaded */
			registerLayerEvents();
		}
	};
	
	initLayerControls = function ()
	{
		var layerObject;
		var i;
		var n = layerNames.length;
		
		for (i = 0; i < n; i++)
		{
			layerObject = mapObj.layerRoot.groups[0].findLayerByAttribute('layerName', layerNames[i]);
			if (layerObject)
			{
				checkboxes[i].disabled = false;
				if (layerObject.isVisible())
				{
					checkboxes[i].checked = true;
				}
				else
				{
					checkboxes[i].checked = false;
				}
			}
			else
			{
				checkboxes[i].disabled = true;
			}
		}
		OpenLayers.Event.observe(clearButton, 'click', clearButtonClicked.bind(this));
		clearButton.disabled = false;
	};
	
	registerLayerEvents = function ()
	{
		var layerObject;
		var i;
		var n = layerNames.length;
		
		for (i = 0; i < n; i++)
		{
			layerObject = mapObj.layerRoot.groups[0].findLayerByAttribute('layerName', layerNames[i]);
			if (layerObject)
			{
				/* call this here though we don't know 
				if the event has occured because it may 
				have happened while the map was reloading */
				layerPropertyChanged(i);
				
				layerObject.registerForEvent(Fusion.Event.LAYER_PROPERTY_CHANGED, layerPropertyChanged.bind(this, i));
			}
		}
	};
	
	layerPropertyChanged = function (index)
	{
		var layerObject = mapObj.layerRoot.groups[0].findLayerByAttribute('layerName', layerNames[index]);
		if (layerObject)
		{
			if (layerObject.isVisible())
			{
				checkboxes[index].checked = true;
			}
			else
			{
				checkboxes[index].checked = false;
			}
		}
	};
	
	this.initialize = function ()
	{
		mapObj.registerForEvent(Fusion.Event.MAP_RELOADED, OpenLayers.Function.bind(mapReloaded, this));
		this.writeToDOM();
	};
	
	/* Write to the DOM */
	this.writeToDOM = function ()
	{
		surroundingFieldset = $(document.createElement('fieldset'));
		surroundingFieldset.setStyle('display', 'none');
		surroundingFieldset.setStyle('visibility', 'hidden');
		parentElement.appendChild(surroundingFieldset);
		var legend = $(document.createElement('legend'));
		legend.innerHTML = title;
		surroundingFieldset.appendChild(legend);
		
		var helpMessP;
		if (helpMessage)
		{
			helpMessP = $(document.createElement('p'));
			helpMessP.innerHTML = helpMessage;
			surroundingFieldset.appendChild(helpMessP);
			surroundingFieldset.appendChild(document.createElement('br'));
		}
		
		var p = $(document.createElement('p'));
		p.innerHTML = OpenLayers.Lang.translate('tempLayerDialogMessage');
		surroundingFieldset.appendChild(p);
		surroundingFieldset.appendChild(document.createElement('br'));
		
		var span;
		var i;
		var n = layerNames.length;
		for (i = 0; i < n; i++)
		{
			checkboxes[i] = $(document.createElement('input'));
			checkboxes[i].setAttribute('type', 'checkbox');
			checkboxes[i].setAttribute('disabled', 'disabled');
			surroundingFieldset.appendChild(checkboxes[i]);
			OpenLayers.Event.observe(checkboxes[i], 'click', checkboxClicked.bind(this, i));
			
			span = $(document.createElement('span'));
			span.innerHTML = '&ensp;' + layerLabels[i];
			surroundingFieldset.appendChild(span);
			surroundingFieldset.appendChild(document.createElement('br'));
		}
		
		clearButton = $(document.createElement('input'));
		clearButton.setAttribute('type', 'button');
		clearButton.setAttribute('value', OpenLayers.Lang.translate('Clear'));
		clearButton.setAttribute('disabled', 'disabled');
		surroundingFieldset.appendChild(clearButton);
		
	};
	
	/* Show the controller */
	this.show = function ()
	{
		surroundingFieldset.setStyle('display', 'block');
		surroundingFieldset.setStyle('visibility', 'visible');
	};
	
	/* Hide the controller */
	this.hide = function ()
	{
		surroundingFieldset.setStyle('display', 'none');
		surroundingFieldset.setStyle('visibility', 'hidden');
	};
	
	this.initialize();
}

/* Constructor for a panel switcher that is a select box that is used
 * to switch between divs that can contain html code.
 * object parentElement: The DOM element that the panel switcher will
 * be appended to.
 * string array panelIds: DOM ids of the divs for the panels
 * string array panelLabels: The names of the panels
 * Only the panels that have something in them are shown when the method 
 * checkPanels is called
 *
 * Example of use:
 *
 * Create a panel switcher
 * snePanelSwitcher = new SnePanelSwitcher($(tab1.content), ['sneInfo', 'drawings', 'sneFeatureInfo'], [OpenLayers.Lang.translate('InformationTitle'), OpenLayers.Lang.translate('Drawings'), OpenLayers.Lang.translate('moreInformation')]);
 * 
 * Check witch panels contain something
 * snePanelSwitcher.checkPanels();
 * 
 * Show panel number 2
 * snePanelSwitcher.setActivePanel(2);
 * 
 */
function SnePanelSwitcher(parentElement, panelIds, panelLabels)
{
	/* The select DOM element */
	var selectBox = null;
	
	/* An array containing the div elements that are used for the panels */
	var panelDivs = [];
	
	/* Number of the active panel div */
	var activePanelDivIndex = 0;
	
	var selectChanged; /* function: A function to call when the select element changes. */
	
	var panelsChecked = false; /* boolean panelsChecked: Tells weather the panels have been checked.*/
	
	selectChanged = function ()
	{
		panelDivs[activePanelDivIndex].hide();
		activePanelDivIndex = selectBox.selectedIndex;
		panelDivs[activePanelDivIndex].show();
	};
	
	this.writeToDOM = function ()
	{
		var message = $(document.createElement('span'));
		message.innerHTML = OpenLayers.Lang.translate('selectTask') + ': ';
		parentElement.appendChild(message);
		
		selectBox = $(document.createElement('select'));
		parentElement.appendChild(selectBox);
		OpenLayers.Event.observe(selectBox, 'change', selectChanged.bind(this));
		
		var panelArea = $(document.createElement('div'));
		panelArea.setAttribute('class', 'snePanelDiv');
		panelArea.setAttribute('className', 'snePanelDiv');
		parentElement.appendChild(panelArea);
		
		var i;
		if (panelIds)
		{
			for (i = 0; i < panelIds.length; i++)
			{
				panelDivs[i] = $(document.createElement('div'));
				panelDivs[i].setAttribute('id', panelIds[i]);
				panelDivs[i].hide();
				panelArea.appendChild(panelDivs[i]);
				
			}
		}
		
	};
	
	/* Set the active panel
	 * number index: The index of the panel to set as active
	 */
	this.setActivePanel = function (index)
	{
		if (panelsChecked)
		{
			selectBox.selectedIndex = index;
			selectChanged();
		}
	};
	
	/* Call this function when fusion 
	 * has been initialized to set the right 
	 * panels in the panel switcher so that 
	 * only the widgets that are in the 
	 * application definition are in the 
	 * panel swithcher */
	this.checkPanels = function ()
	{
		/* Reassign panelDivs */
		var tempPanelDivs = panelDivs;
		panelDivs = [];
		var option;
		var i;
		var n = tempPanelDivs.length;
		for (i = 0; i < n; i ++)
		{
			if (tempPanelDivs[i].innerHTML.length > 1)
			{
				panelDivs.push(tempPanelDivs[i]);
				option = $(document.createElement('option'));
				option.innerHTML = panelLabels[i];
				selectBox.appendChild(option);
			}
		}
		panelDivs[activePanelDivIndex].show();
		panelsChecked = true;
	};
	
	this.writeToDOM();

}

/* A div with information about a feature in the map like a house 
or service 

object parentElement: A DOM element to put the feature info in.
object mapObject: The map object that the feature info is for.

*/
function SneFeatureInfo(parentElement, mapObject)
{
	/* object mainDiv: The main div that contains all the html for this component */
	var mainDiv;
	
	/* function setContent: Set the content of the */
	var setContent;
	
	/* function featureInfoSuccess: Called after
	successfully requesting feature info */
	var featureInfoSuccess;
	
	/* function initialize: A function that performs 
	various initialization tasks */
	var initialize;
	
	initialize = function ()
	{
		mainDiv = $(document.createElement('div'));
		parentElement.appendChild(mainDiv);
		
		var p = $(document.createElement('p'));
		p.innerHTML = OpenLayers.Lang.translate('featureInfoMessage');
		mainDiv.appendChild(p);
	};
	
	/* Request information about feature before the content is sett */
	this.requestContent = function (type, id, name, pointX, pointY)
	{
		/* Put a random number in the request for Internet Explorer */
		var rand = Math.floor(Math.random() * 1000000);
	
		mainDiv.innerHTML = '';
		var loadingIcon = $(document.createElement('img'));
		loadingIcon.setAttribute('src', '../sneInfrapathSimpleLayout/images/loadingIcon.gif');
		mainDiv.appendChild(loadingIcon);
		
		var a = new OpenLayers.Ajax.Request("../../../widgets/sneMaptip/getFeatureInfo.php?" + 
		"layertype=" + encodeURIComponent(type) +
		"&id=" + encodeURIComponent(id) +
		"&name=" + encodeURIComponent(name) +
		"&orgid=" + encodeURIComponent(sneMunicipalityConfig.orgID) +
		"&pointx=" + encodeURIComponent(pointX) +
		"&pointy=" + encodeURIComponent(pointY) +
		"&configDir=" + sneMunicipalityConfig.directory +
		"&rand=" + rand,
		{
			method: 'get',
			onSuccess: featureInfoSuccess.bind(this),
			onFailure : function ()
			{
				alert('SneFeatureInfo.js: Ajax error when trying to get content!');
			}
		});
	};
	
	featureInfoSuccess = function (transport)
	{
		if (transport.responseText === 'error')
		{
			alert('SneFeatureInfo.js: Failure in feature info service!');
		}
		else
		{
			if (transport.responseText)
			{
				var infoArray = transport.responseText.split('&');
				var info = [];
				info.layerType = decodeURIComponent(infoArray[0]);
				info.id = decodeURIComponent(infoArray[1]);
				info.title = decodeURIComponent(infoArray[2]);
				info.description = decodeURIComponent(infoArray[3]);
				info.imageUrl = decodeURIComponent(infoArray[4]);
				info.link = decodeURIComponent(infoArray[5]);
				info.address = decodeURIComponent(infoArray[6]);
				info.zipCode = decodeURIComponent(infoArray[7]);
				info.tel = decodeURIComponent(infoArray[8]);
				info.email = decodeURIComponent(infoArray[9]);
				info.pointX = decodeURIComponent(infoArray[10]);
				info.pointY = decodeURIComponent(infoArray[11]);
				info.propertyNumber = decodeURIComponent(infoArray[12]);
				info.landNumber = decodeURIComponent(infoArray[13]);
				info.noRecord = decodeURIComponent(infoArray[14]);
				setContent(info);
			}
		}
	};
	
	/* Set the content of the feature info div 
	 * object info: An object containing information about feature
	 */
	setContent = function (info)
	{
		mainDiv.innerHTML = '';
		
		var title = $(document.createElement('h1'));
		title.innerHTML = info.title;
		mainDiv.appendChild(title);
		
		var image = $(document.createElement('img'));
		image.setAttribute('src', info.imageUrl);
		mainDiv.appendChild(image);
		
		mainDiv.appendChild(document.createElement('br'));
		mainDiv.appendChild(document.createElement('br'));
		if (info.description)
		{
			if (info.description !== '')
			{
				var pDescription = $(document.createElement('p'));
				pDescription.innerHTML = info.description.replace(/\n/g, '<br />');
				mainDiv.appendChild(pDescription);
				mainDiv.appendChild(document.createElement('br'));
			}
		}
		
		if (info.noRecord === 'false')
		{
			if (info.link)
			{
				if (info.link !== '')
				{
					var link = $(document.createElement('a'));
					link.setAttribute('href', info.link);
					link.setAttribute('target', '_blank');
					
					link.innerHTML = OpenLayers.Lang.translate('homepage');
					mainDiv.appendChild(link);
					
					mainDiv.appendChild(document.createElement('br'));
				}
			}
		}
		
		mainDiv.appendChild(document.createElement('br'));
		
		if (info.layerType === 'thjonusta' || info.layerType === 'travel')
		{
			if (info.address)
			{
				if (info.address !== '')
				{
					mainDiv.innerHTML += '<span class="sneMaptipFieldTitle">' + OpenLayers.Lang.translate('home') + ':</span> ' + info.address + '<br />';
				}
			}
			
			if (info.zipCode)
			{
				if (info.zipCode !== '')
				{
					mainDiv.innerHTML += '<span class="sneMaptipFieldTitle">' + OpenLayers.Lang.translate('areaCode') + ':</span> ' + info.zipCode + '<br />';
				}
			}
			if (info.tel)
			{
				if (info.tel !== '')
				{
					mainDiv.innerHTML += '<span class="sneMaptipFieldTitle">' + OpenLayers.Lang.translate('telephone') + ':</span> ' + info.tel + '<br />';
				}
			}
			if (info.email)
			{
				if (info.email !== '')
				{
					mainDiv.innerHTML += '<span class="sneMaptipFieldTitle">' + OpenLayers.Lang.translate('email') + ':</span> ' + info.email + '<br />';
				}
			}
		}
		
		mainDiv.appendChild(document.createElement('br'));
		
		var printpage = document.createElement('a');
		
		/* Put a random number in the url for Internet Explorer */
		var rand = Math.floor(Math.random() * 1000000);
		
		var aMaps = mapObject.getAllMaps();
		
		var baseMapId = '';
		if (sneMunicipalityConfig.printingBaseMapId != undefined)
		{
			if (sneMunicipalityConfig.printingBaseMapId !== '')
			{
				baseMapId = sneMunicipalityConfig.printingBaseMapId;
			}
		}
		
		var printURL = '../../../widgets/sneprint/featureinfoprint.php' + 
		'?orgid=' + encodeURIComponent(sneMunicipalityConfig.orgID) +
		'&layertype=' + encodeURIComponent(info.layerType) +
		'&id=' + encodeURIComponent(info.id) +
		'&name=' + encodeURIComponent(info.title) +
		'&centerx=' + encodeURIComponent(info.pointX) +
		'&centery=' + encodeURIComponent(info.pointY) +
		'&scale=' + encodeURIComponent(mapObject.getScale()) +
		'&session=' + encodeURIComponent(aMaps[0].getSessionID()) +
		'&mapname=' + encodeURIComponent(aMaps[0].getMapName()) +
		'&language=' + encodeURIComponent(window._FusionLocale) +
		'&basemapid=' + encodeURIComponent(baseMapId) +
		'&rand=' + encodeURIComponent(rand);
		
		printpage.setAttribute('href', printURL);
		printpage.setAttribute('target', '_blank');
		printpage.innerHTML = OpenLayers.Lang.translate('printTitle');
		mainDiv.appendChild(printpage);
	};
	
	/* Hide the feature info div */
	this.hide = function ()
	{
		mainDiv.hide();
	};
	
	/* Show the feature info div */
	this.show = function ()
	{
		mainDiv.show();
	};
	
	initialize();
}

/* Constructor for a clickable span
 * object parentElement: The DOM element that the span is appended to
 * string label: The text that is displayed in the span
 * function clickAction: A function to call when the span is clicked.
 * Use the bind function when passing the function to the constructor
 *
 * Example of use:
 
		//Construct a new clickable span
		var linkbutton = new SneClickableSpan(div, 'Click me', this.buttonClicked.bind(this));
		
		//Enable it
		linkbutton.enable();
		
		//Disable it
		linkbutton.disable();
 */
function SneClickableSpan(parentElement, label, clickAction)
{
	/* The span */
	var spanObject = null;

	/* This function is called when the span is clicked */
	var onClick = null;
	
	/* Tells weather the span is enabled or not */
	var spanEnabled = false;
	
	onClick = function ()
	{
		if (spanEnabled)
		{
			clickAction();
		}
	};
	
	this.initialize = function ()
	{
		spanObject = document.createElement('span');
		spanObject.innerHTML = label;
		parentElement.appendChild(spanObject);
		OpenLayers.Event.observe(spanObject, 'click', onClick.bind(this));
		
		this.disable();
	};
	
	this.enable = function ()
	{
		spanObject.setAttribute('class', 'sneClickableSpan');
		spanObject.setAttribute('className', 'sneClickableSpan');
		spanEnabled = true;
	};
	
	this.disable = function ()
	{
		spanEnabled = false;
		spanObject.setAttribute('class', 'sneClickableSpanDisabled');
		spanObject.setAttribute('className', 'sneClickableSpanDisabled');
	};

	this.initialize();
}

/* A constructor for a select box that controls the visibility of divs
 * object parentElement: The DOM element that the select box will be appended to
 * function changeAction: A function to call when the active div changes
 * Use the bind function when passing the function to the constructor
 *
 * //Example of use
 * this.divSwitcher = new SneDivSwitcher(div, this.activeDivChanged.bind(this));
 */
function SneDivSwitcher(parentElement, changeAction)
{
	/* The div dom elements */
	var divObjects = [];

	/* This function performs initialization tasks for this object */
	var initialize = null;
	
	/* This function is called when the select box changes */
	var selectChanged = null;
	
	/* The select box that is used to switch between the divs */
	var selectBox;
	
	/* Number of the active div */
	var activeDivIndex = 0;
	
	/* Set the active div 
	 * number index: The index of the div to set as active
	 * If index is 0 the first div will be active, if index is 1
	 * the second div will be activ and so on.
	 */
	this.setActiveDiv = function (index)
	{
		selectBox.selectedIndex = index;
		selectChanged();
	};
	
	/* Returns the number of the div that is active */
	this.getActiveDivIndex = function ()
	{
		return activeDivIndex;
	};
	
	/* Add a div to the div switcher 
	 * object divObject: The div dom element to be added to the div switcher
	 * string label: The text to display in the select box for this div
	 */
	this.addDiv = function (divObject, label)
	{
		var option = document.createElement('option');
		option.innerHTML = label;
		selectBox.appendChild(option);
		divObjects.push(divObject);
		if (divObjects.length - 1 === activeDivIndex)
		{
			$(divObject).show();
		}
		else
		{
			$(divObject).hide();
		}
	};
	
	selectChanged = function ()
	{
		$(divObjects[activeDivIndex]).hide();
		activeDivIndex = selectBox.selectedIndex;
		$(divObjects[activeDivIndex]).show();
		changeAction();
	};
	
	initialize = function ()
	{
		selectBox = document.createElement('select');
		parentElement.appendChild(selectBox);
		OpenLayers.Event.observe(selectBox, 'change', selectChanged.bind(this));
	};
	
	initialize();
}

/* A contstructor for a special type of menu bar with menu buttons.
 object parentElement: The DOM element that the menubar is appended to.
 string array menuButtonLabels: The labels on the menu buttons
 */
function SneMenubar(parentElement, menuButtonLabels)
{
	/* object menuContainer: The div containing all the menu buttons */
	var menuContainer = null;
	
	/* object array menuDivs: Array of divs that represent menu buttons. The clickable parts of the menubar */
	var menuDivs = [];
	
	/* object array betweenDivs: Array of divs that are between the clickable parts of the menubar */
	var betweenDivs = [];
	
	/* object menubarEnd: The div at the right end of the menu divs */
	var menubarEnd = null;
	
	/* object mapLabelRight: The div with the map label */
	var mapLabelRight;
	
	/* object array: Array of divs that contain the menus */
	var menuContainers = [];
	
	/* object array menus: Array with the menus.  The objects should be of type Jx.SneMenu */
	var menus = [];
	
	/* Called when the mouse goes over a menu button
	 * number index: The number of the menu button that the mouse went over
	 */
	var mouseOverMenu = function (index)
	{
		menuDivs[index].setAttribute('class', 'SneMapMenuButtonHover');
		menuDivs[index].setAttribute('className', 'SneMapMenuButtonHover');
		var classname = 'SneMenuButtonBetweenHoverRight';
		if (index === 0)
		{
			classname = 'SneMapMenuButtonLeftHover';
		}
		betweenDivs[index].setAttribute('class', classname);
		betweenDivs[index].setAttribute('className', classname);
		
		if (index < menuDivs.length - 1)
		{
			betweenDivs[index + 1].setAttribute('class', 'SneMenuButtonBetweenHoverLeft');
			betweenDivs[index + 1].setAttribute('className', 'SneMenuButtonBetweenHoverLeft');
		}
		else
		{
			menubarEnd.setAttribute('class', 'SneMapMenuEndHover');
			menubarEnd.setAttribute('className', 'SneMapMenuEndHover');			
		}
	};
	
	/* Called when the mouse goes off a menu button.
	 * number index: The number of the menu button that the mouse went off
	 */
	var mouseOutMenu = function (index)
	{
		menuDivs[index].setAttribute('class', 'SneMapMenuButton');
		menuDivs[index].setAttribute('className', 'SneMapMenuButton');
		var classname = 'SneMenuButtonBetween';
		if (index === 0)
		{
			classname = 'SneMapMenuButtonLeft';
		}
		betweenDivs[index].setAttribute('class', classname);
		betweenDivs[index].setAttribute('className', classname);
		
		if (index !== menuDivs.length - 1)
		{
			betweenDivs[index + 1].setAttribute('class', 'SneMenuButtonBetween');
			betweenDivs[index + 1].setAttribute('className', 'SneMenuButtonBetween');
		}
		else
		{
			menubarEnd.setAttribute('class', 'SneMapMenuEnd');
			menubarEnd.setAttribute('className', 'SneMapMenuEnd');		
		}
	};
	
	/* Called when a menu button is clicked
	 * number index: Number of the menu button that was clicked
	 */
	var menuClicked = function (index)
	{
		if (menus[index])
		{
			menus[index].show({e: 1});
		}
	};
	
	/* Do various initialization tasks such as writing to the DOM */
	var initialize = function ()
	{
		menuContainer = document.createElement('div');
		menuContainer.setAttribute('id', 'SneMapMenu');
		parentElement.appendChild(menuContainer);
		
		var i;
		for (i = 0; i < menuButtonLabels.length; i++)
		{
			betweenDivs[i] = document.createElement('div');
			var classname = 'SneMenuButtonBetween';
			if (i === 0)
			{
				classname = 'SneMapMenuButtonLeft';
			}
			betweenDivs[i].setAttribute('class', classname);
			betweenDivs[i].setAttribute('className', classname);
			menuContainer.appendChild(betweenDivs[i]);
			
			menuDivs[i] = document.createElement('div');
			menuDivs[i].setAttribute('class', 'SneMapMenuButton');
			menuDivs[i].setAttribute('className', 'SneMapMenuButton');
			menuDivs[i].innerHTML = menuButtonLabels[i];
			menuContainer.appendChild(menuDivs[i]);
			OpenLayers.Event.observe(menuDivs[i], 'mouseover', mouseOverMenu.bind(this, i));
			OpenLayers.Event.observe(menuDivs[i], 'mouseout', mouseOutMenu.bind(this, i));
			OpenLayers.Event.observe(menuDivs[i], 'click', menuClicked.bind(this, i));
			
			/* Add a menu */
			menuContainers[i] = document.createElement('div');
			menuContainers[i].setAttribute('class', 'sneMenuHolder');
			menuContainers[i].setAttribute('className', 'sneMenuHolder');
			menuDivs[i].appendChild(menuContainers[i]);
			//menus[i] = new Jx.SneMenu({});
			//menus[i].addTo(div);
		}
		
		menubarEnd = document.createElement('div');
		menubarEnd.setAttribute('class', 'SneMapMenuEnd');
		menubarEnd.setAttribute('className', 'SneMapMenuEnd');
		menuContainer.appendChild(menubarEnd);
		
		var mapLabelLeft = document.createElement('div');
		mapLabelLeft.setAttribute('class', 'SneMapLabelLeft');
		mapLabelLeft.setAttribute('className', 'SneMapLabelLeft');
		parentElement.appendChild(mapLabelLeft);
		
		mapLabelRight = document.createElement('div');
		mapLabelRight.setAttribute('class', 'SneMapLabelRight');
		mapLabelRight.setAttribute('className', 'SneMapLabelRight');
		parentElement.appendChild(mapLabelRight);
	};
	
	/* Set the text in the map label
	 * string text: The string to put in the 
	 * innerHTML of the map label div
	 */
	this.setMapLabelText = function (text)
	{
		mapLabelRight.innerHTML = text;
	};
	
	/* Returns the innerHTML of the map label div */
	this.getMapLabelText = function ()
	{
		return mapLabelRight.innerHTML;
	};
	
	/* Get an array with all the menus
	 * Returns an array with objects of type Jx.SneMenu
	 */
	this.getMenuContainers = function ()
	{
		return menuContainers;
	};
	
	/* Register a menu for a specific menu button
	 * Use this function to register a menu that has been added in the ApplicationDefinition.xml file
	 * So that the menu will open when the right menu button is clicked
	 * object menu: The menu.  This should be of type Jx.SneMenu
	 * number index: Number of the menu button in the menubar to assign the menu to.
	 */
	this.registerMenu = function (menu, index)
	{
		menus[index] = menu;
	};
	
	initialize();
}

/* A contstructor for a special type of menu bar with menu buttons.
 object parentElement: The DOM element that the infobox is appended to.
 */
function SneInfoBox()
{
	/* The div that contains everything, both the box and the greyout div */
	var container;
	
	/* The content div in the info box */
	var contentDiv;
	
	/* The div for the info box */
	var boxDiv;
	
	/* Tells weather the info box is open or not */
	var isOpen;

	/* Private method for closing the info box
	 * accessable by other private methods
	 */
	var close = function ()
	{
		isOpen = false;
		$(container).setStyle('display', 'none');
	};
	
	/* Public method for closing the info box */
	this.close = function ()
	{
		close();
	};
	
	this.open = function ()
	{
		$(container).setStyle('display', 'block');
		isOpen = true;
	};
	
	this.getContentDiv = function ()
	{
		return contentDiv;
	};
	
	this.getBoxDiv = function ()
	{
		return boxDiv;
	};
	
	var keyDown = function (event)
	{
		if (isOpen)
		{
			if (event.keyCode === OpenLayers.Event.KEY_ESC)
			{
				close();
			}
		}
	};
	
	var initialize = function ()
	{
		container = document.createElement('div');
		container.setAttribute('class', 'SneInfoBoxContainer');
		container.setAttribute('className', 'SneInfoBoxContainer');
		document.getElementsByTagName('body')[0].appendChild(container);
		
		var greyOutDiv = document.createElement('div');
		greyOutDiv.setAttribute('class', 'SneInfoBoxGrayoutDiv');
		greyOutDiv.setAttribute('className', 'SneInfoBoxGrayoutDiv');
		container.appendChild(greyOutDiv);
		
		boxDiv = document.createElement('div');
		boxDiv.setAttribute('class', 'SneInfoBoxDiv');
		boxDiv.setAttribute('className', 'SneInfoBoxDiv');
		container.appendChild(boxDiv);
		
		var topDiv = document.createElement('div');
		topDiv.setAttribute('class', 'SneInfoBoxTopDiv');
		topDiv.setAttribute('className', 'SneInfoBoxTopDiv');
		boxDiv.appendChild(topDiv);
		
		contentDiv = document.createElement('div');
		contentDiv.setAttribute('class', 'SneInfoBoxContentDiv');
		contentDiv.setAttribute('className', 'SneInfoBoxContentDiv');
		boxDiv.appendChild(contentDiv);
		
		
		/* Add a close button in the top div */
		var closeDiv = document.createElement('div');
		closeDiv.setAttribute('class', 'SneInfoBoxCloseDiv');
		closeDiv.setAttribute('className', 'SneInfoBoxCloseDiv');
		topDiv.appendChild(closeDiv);
		
		var closeButton = new SneClickableSpan(closeDiv, 'Loka', close.bind(this));
		closeButton.enable();
		
		/* Text that informs the user that it is possible 
		to close the box by pressing the Esc key */
		/* Commented out by Ari Þórðarson on 8/10/2010 */
		/*
		var closeEscSpan = document.createElement('span');
		closeEscSpan.innerHTML = '&ensp;' + OpenLayers.Lang.translate('orEscKey');
		closeDiv.appendChild(closeEscSpan);
		*/
		isOpen = false;
		/* Commented out by Ari Þórðarson on 8/10/2010 */
		/*
		OpenLayers.Event.observe(document, 'keydown', keyDown.bind(this));
		*/
	};
	
	initialize();
}

/*
 * Constructor for a option set
 * object parentElement: The dom element that the option set will be appended to
 * string array images: An array of urls to the images in the option buttons
 * string array tooltips: Tooltips that are displayed with the option buttons
 * function array actionsWhenClicked: Functions to call for each option
 */
function SneOptionSet(parentElement, images, tooltips, actionsWhenClicked)
{
	var surroundingSpan;
	var optionImages = [];
	var selectedIndex = 0;

	var optionClicked = function (index)
	{
		if (index !== selectedIndex)
		{
			optionImages[selectedIndex].setAttribute('class', 'sneOptionImage');
			optionImages[selectedIndex].setAttribute('className', 'sneOptionImage');
			selectedIndex = index;
			optionImages[selectedIndex].setAttribute('class', 'sneOptionImageSelected');
			optionImages[selectedIndex].setAttribute('className', 'sneOptionImageSelected');
		}
		actionsWhenClicked[index]();
	};
	
	var initialize = function ()
	{
		surroundingSpan = document.createElement('span');
		parentElement.appendChild(surroundingSpan);
		
		var i;
		var n = images.length;
		for (i = 0; i < n; i++)
		{
			optionImages[i] = document.createElement('img');
			optionImages[i].setAttribute('src', images[i]);
			
			var className = 'sneOptionImage';
			if (i === selectedIndex)
			{
				className = 'sneOptionImageSelected';
			}
			
			optionImages[i].setAttribute('class', className);
			optionImages[i].setAttribute('className', className);
			optionImages[i].setAttribute('title', tooltips[i]);
			surroundingSpan.appendChild(optionImages[i]);
			OpenLayers.Event.observe(optionImages[i], 'click', optionClicked.bind(this, i));
		}
	};
	
	initialize();
}

/*
 * Constructor for a sliding panel
 * object parentElement: The dom element that the sliding panel is appended to
 * number width: The width of the panel
 * number height: The height of the panel
 * string tooltip: A tooltip for the open button
 */
function SneSlidingPanel(parentElement, width, height, tooltip)
{
	/* object panelOpener: This is a html div element. The button that is used to open the panel */
	var panelOpener;
	
	/* object panelDiv: A html div element. The div that contains the panel */
	var panelDiv;
	
	/* Possible states that the panel can be in */
	var panelStates = {closed: 0, open: 1, opening: 2, closing: 3};
	
	/* number panelState: The current state */
	var panelState = panelStates.closed;
	
	/* number panelStartPosX: The starting position of the panel
	 * Let it be width - 2*(the width of the border with paddding)
	 */
	var panelStartPosX = 0 - width - 4;
	
	/* number panelPosX: The current position of the panel */
	var panelPosX = panelStartPosX;
	
	/* number panelPosXIncrement: The amount of pixels the panel is moved in each move */
	var panelPosXIncrement = 50;
	
	/* number timeBetweenMoves: Time in milliseconds between moves while the panel is moving */
	var timeBetweenMoves = 20;
	
	/* boolean pulsating: Tells weather the opening button is pulsating or not */
	var pulsating = false;
	
	/* boolean pulseOn: Tells weather the pulsating style is on while the panel opener is pulsating */
	var pulseOn = false;
	
	/* number pulseTime: Time between pulse change in milliseconds */
	var pulseTime = 1000;
	
	/* Make the panel opener pulsate
	 * Currently this works only when the panel is closed
	 * A pulsating style needs to be added for the opener when the panel is open
	 * if the pulsating effect is to be used when the panel is open.
	 */
	var pulsate = function ()
	{
		if (pulsating)
		{
			if (pulseOn)
			{
				panelOpener.setAttribute('class', 'sneSlidingPanelOpenPulsate');
				panelOpener.setAttribute('className', 'sneSlidingPanelOpenPulsate');
			}
			else
			{
				panelOpener.setAttribute('class', 'sneSlidingPanelOpen');
				panelOpener.setAttribute('className', 'sneSlidingPanelOpen');
			}
			pulseOn = !pulseOn;
			setTimeout(pulsate.bind(this), pulseTime);
		}
	};
	
	/* Turn the pulsating effect in the opening button on */
	var startPulsating = function ()
	{
		pulsating = true;
		pulseOn = true;
		pulsate();
	};
	
	var stopPulsating = function ()
	{
		pulsating = false;
		pulseOn = false;
		if (panelState === panelStates.closed || panelState === panelStates.opening)
		{
			panelOpener.setAttribute('class', 'sneSlidingPanelOpen');
			panelOpener.setAttribute('className', 'sneSlidingPanelOpen');
		}
		else
		{
			panelOpener.setAttribute('class', 'sneSlidingPanelClose');
			panelOpener.setAttribute('className', 'sneSlidingPanelClose');
		}
	};
	
	/* Returns the object panelDiv */
	this.getPanelDiv = function ()
	{
		return panelDiv;
	};
	
	/* Updates the look and position of the panel 
	according to the variables */
	var updatePanel = function ()
	{
		if (panelState === panelStates.closed)
		{
			panelOpener.setAttribute('class', 'sneSlidingPanelOpen');
			panelOpener.setAttribute('className', 'sneSlidingPanelOpen');
		}
		if (panelState === panelStates.open)
		{
			panelOpener.setAttribute('class', 'sneSlidingPanelClose');
			panelOpener.setAttribute('className', 'sneSlidingPanelClose');
		}
		$(panelOpener).setStyle('right', (panelPosX - panelStartPosX) + 'px');
		$(panelDiv).setStyle('right', panelPosX + 'px');
	};
	
	/* Call this to move the panel
	 * If panelState is set to panelStates.opening the panel will open
	 * If panelState is set to panelStates.closing the panel will close
	 */
	var movePanel = function ()
	{
		if (panelState === panelStates.opening)
		{
			if (panelPosX < 0)
			{
				if (panelPosX + panelPosXIncrement >= 0)
				{
					panelPosX = 0;
					panelState = panelStates.open;					
					updatePanel();
				}
				else
				{
					panelPosX = panelPosX + panelPosXIncrement;
					updatePanel();
					setTimeout(movePanel.bind(this), timeBetweenMoves);
				}
			}
		}
		if (panelState === panelStates.closing)
		{
			if (panelPosX > panelStartPosX)
			{
				if (panelPosX - panelPosXIncrement <= panelStartPosX)
				{
					panelPosX = panelStartPosX;
					panelState = panelStates.closed;					
					updatePanel();
				}
				else
				{
					panelPosX = panelPosX - panelPosXIncrement;
					updatePanel();
					setTimeout(movePanel.bind(this), timeBetweenMoves);
				}
			}
		}
	};
	
	/* Open the panel */
	var open = function ()
	{
		if (pulsating)
		{
			stopPulsating();
		}
		if (panelState === panelStates.closed)
		{
			panelState = panelStates.opening;
			movePanel();
		}
	};
	
	/* Close the panel */
	var close = function ()
	{
		if (panelState === panelStates.open)
		{
			panelState = panelStates.closing;
			movePanel();
		}
	};
	
	/* Public function for opening the panel */
	this.open = function ()
	{
		open();
	};
	
	/* Public function for closing the panel */
	this.close = function ()
	{
		close();
	};
	
	/* Called when the opening/closing button is clicked */
	var openerClicked = function ()
	{
		if (panelState === panelStates.closed)
		{
			open();
		}
		if (panelState === panelStates.open)
		{
			close();
		}
	};
	
	/* Do some initialization tasks */
	var initialize = function ()
	{
		panelOpener = document.createElement('div');
		panelOpener.setAttribute('class', 'sneSlidingPanelOpen');
		panelOpener.setAttribute('className', 'sneSlidingPanelOpen');
		if (tooltip)
		{
			if (tooltip !== '')
			{
				panelOpener.setAttribute('title', tooltip);
			}
		}
		parentElement.appendChild(panelOpener);
		OpenLayers.Event.observe(panelOpener, 'click', openerClicked.bind(this));
		
		panelDiv = document.createElement('div');
		panelDiv.setAttribute('class', 'sneSlidingPanelDiv');
		panelDiv.setAttribute('className', 'sneSlidingPanelDiv');
		$(panelDiv).setStyle('right', panelPosX + 'px');
		$(panelDiv).setStyle('width', width);
		$(panelDiv).setStyle('height', height);
		parentElement.appendChild(panelDiv);
	};
	
	initialize();
	startPulsating();
}


/* Constructor for a map dialog that shows
 * information about a selected feature in the map
 * object parentElement: The dom element that the map dialog is appended to
 * object mapObject: The fusion map that the dialog is used with
 * TODO make this work for other features than houses
 */
function SneMapDialog(parentElement, mapObject)
{
	/* The div that contains the dialog */
	var dialogDiv;
	
	/* The div at the top of the dialog */
	var topDiv;
	
	/* A div in the top div containing the label of the top div */
	var topDivLabel;
	
	/* The div in the middle of the dialog */
	var middleDiv;
	
	/* The div at the bottom of the dialog */
	var bottomDiv;
	
	/* Object with information about the selected feature */
	var featureInfo = [];
	
	/* The amount of pixels to add to the height of the middle div to get the height of the dialog div with the arrow */
	var pixelsToAddToHeight = 106;
	
	/* The height of the dialog with the arrow in pixels */
	var dialogHeight;
	
	/* boolean isOpen: This variable tells weather the maptip is open or not
	 * The maptip is visible if this is true and it is in the current map view
	 */
	var isOpen = false;
	
	/* boolean requestingInfo: This variable tells weather a
	feature info request is being processed */
	var requestingInfo = false;
	
	/* Make the map dialog invisible */
	var hide = function ()
	{
		$(dialogDiv).setStyle('display', 'none');
		$(dialogDiv).setStyle('visibility', 'hidden');
	};
	
	/* Make the map dialog visible */
	var show = function ()
	{
		$(dialogDiv).setStyle('display', 'block');
		$(dialogDiv).setStyle('visibility', 'visible');
	};
	
	/* This function returns true if the centroid of 
	 * the selected feature is in the current map view
	 * else false
	 */
	var isPoinOverMap = function ()
	{
		var extents = mapObject.getCurrentExtents();
        //var centerX = (extents.left + extents.right) / 2;
        //var centerY = (extents.top + extents.bottom) / 2;
		if (featureInfo.pointX >= extents.left && featureInfo.pointX <= extents.right && featureInfo.pointY >= extents.bottom && featureInfo.pointY <= extents.top)
		{
			return true;
		}
		return false;
	};
	
	/* This function takes in a string and removes the later part of it
	 * if it is longer than maxLength
	 */
	var changeLengthOfText = function (text, maxLength)
	{
		/* Check length of text */
		var output = '';
		if (text.length < maxLength)
		{
			output = text;
		}
		else
		{
			var tempDescr = text.substring(0, maxLength);

			/* Remove the last word so that the last word in 
			the output is not a piece of a word. */
			var i;
			var n = tempDescr.length;
			var i2;
			for (i = n - 1; i >= 0; i--)
			{
				if (tempDescr.charAt(i) === ' ')
				{
					i2 = i;
					break;
				}
			}
			output = text.substring(0, i2);
			output = output.trim();
			output = output + '...';
		}
		return output;
	};
	
	/* Called when the more link gets clicked */
	var moreLinkClicked = function (type, id, name, pointX, pointY)
	{
		sneShowFeatureInfo(type, id, name, pointX, pointY);
	};
	
	var writeToDialog = function ()
	{
		if (featureInfo)
		{
			/* Clear everything */
			topDivLabel.innerHTML = '';
			middleDiv.innerHTML = '';
			bottomDiv.innerHTML = '';
		
			topDivLabel.innerHTML = featureInfo.title;
			if (featureInfo.noRecord === 'true' && featureInfo.layerType === 'hus')
			{
				dialogHeight = 30 + pixelsToAddToHeight;
				$(dialogDiv).setStyle('width', '200px');
				$(middleDiv).setStyle('height', '30px');
			}
			else
			{
				dialogHeight = 100 + pixelsToAddToHeight;
				$(dialogDiv).setStyle('width', '300px');
				$(middleDiv).setStyle('height', '100px');
				var img = $(document.createElement('img'));
				
				img.setAttribute('id', 'sneMaptipImage');
				middleDiv.appendChild(img);
				if (featureInfo.imageUrl !== '')
				{
					img.setAttribute('src', featureInfo.imageUrl);
				}
				else
				{
					img.setAttribute('src', '../sneInfrapath/images/snehouse.png');
				}
			}
			
			var textDiv = $(document.createElement('div'));
			textDiv.setAttribute('id', 'sneMaptipTextDiv');
			middleDiv.appendChild(textDiv);
			
			if (featureInfo.noRecord === 'true' && featureInfo.layerType === 'hus')
			{
				if (featureInfo.propertyNumber)
				{
					if (featureInfo.propertyNumber !== '')
					{
						textDiv.innerHTML += '<span class="sneMaptipFieldTitle">' + OpenLayers.Lang.translate('proptertyNumber') + ': </span>' + featureInfo.propertyNumber;
						textDiv.appendChild(document.createElement('br'));
					}
				}
				if (featureInfo.landNumber)
				{
					if (featureInfo.landNumber !== '')
					{
						textDiv.innerHTML += '<span class="sneMaptipFieldTitle">' + OpenLayers.Lang.translate('propertiesLandNumber') + ': </span>' + featureInfo.landNumber;
						textDiv.appendChild(document.createElement('br'));
					}
				}
			}
			else
			{
				/* Count the lines that are used so we know how many lines 
				the description can take for a service feature. */
				var lineCount = 0;
				
				if (featureInfo.layerType === 'thjonusta' || featureInfo.layerType === 'travel')
				{
					var addExtraLine = false;
					if (featureInfo.address)
					{
						if (featureInfo.address !== '')
						{
							textDiv.innerHTML += '<span class="sneMaptipFieldTitle">' + OpenLayers.Lang.translate('home') + ':</span> ' + featureInfo.address + '<br />';
							
							/* Add the line count of the address text to the total line count in the text div */
							lineCount += Math.floor((featureInfo.address.length + 10) / 20) + 1;
							
							addExtraLine = true;
						}
					}
					
					if (featureInfo.zipCode)
					{
						if (featureInfo.zipCode !== '')
						{
							textDiv.innerHTML += '<span class="sneMaptipFieldTitle">' + OpenLayers.Lang.translate('areaCode') + ':</span> ' + featureInfo.zipCode + '<br />';
							lineCount++;
							
							addExtraLine = true;
						}
					}
					if (featureInfo.tel)
					{
						if (featureInfo.tel !== '')
						{
							textDiv.innerHTML += '<span class="sneMaptipFieldTitle">' + OpenLayers.Lang.translate('telephone') + ':</span> ' + featureInfo.tel + '<br />';
							lineCount++;
							
							addExtraLine = true;
						}
					}
					if (featureInfo.email)
					{
						if (featureInfo.email !== '')
						{
							textDiv.innerHTML += '<span class="sneMaptipFieldTitle">' + OpenLayers.Lang.translate('email') + ':</span> ' + featureInfo.email + '<br />';
							
							/* Add the line count of the email text to the total line count in the text div */
							lineCount += Math.floor((featureInfo.email.length + 10) / 20) + 1;
							
							addExtraLine = true;
						}
					}
					
					if (addExtraLine)
					{
						textDiv.innerHTML += '<br />';
						lineCount++;
					}
				}

				/* Check if there is a link and increase 
				the line count if there is */
				var noLink = true;
				if (featureInfo.link)
				{
					if (featureInfo.link !== '')
					{
						noLink = false;
					}
				}
				if (!noLink)
				{
					lineCount++;
				}

				if (lineCount < 7)
				{
					if (featureInfo.description)
					{
						if (featureInfo.description !== '')
						{
							/* Check length of text */
							var maxLength;
							maxLength = (7 - lineCount) * 20;
							var description = changeLengthOfText(featureInfo.description, maxLength);
							
							var newlines = description.match(/\n/g);
							if (newlines)
							{
								var newlineCount = newlines.length;
								maxLength = maxLength - newlineCount * 15;
								description = changeLengthOfText(featureInfo.description, maxLength);
							}
							textDiv.innerHTML += description.replace(/\n/g, '<br />') + '&ensp;';
						}
					}
				}
				var moreLink = $(document.createElement('a'));
				moreLink.setAttribute('href', 'javascript:void(0)');
				moreLink.innerHTML = OpenLayers.Lang.translate('more');
				textDiv.appendChild(moreLink);
				OpenLayers.Event.observe(moreLink, 'click', moreLinkClicked.bind(this, featureInfo.layerType, featureInfo.id, featureInfo.title, featureInfo.pointX, featureInfo.pointY));
			}
			
			var linkUrl = featureInfo.link;
			var linkText = OpenLayers.Lang.translate('homepage');
			
			if (featureInfo.noRecord === 'true')
			{
				linkText = '';
			}
			else
			{
				if (linkUrl)
				{
					if (linkUrl !== '')
					{
						
						textDiv.appendChild($(document.createElement('br')));
						var link = $(document.createElement('a'));
						link.setAttribute('href', linkUrl);
						link.setAttribute('target', '_blank');
						
						link.innerHTML = linkText;
						textDiv.appendChild(link);
					}
				}
			}
			
			var disableDrawings = false;
			/* Added this so we can disable the drawings reports for some municipalities */
			if (sneMunicipalityConfig.disableDrawings != undefined)
			{
				if (sneMunicipalityConfig.disableDrawings == 'true')
				{
					disableDrawings = true;
				}
			}
			
			if (featureInfo.layerType === 'hus')
			{
                var reportDirectory;
                if (sneMunicipalityConfig.usingIDR === 'true')
                {
					reportDirectory = sneMunicipalityConfig.idrReportDirectory;
                }
                else 
                {
					reportDirectory = sneMunicipalityConfig.reportDirectory;
                }
            
				if (!disableDrawings)
				{
					var drawingLink = document.createElement('a');
					drawingLink.setAttribute('href', reportDirectory + 'drawings/drawings.php?language=' +
					window._FusionLocale + '&heinum=' + featureInfo.id +
					'&configDir=' + encodeURIComponent(sneMunicipalityConfig.directory));
					drawingLink.setAttribute('target', '_blank');
					drawingLink.setAttribute('title', OpenLayers.Lang.translate('getDrawings'));
					bottomDiv.appendChild(drawingLink);
					
					var drawingImage = document.createElement('img');
					drawingImage.setAttribute('src', '../sneInfrapath/images/report.png');
					drawingLink.appendChild(drawingImage);
					
					var drawingText = document.createElement('span');
					drawingText.innerHTML = OpenLayers.Lang.translate('drawings');
					drawingLink.appendChild(drawingText);
				}
			}
			if (featureInfo.layerType === 'hofn')
			{
				if (!disableDrawings)
				{
					var reportLink = document.createElement('a');
					reportLink.setAttribute('href', 'http://hekla.snertill.is/faxafloahafnir_technical/web/drawings/search_harbordrawings.cfm?search_landnr=' + featureInfo.id);
					reportLink.setAttribute('target', '_blank');
					reportLink.setAttribute('title', OpenLayers.Lang.translate('getDrawings'));
					bottomDiv.appendChild(reportLink);
					
					var reportImage = document.createElement('img');
					reportImage.setAttribute('src', '../sneInfrapath/images/report.png');
					reportLink.appendChild(reportImage);
					
					var reportText = document.createElement('span');
					reportText.innerHTML = OpenLayers.Lang.translate('drawings');
					reportLink.appendChild(reportText);
				}
			}
		}
	};
	
	/* Close the dialog */
	var close = function ()
	{
		isOpen = false;
		hide();
	};
	
	/* Open the dialog */
	var open = function ()
	{
		isOpen = true;
		//middleDiv.innerHTML = featureInfo.landNumber;
		writeToDialog();
		
		var pix = mapObject.geoToPix(featureInfo.pointX, featureInfo.pointY);
		var mapPos = $(mapObject.getDomObj()).getPosition();
		$(dialogDiv).setStyle('left', mapPos.x + pix.x);
		$(dialogDiv).setStyle('top', mapPos.y + pix.y - dialogHeight);		
		
		if (isPoinOverMap())
		{
			show();
		}
		else
		{
			hide();
		}
	};
	
	var featureInfoSuccess = function (transport)
	{
		if (transport.responseText === 'error')
		{
			sneLog('SneMapDialog: Error when trying to get information about feature!');
		}
		else
		{
			if (transport.responseText)
			{
				var infoArray = transport.responseText.split('&');
				featureInfo.layerType = decodeURIComponent(infoArray[0]);
				featureInfo.featId = decodeURIComponent(infoArray[1]);
				featureInfo.id = decodeURIComponent(infoArray[2]);
				featureInfo.title = decodeURIComponent(infoArray[3]);
				featureInfo.description = decodeURIComponent(infoArray[4]);
				featureInfo.imageUrl = decodeURIComponent(infoArray[5]);
				featureInfo.link = decodeURIComponent(infoArray[6]);
				featureInfo.address = decodeURIComponent(infoArray[7]);
				featureInfo.zipCode = decodeURIComponent(infoArray[8]);
				featureInfo.tel = decodeURIComponent(infoArray[9]);
				featureInfo.email = decodeURIComponent(infoArray[10]);
				featureInfo.pointX = decodeURIComponent(infoArray[11]);
				featureInfo.pointY = decodeURIComponent(infoArray[12]);
				featureInfo.propertyNumber = decodeURIComponent(infoArray[13]);
				featureInfo.landNumber = decodeURIComponent(infoArray[14]);
				featureInfo.noRecord = decodeURIComponent(infoArray[15]);
				
				requestingInfo = false;
				
				open();
			}
		}
	};
	
	var locationSuccess = function (transport)
	{
		if (requestingInfo)
		{
			if (transport.responseText === 'error')
			{
				sneLog('SneMapDialog: Error when trying to get location of feature!');
			}
			else
			{
				if (transport.responseText)
				{
					var infoArray = transport.responseText.split('&');
					featureInfo.pointX = decodeURIComponent(infoArray[0]);
					featureInfo.pointY = decodeURIComponent(infoArray[1]);
					
					isOpen = true;
					//middleDiv.innerHTML = featureInfo.landNumber;
					/* Clear everything */
					topDivLabel.innerHTML = '';
					middleDiv.innerHTML = '';
					bottomDiv.innerHTML = '';
					
					/* Put a loading icon in the dialog */
					var loadingIcon = $(document.createElement('img'));
					loadingIcon.setAttribute('src', '../sneInfrapathSimpleLayout/images/loadingIcon.gif');
					middleDiv.appendChild(loadingIcon);
				
					dialogHeight = 30 + pixelsToAddToHeight;
					$(dialogDiv).setStyle('width', '200px');
					$(middleDiv).setStyle('height', '30px');
					
					var pix = mapObject.geoToPix(featureInfo.pointX, featureInfo.pointY);
					var mapPos = $(mapObject.getDomObj()).getPosition();
					$(dialogDiv).setStyle('left', mapPos.x + pix.x);
					$(dialogDiv).setStyle('top', mapPos.y + pix.y - dialogHeight);		
					
					if (isPoinOverMap())
					{
						show();
					}
					else
					{
						hide();
					}
				}
			}
		}
	};
	
	/* Prepare showing the dialog
	 * number featId: The FeatId of the feature
	 * number id: Another id of the feature other than FeatId, if this is a house this should be heinum
	 * string address: address of feature
	 */
	var prepareShow = function (type, featId, id, address)
	{
		requestingInfo = true;
		
		/* Put a random number in the request for Internet Explorer */
		var rand = Math.floor(Math.random() * 1000000);

        var aMaps = mapObject.getAllMaps();
		
		/* Make a special request to get the coordinates of
		the feature so we can show the dialog early */
		var a = new OpenLayers.Ajax.Request("../../../widgets/sneMaptip/getFeatureLocation.php?" + 
		"layertype=" + encodeURIComponent(type) +
		"&featid=" + encodeURIComponent(featId) +
		"&configDir=" + sneMunicipalityConfig.directory +
		'&session=' + encodeURIComponent(aMaps[0].getSessionID()) +
		'&mapname=' + encodeURIComponent(aMaps[0].getMapName()) +
		"&rand=" + rand,
		{
			method: 'get',
			onSuccess: locationSuccess.bind(this),
			onFailure : function ()
			{
				sneLog('SneMapDialog: Failed to get feature info');
			}
		});
		
		a = new OpenLayers.Ajax.Request("../../../widgets/sneMaptip/getFeatureInfoWithLocation.php?" + 
		"layertype=" + encodeURIComponent(type) +
		"&featid=" + encodeURIComponent(featId) +
		"&id=" + encodeURIComponent(id) +
		"&name=" + encodeURIComponent(address) +
		"&orgid=" + encodeURIComponent(sneMunicipalityConfig.orgID) +
		"&pointx=" + encodeURIComponent('1') +
		"&pointy=" + encodeURIComponent('1') +
		"&configDir=" + sneMunicipalityConfig.directory +
		'&session=' + encodeURIComponent(aMaps[0].getSessionID()) +
		'&mapname=' + encodeURIComponent(aMaps[0].getMapName()) +
		"&rand=" + rand,
		{
			method: 'get',
			onSuccess: featureInfoSuccess.bind(this),
			onFailure : function ()
			{
				sneLog('SneMapDialog: Failed to get feature info');
			}
		});
	};
	
	var getId = function (oSelection)
	{
		var featId = null;
		var id = null;
		var address = '';
		var type = '';
	
		var selectionData = null;
        for (var mapName in oSelection)
		{
			if (oSelection[mapName])
			{
				// Don't know how this works. When logged to console it did not appear to look
				// like an array. However I think this equals to oSelection[0], not sure if it equals to oSelection;
				// i.e this.oSelection = oSelection; 
				// This is however done in efforts to get the first map and is on the TODO list to implement this for
				// many maps, which I quite don't understand how to do.
				selectionData = oSelection[mapName];
				break;
			}
        }
        if (!selectionData)
		{
			// No selection, goodbye.
            return;
        }
		var nLayers = selectionData.getNumLayers();
		var i;
		var j;
        for (i = 0; i < nLayers; i++)
		{
			
			var activeElements;
			var activeElementsLength;
			var propertyNames;
			var propertyNamesLength;
			// For every layer in the selection.
            var layerObj = selectionData.getLayer(i);
			if (layerObj.getName() === 'hus')
			{
				activeElements = layerObj.aElements;
				activeElementsLength = activeElements.length;
				propertyNames = layerObj.aPropertiesName;
				propertyNamesLength = propertyNames.length;
				
				/* Only do this if there is only one feature selected */
				if (activeElementsLength === 1)
				{
					for (j = 0; j < propertyNamesLength; j++)
					{
						var idPropertyName = 'FeatId';
						if (sneMunicipalityConfig.usingTopobase != undefined)
						{
							if (sneMunicipalityConfig.usingTopobase == 'true')
							{
								idPropertyName = 'FID'; /* Put the right property for Topobase name here */
							}
						}
						if (propertyNames[j] === idPropertyName)
						{
							featId = activeElements[0][j];
						}
						if (propertyNames[j] === 'HEINUM')
						{
							id = activeElements[0][j];
						}
						if (propertyNames[j] === 'HEIMILISFANG')
						{
							address = activeElements[0][j];
						}
					}
				}
				type = 'hus';
			}
			if (layerObj.getName() === 'hofn')
			{
				activeElements = layerObj.aElements;
				activeElementsLength = activeElements.length;
				propertyNames = layerObj.aPropertiesName;
				propertyNamesLength = propertyNames.length;
				
				/* Only do this if there is only one feature selected */
				if (activeElementsLength === 1)
				{
					for (j = 0; j < propertyNamesLength; j++)
					{
						if (propertyNames[j] === 'FeatId')
						{
							featId = activeElements[0][j];
						}
						if (propertyNames[j] === 'HAFNV_NR_NYTT')
						{
							id = activeElements[0][j];
						}
					}
				}
				type = 'hofn';
			}
        }
		
		if (featId !== null)
		{
			if (type === 'hus')
			{
				prepareShow(type, featId, id, address);
			}
			if (type === 'hofn')
			{
				prepareShow(type, featId, id, '');
			}
		}
	};
	
	var selectionOn = function ()
	{
		if (mapObject.hasSelection())
		{
			mapObject.getSelection(getId.bind(this));
		}
	};
	
	var selectionOff = function ()
	{
		close();
	};
	
	var mapExtentsChanged = function ()
	{
		if (isOpen)
		{
			var pix = mapObject.geoToPix(featureInfo.pointX, featureInfo.pointY);
			var mapPos = $(mapObject.getDomObj()).getPosition();
			$(dialogDiv).setStyle('left', mapPos.x + pix.x);
			$(dialogDiv).setStyle('top', mapPos.y + pix.y - dialogHeight);
			
			if (isPoinOverMap())
			{
				show();
			}
			else
			{
				hide();
			}
		}
	};
	
	var closeButtonClicked = function ()
	{
		close();
	};
	
	var initialize = function ()
	{
		mapObject.registerForEvent(Fusion.Event.MAP_SELECTION_ON, selectionOn.bind(this));
		mapObject.registerForEvent(Fusion.Event.MAP_SELECTION_OFF, selectionOff.bind(this));
		mapObject.registerForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, mapExtentsChanged.bind(this));
	
		dialogDiv = document.createElement('div');
		dialogDiv.setAttribute('class', 'sneMapDialog');
		dialogDiv.setAttribute('className', 'sneMapDialog');
		$(dialogDiv).setStyle('display', 'none');
		$(dialogDiv).setStyle('visibility', 'hidden');
		parentElement.appendChild(dialogDiv);
		
		var arrow = document.createElement('div');
		arrow.setAttribute('class', 'sneMapDialogArrow');
		arrow.setAttribute('className', 'sneMapDialogArrow');
		dialogDiv.appendChild(arrow);
	
		/* Put lots of small divs in the arrow div */
		var i;
		var arrowPart;
		var j = 0;
		for (i = 0; i < 60; i++)
		{
			j += i % 2;
			arrowPart = $(document.createElement('div'));
			arrowPart.setAttribute('class', 'sneMapDialogArrowPart');
			arrowPart.setAttribute('className', 'sneMapDialogArrowPart');
			arrowPart.setStyle('width', (30 - j - (i + 1) % 2) + 'px');
			arrowPart.setStyle('top', i + 'px');
			arrowPart.setStyle('left', (30 - j) + 'px');
			arrow.appendChild(arrowPart);
		}
	
		topDiv = document.createElement('div');
		topDiv.setAttribute('class', 'sneMapDialogTop');
		topDiv.setAttribute('className', 'sneMapDialogTop');
		dialogDiv.appendChild(topDiv);
		
		var topDivTop = document.createElement('div');
		topDivTop.setAttribute('class', 'sneMapDialogTopDivTop');
		topDivTop.setAttribute('className', 'sneMapDialogTopDivTop');
		topDiv.appendChild(topDivTop);
		
		var topDivLeft = document.createElement('div');
		topDivLeft.setAttribute('class', 'sneMapDialogTopDivLeft');
		topDivLeft.setAttribute('className', 'sneMapDialogTopDivLeft');
		topDiv.appendChild(topDivLeft);
		
		var topDivRight = document.createElement('div');
		topDivRight.setAttribute('class', 'sneMapDialogTopDivRight');
		topDivRight.setAttribute('className', 'sneMapDialogTopDivRight');
		topDiv.appendChild(topDivRight);
		
		topDivLabel = document.createElement('div');
		topDivLabel.setAttribute('class', 'sneMapDialogTopDivLabel');
		topDivLabel.setAttribute('className', 'sneMapDialogTopDivLabel');
		topDiv.appendChild(topDivLabel);
		
		middleDiv = document.createElement('div');
		middleDiv.setAttribute('class', 'sneMapDialogMiddle');
		middleDiv.setAttribute('className', 'sneMapDialogMiddle');
		dialogDiv.appendChild(middleDiv);
		
		bottomDiv = document.createElement('div');
		bottomDiv.setAttribute('class', 'sneMapDialogBottom');
		bottomDiv.setAttribute('className', 'sneMapDialogBottom');
		bottomDiv.innerHTML = 'Bottom';
		dialogDiv.appendChild(bottomDiv);
		
		var closeButton = document.createElement('div');
		closeButton.setAttribute('class', 'sneMapDialogCloseButton');
		closeButton.setAttribute('className', 'sneMapDialogCloseButton');
		closeButton.setAttribute('title', OpenLayers.Lang.translate('close'));
		dialogDiv.appendChild(closeButton);
		OpenLayers.Event.observe(closeButton, 'click', closeButtonClicked.bind(this));
	};
	
	initialize();
}


/* Constructor for an object that manages a information box
 * with drawing links that is opened when the user selects
 * certain features
 * object mapObject: A fusion map that this is used for
 */
function SneDrawingsDialog(mapObject)
{
	/* string array layerNames: Array of the layer names to use the dialog for */
	var layerNames;
	
	/* string array layerAttributes: Array of the attributes of the layers that
	 * are used to find the right drawings for each feature
	 */
	var layerAttributes;
	
	/* Get the layer names and attributes from the config file */
	if (sneMunicipalityConfig.drawingsDialogLayers != undefined)
	{
		layerNames = sneMunicipalityConfig.drawingsDialogLayers.split(',');
	}
	if (sneMunicipalityConfig.drawingsDialogLayerAttributes != undefined)
	{
		layerAttributes = sneMunicipalityConfig.drawingsDialogLayerAttributes.split(',');
	}
	
	var infoBox = new SneInfoBox();
	
	/* Array of SneClickableSpan objects
	 * these are used to show the pdf files
	 */
	var drawingLinks = [];
	
	/* Array of divs containing the clickable spans */
	var drawingLinkDivs = [];
	
	/* The index of the selected span */
	var selectedSpanIndex = 0;
	
	/* object pdfDiv: The div element with the pdf object */
	var pdfDiv;
	
	/* Make it a little bit wider than a normal SneInfoBox */
	$(infoBox.getBoxDiv()).setStyle('width', '800px');
	
	/* Change the overflow setting so it will not have scrollbars */
	$(infoBox.getContentDiv()).setStyle('overflow', 'visible');

	/* Set this to true if you want the toolbar in the pdf viewer to be hidden */
	var hideToolbar = false;
	
	/* Create the html code for the pdf object
	 * string pdfUrl: The url of the pdf file
	 * Returns a string with the html code
	 */
	var createPdfObjectCode = function (pdfUrl)
	{
		var hideCode = '';
		if (hideToolbar)
		{
			hideCode = '#toolbar=0';
		}
			
		return '<object data="' + pdfUrl + hideCode + '" type="application/pdf" class="SneDrawingDialogPdf"></object>';
	};
	
	var showPdf = function (clickSpanIndex, pdfUrl)
	{
		pdfDiv.innerHTML = createPdfObjectCode(pdfUrl);
		if (drawingLinkDivs[selectedSpanIndex])
		{
			drawingLinkDivs[selectedSpanIndex].setAttribute('class', 'SneDrawingDialogLinkDiv');
			drawingLinkDivs[selectedSpanIndex].setAttribute('className', 'SneDrawingDialogLinkDiv');
		}
		selectedSpanIndex = clickSpanIndex;
		drawingLinkDivs[clickSpanIndex].setAttribute('class', 'SneDrawingDialogLinkDivSelected');
		drawingLinkDivs[clickSpanIndex].setAttribute('className', 'SneDrawingDialogLinkDivSelected');
	};
	
	var drawingsRequestComplete = function (transport)
	{
		if (transport.responseText === 'error')
		{
			infoBox.getContentDiv().innerHTML = OpenLayers.Lang.translate('SneDrawingsDialogError');
		}
		else if (transport.responseText === 'noresult')
		{
			infoBox.getContentDiv().innerHTML = OpenLayers.Lang.translate('SneDrawingsDialogNoResults');
		}
		else
		{
			infoBox.getContentDiv().innerHTML = ''; /* Clear the content div */
			
			/* The div with a list of clickable spans */
			var linkDiv = document.createElement('div');
			linkDiv.setAttribute('class', 'SneDrawingDialogListDiv');
			linkDiv.setAttribute('className', 'SneDrawingDialogListDiv');
			infoBox.getContentDiv().appendChild(linkDiv);
			
			var drawingsInfos = transport.responseText.split('&');
			var linkText = '';
			var pdfLink = '';
			var firstPdfLink = '';
			var drawingInfo = [];
			drawingLinks = [];
			drawingLinkDivs = [];
			var i;
			var n = drawingsInfos.length;
			for (i = 0; i < n; i++)
			{
				drawingInfo = drawingsInfos[i].split('?');
				linkText = decodeURIComponent(drawingInfo[0]);
				pdfLink = decodeURIComponent(drawingInfo[1]);
				if (i === 0)
				{
					firstPdfLink = pdfLink;
				}
				selectedSpanIndex = 0;
				drawingLinkDivs[i] = document.createElement('div');
				if (i === 0)
				{
					drawingLinkDivs[i].setAttribute('class', 'SneDrawingDialogLinkDivSelected');
					drawingLinkDivs[i].setAttribute('className', 'SneDrawingDialogLinkDivSelected');
				}
				else
				{
					drawingLinkDivs[i].setAttribute('class', 'SneDrawingDialogLinkDiv');
					drawingLinkDivs[i].setAttribute('className', 'SneDrawingDialogLinkDiv');
				}
				linkDiv.appendChild(drawingLinkDivs[i]);
				
				drawingLinks[i] = new SneClickableSpan(drawingLinkDivs[i], linkText, showPdf.bind(this, i, pdfLink));
				drawingLinks[i].enable();
			}
			
			pdfDiv = document.createElement('div');
			pdfDiv.setAttribute('class', 'SneDrawingDialogPdfDiv');
			pdfDiv.setAttribute('className', 'SneDrawingDialogPdfDiv');
			infoBox.getContentDiv().appendChild(pdfDiv);
			pdfDiv.innerHTML = createPdfObjectCode(firstPdfLink);
		}
	};
	
	var requestDrawingInfo = function(layerName, id)
	{
		/* Put a random number in the request for Internet Explorer */
		var rand = Math.floor(Math.random() * 1000000);
	
		var a = new OpenLayers.Ajax.Request("../../../widgets/sneMaptip/getDrawingsLinks.php" +
		"?layername=" + encodeURIComponent(layerName) +
		"&id=" + encodeURIComponent(id) +
		"&rand=" + rand,
		{
			method: 'get', 
			onSuccess: drawingsRequestComplete.bind(this),
			onFailure : function ()
			{
				infoBox.getContentDiv().innerHTML = OpenLayers.Lang.translate('SneDrawingsDialogError');
				sneLog('SneDrawingsDialog.requestDrawingInfo: Ajax request failed!');
			}
		});
	};
	
	var showInfoBox = function (layerName, id)
	{
		var loadingIcon = $(document.createElement('img'));
		loadingIcon.setAttribute('src', '../sneInfrapathSimpleLayout/images/loadingIcon.gif');
		infoBox.getContentDiv().innerHTML = '';
		infoBox.getContentDiv().appendChild(loadingIcon);
		infoBox.open();
		requestDrawingInfo(layerName, id);
	};
	
	var getId = function (oSelection)
	{
		var id = null;
		var layerName = null;
		var selectionData = null;
        for (var mapName in oSelection)
		{
			if (oSelection[mapName])
			{
				// Don't know how this works. When logged to console it did not appear to look
				// like an array. However I think this equals to oSelection[0], not sure if it equals to oSelection;
				// i.e this.oSelection = oSelection; 
				// This is however done in efforts to get the first map and is on the TODO list to implement this for
				// many maps, which I quite don't understand how to do.
				selectionData = oSelection[mapName];
				break;
			}
        }
        if (!selectionData)
		{
			// No selection, goodbye.
            return;
        }
		var nLayers = selectionData.getNumLayers();
		var i;
		var j;
		var k;
		var i2;
		var lastId;
		var allIdsIdentical;
        for (i = 0; i < nLayers; i++)
		{
			/* For every layername in the config file */
			for (j = 0; j < layerNames.length; j++)
			{
				// For every layer in the selection.
				var layerObj = selectionData.getLayer(i);
				if (layerObj.getName() === layerNames[j])
				{
					var activeElements = layerObj.aElements;
					var activeElementsLength = activeElements.length;
					var propertyNames = layerObj.aPropertiesName;
					var propertyNamesLength = propertyNames.length;
					
					for (k = 0; k < propertyNamesLength; k++)
					{
						if (propertyNames[k] === layerAttributes[j])
						{
							/* Only get the id if the id is the same for all the elements.
							 * This is because we only want to open the dialog when there is one object selected.
							 * Once we used to check the length of the activeElements array and we only got the
							 * id when the there was only one object in the array but that did not work in some cases
							 * so now we use this method instead.
							 */
							
							/* Tells weather all the ids are identical in the activeElements array */
							allIdsIdentical = true;
							for (i2 = 0; i2 < activeElementsLength; i2++)
							{
								if (i2 === 0)
								{
									lastId = activeElements[i2][k];
								}
								else
								{
									if (lastId != activeElements[i2][k])
									{
										allIdsIdentical = false;
									}
								}
							}
							
							if (allIdsIdentical)
							{
								id = activeElements[0][k];
								//id = html_entity_decode(activeElements[0][k]);
								//id = OpenLayers.String.unescapeHTML(activeElements[0][k]);
								layerName = layerNames[j];
							}
						}
					}
				}
			}
			/* If a feature has ben found then stop the loop */
			if (id)
			{
				break;
			}
        }
		if (id)
		{
			showInfoBox(layerName, id);
		}
	};
	
	var selectionOn = function ()
	{
		if (mapObject.hasSelection())
		{
			mapObject.getSelection(getId.bind(this));
		}
	};
	
	mapObject.registerForEvent(Fusion.Event.MAP_SELECTION_ON, selectionOn.bind(this));
}

/* Constructor for an object that opens reports for special layers
 * when objects in them are selected
 * object mapObject: The fusion map to use
 * string array layerNames: Names of the layers to open reports for
 * string array layerAttributes: The attributes of the layers to use
 *
 * Note: This only works for reports that use a obj_keys input
 * that is a comma seperated list of LANDNR values from the map
 */
function SneReportOpener(mapObject)
{
	var layerNames = ['Lodasamningar', 'Lodablod'];
	var layerAttributes = ['LANDNR', 'LANDNR'];
	
	var openReport = function (layerName, id)
	{
		var url = '';
		var open = false;
		
		if (layerName == 'Lodasamningar')
		{
			url = 'http://hekla.snertill.is/Faxafloahafnir_technical/web/drawings/rep_contracts.cfm';
			open = true;
		}
		if (layerName == 'Lodablod')
		{
			url = 'http://hekla.snertill.is/Faxafloahafnir_technical/web/drawings/rep_data.cfm';
			open = true;
		}
		if (open)
		{
			window.open(url + '?obj_keys=' + id);
		}
	};

	var getId = function (oSelection)
	{
		var id = null;
		var layerName = null;
		var selectionData = null;
        for (var mapName in oSelection)
		{
			if (oSelection[mapName])
			{
				// Don't know how this works. When logged to console it did not appear to look
				// like an array. However I think this equals to oSelection[0], not sure if it equals to oSelection;
				// i.e this.oSelection = oSelection; 
				// This is however done in efforts to get the first map and is on the TODO list to implement this for
				// many maps, which I quite don't understand how to do.
				selectionData = oSelection[mapName];
				break;
			}
        }
        if (!selectionData)
		{
			// No selection, goodbye.
            return;
        }
		var nLayers = selectionData.getNumLayers();
		var i;
		var j;
		var k;
		var i2;
		var lastId;
		var allIdsIdentical;
        for (i = 0; i < nLayers; i++)
		{
			/* For every layername in the config file */
			for (j = 0; j < layerNames.length; j++)
			{
				// For every layer in the selection.
				var layerObj = selectionData.getLayer(i);
				if (layerObj.getName() === layerNames[j])
				{
					var activeElements = layerObj.aElements;
					var activeElementsLength = activeElements.length;
					var propertyNames = layerObj.aPropertiesName;
					var propertyNamesLength = propertyNames.length;
					
					for (k = 0; k < propertyNamesLength; k++)
					{
						if (propertyNames[k] === layerAttributes[j])
						{
							/* Only get the id if the id is the same for all the elements.
							 * This is because we only want to open the dialog when there is one object selected.
							 * Once we used to check the length of the activeElements array and we only got the
							 * id when the there was only one object in the array but that did not work in some cases
							 * so now we use this method instead.
							 */
							
							/* Tells weather all the ids are identical in the activeElements array */
							allIdsIdentical = true;
							for (i2 = 0; i2 < activeElementsLength; i2++)
							{
								if (i2 === 0)
								{
									lastId = activeElements[i2][k];
								}
								else
								{
									if (lastId != activeElements[i2][k])
									{
										allIdsIdentical = false;
									}
								}
							}
							
							if (allIdsIdentical)
							{
								id = activeElements[0][k];
								//id = html_entity_decode(activeElements[0][k]);
								//id = OpenLayers.String.unescapeHTML(activeElements[0][k]);
								layerName = layerNames[j];
							}
						}
					}
					
				}
			}
			/* If a feature has ben found then stop the loop */
			if (id)
			{
				break;
			}
        }
		if (id)
		{
			openReport(layerName, id);
		}
	};

	var selectionOn = function ()
	{
		if (mapObject.hasSelection())
		{
			mapObject.getSelection(getId.bind(this));
		}
	};
	
	mapObject.registerForEvent(Fusion.Event.MAP_SELECTION_ON, selectionOn.bind(this));
}
