
	//
	// Utility functions
	//
	function doNothing() {}
	
	
    Array.prototype.sortNum = function() 
    {
       return this.sort( function (a,b) { return a-b; } );
    }


	function detectMobileDevice() 
	{
	  var useragent = navigator.userAgent;
	  var mapdiv = document.getElementById("mapCanvas");
		
	  if (useragent.indexOf('iPhone') >=0 || useragent.indexOf('Android') >=0) 
	  {
		mapdiv.style.width = '100%';
		mapdiv.style.height = '100%';
		
		// Hide the regular Title block, display the map only...
		document.getElementById('headerBlock').style.visibility = 'hidden';
		
		// Hide the regular Menu system, display the map only...
		document.getElementById('menuBlock').style.visibility = 'hidden';
	  } 
	  
	  else
	  {
		// Show the regular Title block...
		document.getElementById('headerBlock').style.visibility = 'visible';
		
		// Show the regular Menu system...
		document.getElementById('menuBlock').style.visibility = 'visible';
	  }
	  
	  
	  // Turn of the NWS Alerts stuff if iPad/iPhone/iPod
	  if (useragent.indexOf('iPad') >= 0 || useragent.indexOf('iPhone') >= 0 || useragent.indexOf('iPod') >= 0)
	  {
		//document.myForm.checkboxWeather_NWS_Alerts.disabled = true;
		//document.myForm.checkboxWeather_NWS_Alerts_Warnings.disabled = true;
		//document.myForm.checkboxWeather_NWS_Alerts_Watches.disabled = true;
		//document.myForm.checkboxWeather_NWS_Alerts_Advisories.disabled = true;
		//document.myForm.checkboxWeather_NWS_Alerts_Statements.disabled = true;
		//document.myForm.checkboxWeather_NWS_Alerts_ShortTermForecasts.disabled = true;
		//document.myForm.checkboxWeather_NWS_Alerts_Other.disabled = true;
		
		//document.getElementById('lblWeather_NWS_Alerts_Unavailable').innerHTML = "Feature unavailable for mobile devices<br />";
	  }
	  
	};
	
	
	function downloadUrl(url, callback) 
	{
	    try
	    {
		    var request = window.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : new XMLHttpRequest;

		    request.onreadystatechange = function() 
		    {
   			    if (request.readyState == 4) 
			    {
     			    request.onreadystatechange = doNothing;
    				
     			    callback(request.responseText, request.status);
   			    }
 		    };

 		    request.open('GET', url, true);
    		
 		    request.send(null);
	    }
	    
		catch (ex1)
		{
			document.getElementById('errorMessage').innerHTML = "function 'downloadUrl' -- Error: " + ex1.description;
		}
	}



	function parseXml(data) 
	{
      if (window.ActiveXObject) 
	  {
        var doc = new ActiveXObject('Microsoft.XMLDOM');
		
        doc.loadXML(data);
		
        return doc;
      }
	  
	  else if (window.DOMParser) 
	  {
        return (new DOMParser).parseFromString(data, 'text/xml');
      }
    }
	
	
	function subString_ExtractPiece(sSourceData, sSearchTagBegin, sSearchTagEnd, bAlsoRemoveSearchTagBegin)
	{
		var iBeginPosition = 0;
		var iEndPosition = 0;
		var sResult = "";

		try
		{
			if (sSourceData != "")
			{
				if (sSearchTagBegin == "")
				{
					iBeginPosition = 0;
				}
				else
				{
					iBeginPosition = sSourceData.toUpperCase().indexOf(sSearchTagBegin.toUpperCase());
				}

				if (iBeginPosition != -1)
				{
					if (sSearchTagEnd == "" |
					   (sSearchTagEnd == "$$" & sSourceData.indexOf("$$", iBeginPosition) == -1) |
					   (sSearchTagEnd == "&&" & sSourceData.indexOf("&&", iBeginPosition) == -1) |
					   (sSearchTagEnd == "" & sSourceData.indexOf("", iBeginPosition) == -1))
					{
						iEndPosition = sSourceData.length;
					}
					else
					{
						iEndPosition = sSourceData.indexOf(sSearchTagEnd, iBeginPosition + sSearchTagBegin.length);
					}

					if (iEndPosition > iBeginPosition)
					{
						sResult = sSourceData.substr(iBeginPosition, iEndPosition - iBeginPosition);

						if (bAlsoRemoveSearchTagBegin)
						{
							sResult = sResult.replace(sSearchTagBegin, "");
						}
						
					}
				}
			}
		}

		catch (ex1)
		{
			sResult = "";
			
			document.getElementById('errorMessage').innerHTML = "function 'subString_ExtractPiece' -- Error: " + ex1.description;
		}

		return sResult;
	}


	function trimString(s)
	{
		// Removes all leading and trailing spaces from a string...
		
		var l=0; 
		var r=s.length -1;
		
		while(l < s.length && s[l] == ' ')
		{
			l++; 
		}
		
		while(r > l && s[r] == ' ')
		{
			r-= 1;     
		}

		return s.substring(l, r + 1);
	} 


    function radiusCircleBasedOnMapZoomLevel()
    {
    
        var zoomLevel = mapCanvas.getZoom();

        var radiusCircleKM = 0;
        
        switch (zoomLevel)
        {
            case 2:
                radiusCircleKM = 128;
                break;

            case 3:
                radiusCircleKM = 64;
                break;
                
            case 4:
                radiusCircleKM = 32;
                break;
                
            case 5:
                radiusCircleKM = 16;
                break;
                
            case 6:
                radiusCircleKM = 8;
                break;
                
            case 7:
                radiusCircleKM = 4;
                break;
                
            case 8:
                radiusCircleKM = 2;
                break;
                
            case 9:
                radiusCircleKM = 1;
                break;
                
            case 10:
                radiusCircleKM = .50;
                break;
                
            case 11:
                radiusCircleKM = .25;
                break;
                
            case 12:
                radiusCircleKM = .125;
                break;
                
            case 13:
                radiusCircleKM = .0625;
                break;
                
            case 14:
                radiusCircleKM = .03125;
                break;
                
            default:
                radiusCircleKM = 0.015625;
                break;
        }

        var radiusCircleMeters = radiusCircleKM * 10000;

        return radiusCircleMeters;
    }

	function randomOffsetCoordinateSlightly() 
	{
		var u1, u2;
		
		u1 = u2 = 0.;
		
		while (u1 * u2 == 0.) 
		{
			u1 = Math.random();
			u2 = Math.random();
		}
		
		var normalizedNumber = Math.sqrt(-2. * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
		
		/*
		  GAUSS method:
		  Generator of pseudo-random number according to a normal distribution with given mean and variance. Normalizes the outcome...
		*/
		 
		return parseFloat(0.5 * normalizedNumber + 1. * 1);
	}
	
	
	// Convert a string to Proper Case (Mixed Case)...
	String.prototype.toProperCase = function () 
	{
		return this.replace(/(\w)(\w*)/g, function (_, i, r) {return i.toUpperCase() + (r != null ? r : "");});
	};

	function convertToMixedCase(oldPhrase)
	{
		
	   	var newPhrase = this.trimString(oldPhrase).toLowerCase();
		
		newPhrase = newPhrase.toProperCase();
		
		newPhrase = newPhrase.replace("Off The ", "Off the ");
		newPhrase = newPhrase.replace(" Of The ", " of the ");
		newPhrase = newPhrase.replace(" Of ", " of ");
		newPhrase = newPhrase.replace(" And ", " and ");
		newPhrase = newPhrase.replace("Near The ", "Near the ");
		newPhrase = newPhrase.replace("In The ", "In the ");
		newPhrase = newPhrase.replace("On The ", "On the ");
		newPhrase = newPhrase.replace("N of ", "North of ");
		newPhrase = newPhrase.replace("S of ", "South of ");
		newPhrase = newPhrase.replace("E of ", "East of ");
		newPhrase = newPhrase.replace("W of ", "West of ");
		newPhrase = newPhrase.replace("Nw of ", "Northwest of ");
		newPhrase = newPhrase.replace("Ne of ", "Northeast of ");
		newPhrase = newPhrase.replace("Sw of ", "Southwest of ");
		newPhrase = newPhrase.replace("Se of ", "Southeast of ");

		return newPhrase;
	}
	

	function convertMetersToFeet(valueMeters)
	{
		// Performs a conversion of meters to feet...
		
		var valueFeet;
	
		valueFeet = Math.round(valueMeters / .3048 * 100) * .01;
		
		return Math.round(valueFeet);
	}
	
	
	function convertMilesToKilometers(valueMiles)
	{
		// Performs a conversion of miles to kilometers...
		var valueKilometers;
	
		valueKilometers = Math.round(valueMiles * 1.609344);
		
		return Math.round(valueKilometers);
	}
	
	
	function formatLocalDateTimeString(dtValue, doIncludeSeconds)
	{
		var dateString = dtValue.toLocaleDateString();;

		// Remove any leading zeroes from the date field...
		dateString = dateString.replace(" 0", " ");
		dateString = dateString.replace(", 20", " 20");
		
		// Process the time value field...
		var hour   = parseInt(dtValue.getHours(), 10);
		var minute = dtValue.getMinutes();
		var second = dtValue.getSeconds();
		var ap = "AM";
		
		if (hour > 11) { ap = "PM"; }
		if (hour > 12) { hour = hour - 12; }
		if (hour == 0) { hour = 12; }
		if (minute < 10) { minute = "0" + minute; }
		if (second < 10) { second = "0" + second; }
		
		var timeStringAMPM = hour + ':' + minute;
		
		if (doIncludeSeconds && second > 0)
		{
			timeStringAMPM += ':' + second;
		}
		
		timeStringAMPM += " " + ap;


		var sLocalDateTime = dateString + " - " + timeStringAMPM;


		// Add the local Time Zone tag value...
		var timeZoneTag = subString_ExtractPiece(dtValue.toString(), "(", ")", true);
		
		timeZoneTag = this.trimString(timeZoneTag);
		
		if (timeZoneTag.indexOf(" ") >= 0)
		{
			var oTimeZoneTag = timeZoneTag.split(' ');
			
			timeZoneTag = "";
			
			for (var iX = 0; iX < oTimeZoneTag.length; iX++) 
			{
				timeZoneTag += oTimeZoneTag[iX].substr(0, 1);
			}
		}
		
		sLocalDateTime += " " + timeZoneTag;


		// Done...
		return sLocalDateTime;
	}
	
	
	function convertDecimalDegrees(values_dms)
  	{

		var value_dms = values_dms.split(' ');

		var precision = 6;  // maximum numbers after decimal point
		var ten_to_n = Math.pow(10, precision);
		
		var neg_re = /[WS]/i
		
    	var direction = value_dms[0];
    	var degrees = parseInt(value_dms[1], 10);
	    var minutes = parseInt(value_dms[2], 10);
    	var seconds = parseInt(value_dms[3], 10);

		var value_dd = (degrees + (minutes / 60.0) + (seconds / 3600.00));
		
		if (direction.substr(0, 1).match(neg_re))
		{
		  value_dd = -value_dd;
		}

		return value_dd;
	}



	//
	// *** MAP FUNCTIONS ***
	//
	
	function clearMessages()
	{
		document.getElementById('menuUpdateMap').innerHTML = "Update Map";
		document.getElementById('menuUpdateMap').style.backgroundColor = "Gainsboro";

		document.getElementById('loadingMessage').style.visibility = 'hidden';
		document.getElementById("loadingMessageText").innerHTML = "";
	}


	// Centers the map on the given coordinates...
	function positionMap(mapLatitude, mapLongitude, mapZoomLevel) 
	{
		try
		{
			// Hide any INFO WINDOW and MARKER CIRCLE that happens to be displaying...
			document.getElementById('overlayInfoWindowBlock').style.visibility = 'hidden';
		    circleSelectedMarker.setMap(null);
			
			
			var locationCoordinates = new google.maps.LatLng(mapLatitude, mapLongitude);
			
			mapCanvas.setCenter(locationCoordinates);
			
			mapCanvas.setZoom(mapZoomLevel);
		}
		
		catch (ex1)
		{
			document.getElementById('errorMessage').innerHTML = "function 'positionMap' -- Error: " + ex1.description;
		}
	}
	
	
	function resetVolcanoCatalog(clearInputFields)
	{
		// Hide any INFO WINDOW and MARKER CIRCLE that happens to be displaying...
		document.getElementById('overlayInfoWindowBlock').style.visibility = 'hidden';
	    circleSelectedMarker.setMap(null);

		
		//for (var iX = 0; iX < markersVolcano_Catalog.length; iX++) 
		//{
		//	markersVolcano_Catalog[iX].setMap(null);
		//}
		
		markersVolcano_Catalog = [];		// Clear array...

        markerclusterVolcanoCatalog.clearMarkers();
        

		isEnabled_VolcanoCatalog = false;

		if (clearInputFields)
		{
			document.myForm.textboxVolcanoCatalog_FindByName.value = "";
			document.myForm.textboxVolcanoCatalog_FindByLocation.value = "";
			document.myForm.dropdownVolcanoCatalog_FindByType.selectedIndex = -1;
	
			document.getElementById('countVolcanoCatalogSearchResults').innerHTML = "Showing 0 volcanoes from the catalog";
		}
	}

	
	function createMarker(markerLatLng, markerIcon, markerIconShadow, markerTitle, markerImportanceLevel, markerContentHtml)
	{
		// Create a MARKER object...
		var markerNew = null;
			
		try
		{
			if (markerIconShadow != null)
			{
				markerNew = new google.maps.Marker({map:mapCanvas, position:markerLatLng, icon:markerIcon, shadow:markerIconShadow, zIndex:markerImportanceLevel});
			}
			else
			{
				markerNew = new google.maps.Marker({map:mapCanvas, position:markerLatLng, icon:markerIcon, zIndex:markerImportanceLevel});
			}
									
			
			// Create a CLICK event to display the INFO WINDOW if we have HTML-formatted content for the marker...
			if (markerContentHtml != "")
			{
			    google.maps.event.addListener(markerNew, 'click', function () {
			        // Reposition the map over the marker...
			        mapCanvas.panTo(markerLatLng);


					// Hide any INFO WINDOW and MARKER CIRCLE that happens to be displaying...
					document.getElementById('overlayInfoWindowBlock').style.visibility = 'hidden';
					circleSelectedMarker.setMap(null);
					
					
			        // MARKER CIRCLE: display a white circle object over the marker; adjust radius based on zoom level...
			        circleSelectedMarker.setMap(null);

			        var radiusCircle = radiusCircleBasedOnMapZoomLevel();

			        circleCenterPosition = markerLatLng;

			        circleSelectedMarker.setOptions({ radius: radiusCircle, center: circleCenterPosition, strokeColor: 'white', fillColor: 'white' });
			        //circleSelectedMarker.bindTo('center', markerNew, 'position');

			        circleSelectedMarker.setMap(mapCanvas);


			        // INFO WINDOW: display the main information window...
			        document.getElementById('overlayInfoWindowBlock').innerHTML = markerContentHtml;
					document.getElementById('overlayInfoWindowBlock').style.visibility = 'visible';
					

			        // Hide the small tooltip info window...
			        tooltip.hide();
			    });
			}
			
			
		   	// Add a listener for the mousemove event
			google.maps.event.addListener(markerNew, 'mouseout', function() 
			{
				tooltip.hide();
			});
			
			google.maps.event.addListener(markerNew, 'mouseover', function() 
			{
				tooltip.show(markerTitle);
			});
		}
		
		catch (ex1)
		{
			document.getElementById('errorMessage').innerHTML = "function 'createMarker' -- Error: " + ex1.description;
		}

		return markerNew;
	}


	function createPolygon(polygonCoordinates, polygonColor, polygonOpacityLevel, polygonTitle, polygonContentHtml, polygonImportanceLevel)
	{
	    var polygonNew;
	    
		try
		{
		
			// Construct the polygon...
			polygonNew = new google.maps.Polygon({paths: polygonCoordinates, strokeColor: polygonColor, strokeOpacity: 0.65, strokeWeight: 1, 
												  fillColor: polygonColor, fillOpacity: polygonOpacityLevel, title:polygonTitle, zIndex:polygonImportanceLevel});
			
		
			// Create a CLICK event to display the INFO WINDOW...
			google.maps.event.addListener(polygonNew, 'click', function() 
			{
		        // INFO WINDOW: display the main information window...
		        document.getElementById('overlayInfoWindowBlock').innerHTML = polygonContentHtml;
				document.getElementById('overlayInfoWindowBlock').style.visibility = 'visible';

                // Hide the small tooltip info window...
				tooltip.hide();
			});
			
			
		   	// Add a listener for the mousemove event
			google.maps.event.addListener(polygonNew, 'mouseout', function() 
			{
				polygonNew.setOptions({strokeColor: polygonColor, strokeOpacity: 0.65, strokeWeight: 1, fillOpacity: polygonOpacityLevel, zIndex:polygonImportanceLevel});
				tooltip.hide();
			});
			
			google.maps.event.addListener(polygonNew, 'mouseover', function() 
			{
				polygonNew.setOptions({strokeColor: '#000000', strokeOpacity: 1.0, strokeWeight: 1.5, fillOpacity: 1.0, zIndex:2000});
				tooltip.show(polygonTitle);
			});
			
		}
		
		catch (ex1)
		{
		    polygonNew = this.createMarker(polygonCoordinates[0], iconMostRecent_Star, null, polygonTitle, 100, polygonContentHtml);
		}

		return polygonNew;
	}
	

	var tooltip=function()
	{
		var id = 'tt';
		var top = 3;
		var left = 3;
		var maxw = 350;
		var speed = 10;
		var timer = 20;
		var endalpha = 85;
		var alpha = 0;
		var tt,t,c,b,h;
		var ie = document.all ? true : false;
		return{
			show:function(v,w)
			{
				if(tt == null)
				{
					tt = document.createElement('div');
					tt.setAttribute('id',id);
					t = document.createElement('div');
					t.setAttribute('id',id + 'top');
					c = document.createElement('div');
					c.setAttribute('id',id + 'cont');
					b = document.createElement('div');
					b.setAttribute('id',id + 'bot');
					tt.appendChild(t);
					tt.appendChild(c);
					tt.appendChild(b);
					document.body.appendChild(tt);
					tt.style.opacity = 0;
					tt.style.filter = 'alpha(opacity=0)';
					document.onmousemove = this.pos;
				}
				tt.style.display = 'block';
				c.innerHTML = v;
				tt.style.width = w ? w + 'px' : 'auto';
				if(!w && ie){
					t.style.display = 'none';
					b.style.display = 'none';
					tt.style.width = tt.offsetWidth;
					t.style.display = 'block';
					b.style.display = 'block';
				}
				if(tt.offsetWidth > maxw){tt.style.width = maxw + 'px'}
				h = parseInt(tt.offsetHeight) + top;
				clearInterval(tt.timer);
				tt.timer = setInterval(function(){tooltip.fade(1)},timer);
			},
			pos:function(e)
			{
				var u = ie ? event.clientY + document.documentElement.scrollTop : e.pageY;
				var l = ie ? event.clientX + document.documentElement.scrollLeft : e.pageX;
				tt.style.top = (u - h) + 'px';
				tt.style.left = (l + left) + 'px';
			},
			fade:function(d)
			{
				var a = alpha;
				if((a != endalpha && d == 1) || (a != 0 && d == -1))
				{
					var i = speed;
					if(endalpha - a < speed && d == 1){
						i = endalpha - a;
					}else if(alpha < speed && d == -1){
						i = a;
					}
					alpha = a + (i * d);
					tt.style.opacity = alpha * .01;
					tt.style.filter = 'alpha(opacity=' + alpha + ')';
				}
				else
				{
					clearInterval(tt.timer);
					if(d == -1){tt.style.display = 'none'}
				}
			},
			hide:function()
			{
				clearInterval(tt.timer);
				tt.timer = setInterval(function(){tooltip.fade(-1)},timer);
			}
		};
	}();
	

