/******************************************************************************
 * @file:  ymmaps.js
 * @brief: This JavaScript file describes the class that represents the YMmaps
 *         widget
 * @author Kyle Overby
 *
 *  $Revision: YMmaps_Development_koverby/16 $
 *  $Date: 2008/12/05 23:34:16 $
 *  $LastEditedBy: koverby $
 *
 * $Copyright: 2007-2008, Southwest Research Institute.  All rights reserved. $
 *****************************************************************************/

/******************************************************************************
 *  $Log: ymmaps.js $
 *  Revision YMmaps_Development_koverby/16 2008/12/05 23:34:16 koverby
 *     Added support for retrieving line features from
 *     the database and drawing them to the map.
 *
 *  Revision YMmaps_Development_koverby/15 2008/12/05 01:59:25 koverby
 *     Added a new overlay titled Central Geology.
 *
 *  Revision YMmaps_Development_koverby/14 2008/11/04 21:15:50 koverby
 *     Added the distance tool and fixed some minor
 *     typos.
 *
 *  Revision YMmaps_Development_koverby/13 2008/10/20 22:40:28 koverby
 *     Added calls to integrate the new search tool.
 *
 *  Revision YMmaps_Development_koverby/12 2008/09/18 22:50:31 koverby
 *     Fixed an issue where a pop-up window would
 *     not close when a well marker was unselected.
 *
 *  Revision YMmaps_Development_koverby/11 2008/08/30 02:54:16 koverby
 *     Fixed some minor display issues in IE6/IE7.
 *
 *     Made faults and labels not-transparent.
 *
 *  Revision YMmaps_Development_koverby/10 2008/08/29 01:02:05 koverby
 *     Changed some style formatting, moved to a new
 *     color them.  Change selection behavior.
 *
 *  Revision YMmaps_Development_koverby/9 2008/08/25 23:01:29 koverby
 *     Added the first revision of a transparency
 *     slider.  Updated the style based upon input
 *     from Mike Meadows.  Fixed some minor spacing
 *     issues.
 *
 *  Revision YMmaps_Development_koverby/8 2008/08/13 03:23:38 koverby
 *     Completely rewrote how features and the feature
 *     list is handled to allow greater flexiblity in
 *     how features ore organized and displayed.
 *
 *  Revision YMmaps_Development_koverby/7 2008/08/02 20:59:42 koverby
 *     Added a control that displays the mouses current
 *     latitude and longitude.  Add tooltips via the
 *     alt tag for the various cusotm layers.
 *
 *  Revision YMmaps_Development_koverby/6 2008/08/02 03:06:33 koverby
 *     Added pop-ups for when wells are clicked as
 *     well as reworking how the size of the various
 *     widgets is determined.
 *
 *  Revision YMmaps_Development_koverby/5 2008/07/23 00:38:52 koverby
 *     Added a zoom call when a specific well is panned
 *     to.  Also added a call to set the height
 *     of the sidebar based upon the map.
 *
 *  Revision YMmaps_Development_koverby/4 2008/07/14 03:11:57 koverby
 *     Added functions that handle events when a
 *     classification grouping is clicked and when
 *     a individual well is clicked.
 *
 *  Revision YMmaps_Development_koverby/3 2008/07/04 00:45:50 koverby
 *     Added the ability to toggle the display of wells
 *     on and off by group.
 *
 *  Revision YMmaps_Development_koverby/2 2008/06/26 01:10:01 koverby
 *     Readded the disabled YM 100k layer as well
 *     as adjusting the transparency of some of the
 *     other layers.
 *
 *
 *****************************************************************************/

/** Global Varibles for adjusting width/height on full screen */
var widthAdjust = 0;
var heightAdjust = 0;

/** Code for all browser except IE */
if( typeof( window.innerWidth ) == 'number' ) {
    widthAdjust = 12;
    heightAdjust = 14;
}

/** Code for IE6+ in standards compliant mode */
else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
    widthAdjust = 18;
    heightAdjust = 20;
}

/** Code for older IE or non-compliant mode */
else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
    widthAdjust = 20;
    heightAdjust = 20;
}

 /*****************************************************************************
 * CLASS:YMmapsWidget <br>
 *
 * This Prototype class is used to create a widget that
 * uses the GoogleMaps API to create an interactive map
 * widget.
 *
 * @author Kyle Overby
 * @since June 24, 2008
 * @version $Revision: YMmaps_Development_koverby/16 $
 *****************************************************************************/
YMmapsWidget = Class.create({

    /** Constructor */
    initialize: function(){

        /** Main Map Object */
        this.map = null;

        /** Create the Distance Tool */
        this.distanceTool = new DistanceTool();

        /** Create the Search Tool */
        this.searchTool = new SearchTool();

        /** Create Hash for storing displayed Map Features */
        this.displayedFeatures = new Hash();

        /** Create Hash for storing displayed Search Features */
        this.searchFeatures = new Hash();

        /** Create Array for storing line graphics */
        this.lineGraphics = new Array();

        /** Create the polyline encoder */
        this.polylineEncoder = new PolylineEncoder();

        /** Create the Map and Sidebar Panels */
        this.mapPanel = Builder.node("div", {className:"ymmapsMapPanel"});
        this.sideBarPanelHeader = Builder.node("div", {className:"ymmapsMapSideBarPanelHeader"});
        this.sideBarPanelHeader.innerHTML = "Feature List";

        this.featureList = Builder.node("div", {className:"ymmapsFeatureList"});

        this.showReferencesButton = Builder.node("div", {className:"ymmapsBottomPanelButton"});
        this.showReferencesButton.title = "Click to show Yucca Mountain Maps References";
        this.showReferencesButton.innerHTML = "Show Yucca Mountain Maps References";

        this.showReferencesButton.clickedEvent = function() {
          openNewWindow('/references/map_references.pdf','References_for_YMmaps');
        }
        Event.observe(this.showReferencesButton, "click", this.showReferencesButton.clickedEvent);

        this.showLegendButton = Builder.node("div", {className:"ymmapsBottomPanelButton"});
        this.showLegendButton.title = "Click to show legend for currently selected Overlay";
        this.showLegendButton.innerHTML = "Show Legend for Currently Selected Overlay";

        this.showLegendButton.clickedEvent = function() {
            var currentMapName = this.map.getCurrentMapType().getName();

            if (currentMapName == "Aeromagnetic")
                openNewWindow('/legends/aeromagnetic_legend.pdf','Legend_for_Aeromagnetic');

            else if (currentMapName == "Beatty Bedrock Geology")
                openNewWindow('/legends/beatty_bedrock_geology_legend.pdf','Legend_for_Beatty_Bedrock_Geology');

            else if (currentMapName == "Yucca Mountain Geology")
                openNewWindow('/legends/yucca_mountain_geology.pdf','Legend_for_Yucca_Mountain_Geology');

            else if (currentMapName == "Faults")
                openNewWindow('/legends/faults.pdf','Legend_for_Faults');

            else if (currentMapName == "Central Block Bedrock Geology")
                openNewWindow('/legends/day_geology.pdf','Legend_for_Day_Geology');

            else alert("No Legend Available for Current Overlay");
        }.bind(this);

        Event.observe(this.showLegendButton, "click", this.showLegendButton.clickedEvent);

        this.sideBarPanelBottomPanel = Builder.node("div", {className:"ymmapsMapSideBarPanelBottomPanel"}, [this.showReferencesButton, this.showLegendButton]);

        this.sideBarPanelContent = Builder.node("div", {className:"ymmapsMapSideBarPanelContnet"}, [this.featureList,  this.sideBarPanelBottomPanel]);
        this.sideBarPanel = Builder.node("div", {className:"ymmapsMapSideBarPanel"}, [this.sideBarPanelHeader, this.sideBarPanelContent]);
        this.sideBarPanel.hide();

        this.toolContainer = Builder.node("div", {className:"ymmapsContainer"}, [this.mapPanel, this.sideBarPanel]);

        /** Map Properites */
        this.defaultZoom = 10;
        this.minZoom = 9;
        this.maxZoom = 18;
        this.aberration = 0.2;
        this.defaultCenter = new GLatLng(36.709313, -116.588334);
        this.maxSW = new GLatLng(36.046878280461684, -117.96295166015624);
        this.maxNE = new GLatLng(37.36797435878155, -115.21636962890624);
        this.allowedBounds = new GLatLngBounds(this.maxSW, this.maxNE);
    },

    /**************************************************************************
     * This sets up the YMmaps Widget Google Map's map.
     *************************************************************************/
    initMap:function() {

        /** Make sure the browser is GMap compatible before continuing */
        if (GBrowserIsCompatible()) {

            /** Create a new map using the swagmap div */
            this.map = new GMap2(this.mapPanel);

            /** Associate Tool Container, Map Panel and Map Sidebar Panel
             * with GMap so the  can be accessed by custom controls */
            this.map.toolContainer = this.toolContainer;
            this.map.mapPanel = this.mapPanel;
            this.map.sideBarPanel = this.sideBarPanel;
            this.map.featureList = this.featureList;

            /** Set the map center of the Yukon Mountains at Zoom Level 10 */
            this.map.setCenter(new GLatLng(36.709313, -116.588334), 10);

            /** Add controls */
            var customMenuMapControl = new GMenuMapTypeControl()
            customMenuMapControl.getDefaultPosition = function() {
                return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(80, 27));
            }

            this.map.addControl(new GLargeMapControl());
            this.map.addControl(customMenuMapControl);
            this.map.addControl(new GScaleControl());

            if (this.searchTool != null) {

                this.map.addControl(new ToggleSearchToolControl(this.mapPanel));
                var toggleSearchButton = $("searchToggleButton");

                /** Set control behvaior */
                GEvent.addDomListener(toggleSearchButton, "click", function() {
                    if (this.searchTool.searchToolWindow.visible()) {
                        this.searchTool.searchToolWindow.hide();

                        /** Clear the search results */
                        this.searchTool.clearSearchResults();

                        this.searchTool.searchToolWindow.setStyle({height: "120px"});
                        toggleSearchButton.innerHTML = "Open Search Tool";
                        toggleSearchButton.title = "Click to Open Search Tool";
                        toggleSearchButton.style.border = "2px outset";
                    }
                    else  {
                        this.searchTool.searchToolWindow.show();
                        toggleSearchButton.innerHTML = "Close Search Tool";
                        toggleSearchButton.title = "Click to Close Search Tool";
                        toggleSearchButton.style.border = "2px inset";
                    }
                }.bind(this));
            }

            if (this.distanceTool != null) {
                this.distanceTool.setMap(this.map);
                this.map.addControl(new ToggleDistanceToolControl(this.mapPanel));
                var toggleDistanceButton = $("distanceToggleButton");

                /** Set control behvaior */
                GEvent.addDomListener(toggleDistanceButton, "click", function() {
                    if (this.distanceTool.distanceToolWindow.visible()) {
                        this.distanceTool.distanceToolWindow.hide();

                        /** Remove event listener */
                        this.distanceTool.closeDistanceTool();

                        this.distanceTool.distanceToolWindow.setStyle({height: "140px"});
                        toggleDistanceButton.innerHTML = "Open Measure Tool";
                        toggleDistanceButton.title = "Click to Open Measure Tool";
                        toggleDistanceButton.style.border = "2px outset";
                    }
                    else  {
                        this.distanceTool.distanceToolWindow.show();
                        toggleDistanceButton.innerHTML = "Close Measure Tool";
                        toggleDistanceButton.title = "Click to Close Measure Tool";
                        toggleDistanceButton.style.border = "2px inset";

                        /** Remove event listener */
                        this.distanceTool.openDistanceTool();
                    }
                }.bind(this));
            }

            this.map.addControl(new ToggleFeaureListControl(this.mapPanel));
            this.map.addControl(new MouseCursorPosistionControl(this.mapPanel));
            this.map.addControl(new TransparencySliderControl(this.mapPanel));

            /** Enable mouse wheel scrolling */
            this.map.enableScrollWheelZoom();

            /** Enable continuous zooming */
            this.map.enableContinuousZoom();

            /** Load Layers into the Map */
            this.loadLayers();

            /** Load the list of Level 1 Groups */
            this.loadLevel1Groups();

            /** Restrict Zooming */
            this.setZoomRestrictions();

            /** TODO Bound Checking Code seems to eats massive CPU */
            /** Add Listener To Restrict Panning */
            //GEvent.addListener(this.map, "move", function(){
            //    this.checkBounds();
            //}.bind(this));

            /** Start monitoring when it is safe to adjust the control style */
            setTimeout(function(){this.adjustControlStyle();}.bind(this),10);

            /** Attach a window resize event listener */
            this.windowReizeEvent = this.setWindowFullscreen.bindAsEventListener(this);
            Event.observe(window, "resize", this.windowReizeEvent);

            /** Set the display to full screen */
            this.setWindowFullscreen();

            /** Associate the search tool with this map tool */
           this.searchTool.setMapTool(this);
        }

        /** If browser was not compatible, display an error message */
        if (this.map == null)
            this.mapDivElement.innerHTML = "Your browser is not Google Maps Comptabile";
    },

    /**************************************************************************
     * This function is called as part of the setup process.  It makes
     * an AJAX call to a Java servelt on the backend which returns a
     * JSON response containg all the features to display.
    **************************************************************************/
    loadFeatures:function() {

        new Ajax.Request("featurelist", {
            method: "get",
            requestHeaders: "application/json",
            asynchronous: true,
            onSuccess: function(transport){

                var response = transport.responseText.evalJSON();

                if (response.featureList == "FailedDB") {
                    alert("Error Loading Features from Database");
                }

                else if (response.featureList == "FailedJSON") {
                    alert("Error Creaint JSON for FailedDB ");
                }

                else if (response.featureList == "NoFeatures") {
                    alert("No Features in Datbase");
                }

                else if (response.featureList == "Success") {
                    response.features.each(function (feature) {
                        this.addFeatureToMap(feature, "#FF0000");
                    }.bind(this));
                }
            }.bind(this)
        });
    },

    /**************************************************************************
     * This function is called each time a new Feature is added to the map.
     *************************************************************************/
    addFeatureToMap:function(feature, color) {

        /** Feature is of type POINT */
        if (feature.type == "POINT") {

            /** Create custom marker graphic icon */
            var iconOptions = {};
            iconOptions.width = 32;
            iconOptions.height = 32;
            iconOptions.primaryColor = color;
            iconOptions.cornerColor = "#FFFFFF";
            iconOptions.strokeColor = "#000000";
            var customIcon = MapIconMaker.createMarkerIcon(iconOptions);

            var featurePoint = new GLatLng(feature.latitude, feature.longitude);

            if (color == "#0000FF")
                var featureOptions = {title:feature.name, icon:customIcon, zIndexProcess:function (marker,b){return 1;}};
            else var featureOptions = {title:feature.name, icon:customIcon, zIndexProcess:function (marker,b){return 0;}};

            /** Create a GMarker for this point */
            feature.graphic = new GMarker(featurePoint, featureOptions);

            /** Add the well marker to the overlay */
            this.map.addOverlay(feature.graphic);

            /** If the marker is a search marker, add it to the search list
             * otherwise add it to the regular feature list of displayed wells */
            if (color == "#0000FF")
                this.searchFeatures.set(feature.name, feature);
            else this.displayedFeatures.set(feature.name, feature);
        }

        /** Feature is of type LINE */
        else if (feature.type == "LINE") {


            for (var i = 0; i < feature.lines.length; i++) {

                /** Get the first line for now */
                var linePoints = feature.lines[i];

                /** Create an array of GLatLongs */
                var points = new Array(linePoints.length / 2);

                for (var j = 0; j < points.length; j++) {
                    points[j] = new GLatLng(linePoints[j * 2 + 1], linePoints[j * 2]);
                }

                /** Get the style information */
                var styleSettings = feature.style.split(":");
                var color = styleSettings[0];
                var weight = styleSettings[1];
                var opacity = styleSettings[2];

                /** Create a new object to store the polyline */
                var lineGraphic = new Object();

                /** Set the graphics feature Id to the featureId of the feature */
                lineGraphic.featureId = feature.featureId;

                /** Create a GPolyline using the encoder */
                lineGraphic.graphic = this.polylineEncoder.dpEncodeToGPolyline(points, color, weight, opacity);

                /** Hide the line by default */
                lineGraphic.graphic.hide();

                /** Add the polyline to the overlay */
                this.map.addOverlay(lineGraphic.graphic);

                /** Add the line features to the line features list */
                this.lineGraphics.push(lineGraphic);
            }
        }
    },

    /**************************************************************************
     * This function is called to remove all the features added by the
     * search tool.
     *************************************************************************/
    removeSearchFeatures:function() {

        /** Remove all the features by name */
        this.searchFeatures.each(function(pair){

            /** If this item has a valid graphic, remove it */
            if (pair.value.graphic != null)
                this.map.removeOverlay(pair.value.graphic);

            this.searchFeatures.unset(pair.key);

        }.bind(this));
    },

    /**************************************************************************
     * This function is called to remove a set of features by name
     *************************************************************************/
    removeFeaturesByName:function(featureList) {

        /** Remove all the features by name */
        featureList.each(function(featureName){
            var feature = this.displayedFeatures.get(featureName);

            /** If this item has a valid graphic, remove it */
            if (feature.graphic != null)
                this.map.removeOverlay(feature.graphic);

            this.displayedFeatures.unset(featureName);
        }.bind(this));
    },

    /**************************************************************************
     * This function is called each time a new Feature is added to the sidebar.
     *************************************************************************/
    addFeatureToSideBar:function(feature, parentGroup) {

        var featureElementText = Builder.node("div");
        featureElementText.innerHTML = feature.name;
        var featureElementGraphic = Builder.node("div", {className:"ymmapsFeatureImage"});
        var featureElement = Builder.node("div", {className:"ymmapsFeature"}, [featureElementText, featureElementGraphic]);

        /** If the feature is a Well add the event listener for when the well type is clicked */
        if (feature.classification == "Well") {

            featureElement.title = "Click to pan map to selection";

            /** Create an event listener for when the feature is clicked in the feature list */
            featureElement.featureElementClickedEvent = this.selectFeature.bindAsEventListener(this, feature, featureElement, parentGroup);
            Event.observe(featureElement, "click", featureElement.featureElementClickedEvent);

            /** Create an event listner for when the feature is clicked */
            GEvent.addListener(feature.graphic, "click", function(){this.selectFeature(this, feature, featureElement, parentGroup)}.bind(this));

            /** Create an event listner for when the info window for the feature is closed */
            GEvent.addListener(feature.graphic, "infowindowclose", function(){this.deselectFeature(this, feature, featureElement, parentGroup)}.bind(this));
        }

        /** If the feature is a line feature add the event listener for when the well type is clicked */
        else if (feature.classification == "Line Feature") {

            featureElement.title = "Click to add/remove line feature";

            /** Create an event listener for when the feature is clicked in the feature list */
            featureElement.featureElementClickedEvent = this.toggleLineFeature.bindAsEventListener(this, feature, featureElement);
            Event.observe(featureElement, "click", featureElement.featureElementClickedEvent);
        }

        parentGroup.groupContainer.appendChild(featureElement);
    },

    /**************************************************************************
     * This function is called as part of the setup process. It makes
     * an AJAX call that returns a list of all the Feature Level 1 Groups that
     * are currently stored in the database.
    **************************************************************************/
    loadLevel1Groups:function() {
            new Ajax.Request("featurelist", {
            method: "get",
            requestHeaders: "application/json",
            parameters: "action=getlevel1groups",
            asynchronous: true,
            onSuccess: function(transport){

                var response = transport.responseText.evalJSON();

                if (response.level1Groups == "FailedDB") {
                    alert("Error Loading Feature Level 1 Groups from Database");
                }

                else if (response.level1Groups == "NoGroups") {
                    alert("No Feature Level 1 Groups in Datbase");
                }

                else if (response.level1Groups == "Success") {
                    response.groups.each(function (group) {
                        this.addLevel1Group(group);
                    }.bind(this));
                }
            }.bind(this)
        });
    },

    /**************************************************************************
     * This function is called each time a new Features Level 1 Group is added
     * to the map.
     *************************************************************************/
    addLevel1Group:function(group) {
        var level1Group = new Object();
        level1Group.name = group;
        level1Group.displayLabelText = Builder.node("div");
        level1Group.displayLabelText.innerHTML = level1Group.name;
        level1Group.displayLabelArrow = Builder.node("div", {className:"ymmapsUpArrowLevel1Group"});
        level1Group.displayLabel = Builder.node("div", {className:"ymmapsLevel1Group"}, [level1Group.displayLabelText, level1Group.displayLabelArrow]);
        level1Group.groupContainer = Builder.node("div", {className:""}, [level1Group.displayLabel]);
        level1Group.groupContainer.title = "Click to expand/collapse List";

        /** Add the event listener for when the well type is clicked */
        level1Group.groupClickedEvent = this.toggleLevel1Group.bindAsEventListener(this, level1Group);
        Event.observe(level1Group.displayLabel, "click", level1Group.groupClickedEvent);

        this.featureList.appendChild(level1Group.groupContainer);
    },

    /**************************************************************************
     * This function is called when one of the Feature Level 1 Groups is
     * clicked to toggle the display of the associated Feature Level 2 Groups
     * under it.
     *************************************************************************/
    toggleLevel1Group: function(event, level1Group) {

        if (level1Group.displayLabel.hasClassName("ymmapsLevel1GroupSelected")) {

            level1Group.displayLabel.removeClassName("ymmapsLevel1GroupSelected");

            /** Change the direction of the arrows */
            level1Group.displayLabelArrow.removeClassName("ymmapsDownArrowLevel1Group");
            level1Group.displayLabelArrow.addClassName("ymmapsUpArrowLevel1Group");

            this.removeFeaturesByLevel1Group(level1Group.name);

            /** Check to see if any level 2 Groups need to be removed */
            if (level1Group.groupContainer.childElements().length > 1) {
                while (level1Group.groupContainer.childElements().length > 1 )
                    level1Group.groupContainer.removeChild(level1Group.groupContainer.childElements()[1]);
            }
        }
        else {
            level1Group.displayLabel.addClassName("ymmapsLevel1GroupSelected");
            level1Group.displayLabelArrow.addClassName("ymmapsDownArrowLevel1Group");

            new Ajax.Request("featurelist", {
                method: "get",
                requestHeaders: "application/json",
                parameters: "action=getlevel2groups&level1=" + level1Group.name,
                asynchronous: true,
                onSuccess: function(transport){
                    var response = transport.responseText.evalJSON();

                    if (response.level2Groups == "FailedDB") {
                        alert("Error Loading Feature Level 2 Groups from Database");
                    }

                    else if (response.level2Groups == "NoGroups") {
                        this.addFeaturesByLevel1Group(level1Group);
                    }

                    else if (response.level2Groups == "Success") {
                       response.groups.each(function (group) {
                           this.addLevel2GroupToLevel1Group(level1Group, group);
                       }.bind(this));
                    }
                }.bind(this)
            });
        }
    },

    /**************************************************************************
     * This function is called to add features belonging to a given Feature
     * Level 2 Group.
     *************************************************************************/
    addFeaturesByLevel1Group:function(level1Group) {

        new Ajax.Request("featurelist", {
            method: "get",
            requestHeaders: "application/json",
            parameters: "action=getfeatures&level1=" + level1Group.name,
            asynchronous: true,
            onSuccess: function(transport){
                var response = transport.responseText.evalJSON();

                if (response.featureList == "FailedDB") {
                    alert("Error Loading Features from Database");
                }

                else if (response.featureList == "NoWells") {
                   alert("No Features for given Group in Datbase");
                }

                else if (response.featureList == "Success") {
                   response.features.each(function (feature) {
                       this.addFeatureToMap(feature, "#FF0000");
                       this.addFeatureToSideBar(feature, level1Group);
                   }.bind(this));
                }
            }.bind(this)
        });

    },

    /**************************************************************************
     * This function is called to remove features of a given Feature Level 1
     * Group.
     *************************************************************************/
    removeFeaturesByLevel1Group:function(level1Group) {

        /** If the group being removed is the special line features group */
        if (level1Group == "LINE FEATURES") {
            /** Iterate through all the line features */
            while (this.lineGraphics.length != 0) {

                /** Get a line features from the list */
                var lineGraphic = this.lineGraphics.pop();

                /** Remove it from the map */
                this.map.removeOverlay(lineGraphic.graphic);
            }
        }

        else {
            /** Itearate over all the displayed wells */
            this.displayedFeatures.each(function(pair){

                /** Remove those features whose group matches the given group
                 * and are not part of the current search results
                 */
                if (pair.value.level1Group == level1Group) {

                    /** If this item has a valid graphic, remove it */
                    if (pair.value.graphic != null)
                        this.map.removeOverlay(pair.value.graphic);
                        this.displayedFeatures.unset(pair.key);
                    }
            }.bind(this));
        }
    },

    /**************************************************************************
     * This function is called to add Feature Level 2 Groups under the given
     * Feature Level 1 Group.
     *************************************************************************/
    addLevel2GroupToLevel1Group:function(level1Group, group) {

        var level2Group = new Object();
        level2Group.name = group;
        level2Group.displayLabelText = Builder.node("div");
        level2Group.displayLabelText.innerHTML = level2Group.name;
        level2Group.displayLabelArrow = Builder.node("div", {className:"ymmapsUpArrowLevel2Group"});
        level2Group.displayLabel = Builder.node("div", {className:"ymmapsLevel2Group"},[level2Group.displayLabelText, level2Group.displayLabelArrow]);
        level2Group.groupContainer = Builder.node("div", {className:""}, [level2Group.displayLabel]);
        level2Group.groupContainer.title = "Click to expand/collapse List";

        /** Add the event listener for when the well type is clicked */
        level2Group.groupClickedEvent = this.toggleLevel2Group.bindAsEventListener(this, level1Group, level2Group);
        Event.observe(level2Group.displayLabel, "click", level2Group.groupClickedEvent);

        level1Group.groupContainer.appendChild(level2Group.groupContainer);
    },

    /**************************************************************************
     * This function is called when one of the level 2 groups in the feature
     * list is clicked.
     *************************************************************************/
    toggleLevel2Group: function(event, level1Group, level2Group) {

        if (level2Group.displayLabel.hasClassName("ymmapsLevel2GroupSelected")) {

            level2Group.displayLabel.removeClassName("ymmapsLevel2GroupSelected");

             /** Change the direction of the arrows */
            level2Group.displayLabelArrow.removeClassName("ymmapsDownArrowLevel2Group");
            level2Group.displayLabelArrow.addClassName("ymmapsUpArrowLevel2Group");

            this.removeFeaturesByLevel1GroupAndLevel2Group(level1Group.name, level2Group.name);

            /** Check to see if any level 2 groups need to be removed */
            if (level2Group.groupContainer.childElements().length > 1) {
                while (level2Group.groupContainer.childElements().length > 1 )
                    level2Group.groupContainer.removeChild(level2Group.groupContainer.childElements()[1]);
            }
        }
        else {
            level2Group.displayLabel.addClassName("ymmapsLevel2GroupSelected");
            level2Group.displayLabelArrow.addClassName("ymmapsDownArrowLevel2Group");

            new Ajax.Request("featurelist", {
                method: "get",
                requestHeaders: "application/json",
                parameters: "action=getlevel3groups&level1=" + level1Group.name + "&level2=" + level2Group.name,
                asynchronous: true,
                onSuccess: function(transport){
                    var response = transport.responseText.evalJSON();

                    if (response.level3Groups == "FailedDB") {
                        alert("Error Loading Feature Level 3 Groups from Database");
                    }

                    else if (response.level3Groups == "NoGroups") {
                        this.addFeaturesByLevel1GroupAndLevel2Group(level1Group, level2Group);
                    }

                    else if (response.level3Groups == "Success") {
                       response.groups.each(function (group) {
                           this.addLevel3GroupToLevel2Group(level1Group, level2Group, group);
                       }.bind(this));
                    }
                }.bind(this)
            });
        }
    },

    /**************************************************************************
     * This function is called to remove features of a given Feature Level 2
     * Group under the given Feature Level 1 Group from the map.
     *************************************************************************/
    removeFeaturesByLevel1GroupAndLevel2Group:function(level1Group, level2Group) {

        /** Itearate over all the displayed wells */
        this.displayedFeatures.each(function(pair){

            /** Remove those features whose groups match the given groups */
            if (pair.value.level1Group == level1Group
                && pair.value.level2Group == level2Group){

                /** If this item has a valid graphic, remove it */
                if (pair.value.graphic != null)
                    this.map.removeOverlay(pair.value.graphic);

                this.displayedFeatures.unset(pair.key);
            }
        }.bind(this));
    },

    /**************************************************************************
     * This function is called to add features of a given Feature Level 2
     * Group under the given Feature Level 1 group to the map.
     *************************************************************************/
    addFeaturesByLevel1GroupAndLevel2Group:function(level1Group, level2Group) {

        new Ajax.Request("featurelist", {
            method: "get",
            requestHeaders: "application/json",
            parameters: "action=getfeatures&level1=" + level1Group.name + "&level2=" + level2Group.name,
            asynchronous: true,
            onSuccess: function(transport){
                var response = transport.responseText.evalJSON();

                if (response.featureList == "FailedDB") {
                    alert("Error Loading Features from Database");
                }

                else if (response.featureList == "NoFeatures") {
                   alert("No Features for given Groups in Datbase");
                }

                else if (response.featureList == "Success") {
                   response.features.each(function (feature) {
                       this.addFeatureToMap(feature, "#FF0000");
                       this.addFeatureToSideBar(feature, level2Group);
                   }.bind(this));
                }
            }.bind(this)
        });

    },

    /**************************************************************************
     * This function is called to add Feature Level 2 Groups under the given
     * Feature Level 1 Group.
     *************************************************************************/
    addLevel3GroupToLevel2Group:function(level1Group, level2Group, group) {

        var level3Group = new Object();
        level3Group.name = group;
        level3Group.displayLabelText = Builder.node("div");
        level3Group.displayLabelText.innerHTML = level3Group.name;
        level3Group.displayLabelArrow = Builder.node("div", {className:"ymmapsUpArrowLevel3Group"});
        level3Group.displayLabel = Builder.node("div", {className:"ymmapsLevel3Group"},[level3Group.displayLabelText, level3Group.displayLabelArrow]);
        level3Group.groupContainer = Builder.node("div", {className:""}, [level3Group.displayLabel]);
        level3Group.groupContainer.title = "Click to expand/collapse List";

        /** Add the event listener for when the well type is clicked */
        level3Group.groupClickedEvent = this.toggleLevel3Group.bindAsEventListener(this, level1Group, level2Group, level3Group);
        Event.observe(level3Group.displayLabel, "click", level3Group.groupClickedEvent);

        level2Group.groupContainer.appendChild(level3Group.groupContainer);
    },

    /**************************************************************************
     * This function is called when one of the level 3 groups in the feature
     * list is clicked.
     *************************************************************************/
    toggleLevel3Group: function(event, level1Group, level2Group, level3Group) {

        if (level3Group.displayLabel.hasClassName("ymmapsLevel3GroupSelected")) {

            level3Group.displayLabel.removeClassName("ymmapsLevel3GroupSelected");

             /** Change the direction of the arrows */
            level3Group.displayLabelArrow.removeClassName("ymmapsDownArrowLevel3Group");
            level3Group.displayLabelArrow.addClassName("ymmapsUpArrowLevel3Group");

            this.removeFeaturesByLevel1GroupAndLevel2GroupAndLevel3Group(level1Group.name, level2Group.name, level3Group.name);

            /** Check to see if any level 3 groups need to be removed */
            if (level3Group.groupContainer.childElements().length > 1) {
                while (level3Group.groupContainer.childElements().length > 1 )
                    level3Group.groupContainer.removeChild(level3Group.groupContainer.childElements()[1]);
            }
        }
        else {
            level3Group.displayLabel.addClassName("ymmapsLevel3GroupSelected");
            level3Group.displayLabelArrow.addClassName("ymmapsDownArrowLevel3Group");
            this.addFeaturesByLevel1GroupAndLevel2GroupAndLevel3Group(level1Group, level2Group, level3Group);
        }
    },

    /**************************************************************************
     * This function is called to remove features of a given Feature Level 3
     * Group under the given Feature Level 2 Group from the map.
     *************************************************************************/
    removeFeaturesByLevel1GroupAndLevel2GroupAndLevel3Group:function(level1Group, level2Group, level3Group) {

        /** Itearate over all the displayed wells */
        this.displayedFeatures.each(function(pair){

            /** Remove those features whose groups match the given groups */
            if (pair.value.level1Group == level1Group
                && pair.value.level2Group == level2Group
                && pair.value.level3Group == level3Group) {

                /** If this item has a valid graphic, remove it */
                if (pair.value.graphic != null)
                    this.map.removeOverlay(pair.value.graphic);

                this.displayedFeatures.unset(pair.key);
            }
        }.bind(this));
    },

    /**************************************************************************
     * This function is called to add features of a given Feature Level 3
     * Group under the given Feature Level 2 group to the map.
     *************************************************************************/
    addFeaturesByLevel1GroupAndLevel2GroupAndLevel3Group:function(level1Group, level2Group, level3Group) {

        new Ajax.Request("featurelist", {
            method: "get",
            requestHeaders: "application/json",
            parameters: "action=getfeatures&level1=" + level1Group.name + "&level2=" + level2Group.name + "&level3=" + level3Group.name,
            asynchronous: true,
            onSuccess: function(transport){
                var response = transport.responseText.evalJSON();

                if (response.featureList == "FailedDB") {
                    alert("Error Loading Features from Database");
                }

                else if (response.featureList == "NoFeatures") {
                   alert("No Features for given Groups in Datbase");
                }

                else if (response.featureList == "Success") {
                   response.features.each(function (feature) {
                       this.addFeatureToMap(feature, "#FF0000");
                       this.addFeatureToSideBar(feature, level3Group);
                   }.bind(this));
                }
            }.bind(this)
        });

    },

    /**************************************************************************
     * This function is called when one of the wells in the Feature List is
     * clicked on the map, which casues the map to pan to it and show the
     * well's pop-up.
     *************************************************************************/
    selectFeature: function(event, feature, featureElement, parentGroup) {

        /** Remove the selected class from any other wells */
        for (var i=0; i < parentGroup.groupContainer.childElements().length; i++)
           if (parentGroup.groupContainer.childElements()[i].hasClassName("ymmapsFeatureSelected") &&
               parentGroup.groupContainer.childElements()[i] != featureElement)
               parentGroup.groupContainer.childElements()[i].removeClassName("ymmapsFeatureSelected");

        /** Check if the parent group is the search tool container */
        if (parentGroup == this.searchTool.searchToolResultsList)
            var feature = this.searchFeatures.get(feature.name);

        /** Otherwise assume the regular feature list */
        else var feature = this.displayedFeatures.get(feature.name);

        /** Check to see if any wells need to be removed */
        if (featureElement.hasClassName("ymmapsFeatureSelected")) {
            featureElement.removeClassName("ymmapsFeatureSelected");

            feature.graphic.closeInfoWindow();
        }
        else {
            featureElement.addClassName("ymmapsFeatureSelected");

            /** If not atleast at zoom level 12, zoom in to 12 */
            if (this.map.getZoom() < 12)
                this.map.setZoom(12);

            /** Pan to selected well */
            this.map.panTo(new GLatLng(feature.latitude, feature.longitude));

            if (feature.classification == "Well")
                this.displayWellPopup(feature);
        }
    },

    /**************************************************************************
     * This function is called when one of the wells in the Feature List has
     * there info window closed.
     *
     *************************************************************************/
    deselectFeature: function(event, feature, featureElement, parentGroup) {

        /** Remove the selected class from any other wells */
        for (var i=0; i < parentGroup.groupContainer.childElements().length; i++)
           if (parentGroup.groupContainer.childElements()[i].hasClassName("ymmapsFeatureSelected") &&
               parentGroup.groupContainer.childElements()[i] == featureElement)
               parentGroup.groupContainer.childElements()[i].removeClassName("ymmapsFeatureSelected");
    },

    /**************************************************************************
     * This function is called when one of the line features in the Feature
     * List is clicked, which toggles it's display on and off.
     *************************************************************************/
    toggleLineFeature: function(event, feature, featureElement) {

        /** Check to see if any wells need to be removed */
        if (featureElement.hasClassName("ymmapsFeatureSelected")) {
            featureElement.removeClassName("ymmapsFeatureSelected");

            /** Iterate through the line graphics */
            for (var i = 0; i < this.lineGraphics.length; i ++) {

                /** Toggle the visiblity of the lines for this line feature */
                if (this.lineGraphics[i].featureId == feature.featureId)
                    this.lineGraphics[i].graphic.hide();
            }
        }
        else {
            featureElement.addClassName("ymmapsFeatureSelected");

            /** Iterate through the line graphics */
            for (var i = 0; i < this.lineGraphics.length; i ++) {

                /** Toggle the visiblity of the lines for this line feature */
                if (this.lineGraphics[i].featureId == feature.featureId)
                    this.lineGraphics[i].graphic.show();
            }
        }
    },

    /**************************************************************************
     * This function is called as part of the setup process.  It makes
     * an AJAX call to a Java servelt on the backend which returns a
     * JSON response containg all the features to display.
    **************************************************************************/
    loadLayers:function() {

        /** Add Terain Map */
        this.map.addMapType(G_PHYSICAL_MAP);

        /** Add Aeromagnetic Map */
        var aeromagneticTiles = function (a,b) {
            var f = "/tiles/aeromagnetic/Z" + b + "/" + "x" + a.x + "y" + a.y + ".png";
            return f;
        }

        aeromagneticHybridLayer = new Array();
        aeromagneticHybridLayer[0] = G_HYBRID_MAP.getTileLayers()[0];
        aeromagneticHybridLayer[1] = new GTileLayer(new GCopyrightCollection(''),1,20);
        aeromagneticHybridLayer[1].getTileUrl = aeromagneticTiles;
        aeromagneticHybridLayer[1].getOpacity = function () {return 0.6;};
        aeromagneticHybridLayer[1].isPng = function() {return true;};
        aeromagneticHybridLayer[2] = G_HYBRID_MAP.getTileLayers()[1];

        aeromagneticMap = new GMapType(aeromagneticHybridLayer,
              G_SATELLITE_MAP.getProjection(), "Aeromagnetic",{errorMessage:"No Aeronagnetic Here", textColor:"#0000FF", alt:"Show Aeromagnetic Overlay"});

        /** Create the Beatty Bedrock Geology Map */
        var beattyBedrockGeologyTiles = function (a,b) {
            var f = "/tiles/beatty_bedrock_geology/Z" + b + "/" + "x" + a.x + "y" + a.y + ".png";
            return f;
        }

        beattyBedrockGeologyHybridLayer = new Array();
        beattyBedrockGeologyHybridLayer[0] = G_HYBRID_MAP.getTileLayers()[0];
        beattyBedrockGeologyHybridLayer[1] = new GTileLayer(new GCopyrightCollection(''),1,20);
        beattyBedrockGeologyHybridLayer[1].getTileUrl = beattyBedrockGeologyTiles;
        beattyBedrockGeologyHybridLayer[1].getOpacity = function () {return 0.6;};
        beattyBedrockGeologyHybridLayer[1].isPng = function() {return true;};
        beattyBedrockGeologyHybridLayer[2] = G_HYBRID_MAP.getTileLayers()[1];

        beattyBedrockGeologyMap = new GMapType(beattyBedrockGeologyHybridLayer,
              G_SATELLITE_MAP.getProjection(), "Beatty Bedrock Geology",{errorMessage:"No Beatty Bedrock Geology Here", textColor:"#0000FF", alt:"Show Beatty Bedcrock Overlay"});

        /** Create the Faults Map */
        var faultsTiles = function (a,b) {
            var f = "/tiles/faults/Z" + b + "/" + "x" + a.x + "y" + a.y + ".png";
            return f;
        }

        faultsHybridLayer = new Array();
        faultsHybridLayer[0] = G_HYBRID_MAP.getTileLayers()[0];
        faultsHybridLayer[1] = new GTileLayer(new GCopyrightCollection(''),1,20);
        faultsHybridLayer[1].getTileUrl = faultsTiles;
        faultsHybridLayer[1].getOpacity = function () {return 1.0;};
        faultsHybridLayer[1].isPng = function() {return true;};
        faultsHybridLayer[2] = G_HYBRID_MAP.getTileLayers()[1];

        faultsMap = new GMapType(faultsHybridLayer,
              G_SATELLITE_MAP.getProjection(), "Faults",{errorMessage:"No Faults Here", textColor:"#0000FF", alt:"Show Faults Overlay"});

        /** Create the Labels Map */
        var labelsTiles = function (a,b) {
            var f = "/tiles/labels/Z" + b + "/" + "x" + a.x + "y" + a.y + ".png";
            return f;
        }

        labelsHybridLayer = new Array();
        labelsHybridLayer[0] = G_HYBRID_MAP.getTileLayers()[0];
        labelsHybridLayer[1] = new GTileLayer(new GCopyrightCollection(''),1,20);
        labelsHybridLayer[1].getTileUrl = labelsTiles;
        labelsHybridLayer[1].getOpacity = function () {return 1.0;};
        labelsHybridLayer[1].isPng = function() {return true;};
        labelsHybridLayer[2] = G_HYBRID_MAP.getTileLayers()[1];

        labelsMap = new GMapType(labelsHybridLayer,
              G_SATELLITE_MAP.getProjection(), "Labels",{errorMessage:"No Labels Here", textColor:"#0000FF", alt:"Show Labels overlay"});

        /** Create the Yucca Mountain Geology Map */
        var yuccaMountainGeologyTiles = function (a,b) {
            var f = "/tiles/yucca_mountain_geology/Z" + b + "/" + "x" + a.x + "y" + a.y + ".png";
            return f;
        }

        yuccaMountainGeologyHybridLayer = new Array();
        yuccaMountainGeologyHybridLayer[0] = G_HYBRID_MAP.getTileLayers()[0];
        yuccaMountainGeologyHybridLayer[1] = new GTileLayer(new GCopyrightCollection(''),1,20);
        yuccaMountainGeologyHybridLayer[1].getTileUrl = yuccaMountainGeologyTiles;
        yuccaMountainGeologyHybridLayer[1].getOpacity = function () {return 0.6;};
        yuccaMountainGeologyHybridLayer[1].isPng = function() {return true;};
        yuccaMountainGeologyHybridLayer[2] = G_HYBRID_MAP.getTileLayers()[1];

        yuccaMountainGeologyMap = new GMapType(yuccaMountainGeologyHybridLayer,
              G_SATELLITE_MAP.getProjection(), "Yucca Mountain Geology",{errorMessage:"Yucca Mountain Geology", textColor:"#0000FF", alt:"Show Yucca Mountain Geology Overlay"});

        /** Create Yucca Mountain Topography Map */
        var yuccaMountainTopographyTiles = function (a,b) {
            var f = "/tiles/yucca_mountain_topography/Z" + b + "/" + "x" + a.x + "y" + a.y + ".png";
            return f;
        }

        /** Create the Day Central Block Bedrock Geology Map */
        var dayGeologyTiles = function (a,b) {
            var f = "/tiles/day_geology/Z" + b + "/" + "x" + a.x + "y" + a.y + ".png";
            return f;
        }

        dayGeologyHybridLayer = new Array();
        dayGeologyHybridLayer[0] = G_HYBRID_MAP.getTileLayers()[0];
        dayGeologyHybridLayer[1] = new GTileLayer(new GCopyrightCollection(''),1,20);
        dayGeologyHybridLayer[1].getTileUrl = dayGeologyTiles;
        dayGeologyHybridLayer[1].getOpacity = function () {return 0.6;};
        dayGeologyHybridLayer[1].isPng = function() {return true;};
        dayGeologyHybridLayer[2] = G_HYBRID_MAP.getTileLayers()[1];

        dayGeologyMap = new GMapType(dayGeologyHybridLayer,
              G_SATELLITE_MAP.getProjection(), "Central Block Bedrock Geology",{errorMessage:"Central Block Bedrock Geology", textColor:"#0000FF", alt:"Show Central Block Bedrock Geology Overlay"});

        /** Create Yucca Mountain Topography Map */
        var yuccaMountainTopographyTiles = function (a,b) {
            var f = "/tiles/yucca_mountain_topography/Z" + b + "/" + "x" + a.x + "y" + a.y + ".png";
            return f;
        }

        yuccaMountainTopographyHybridLayer = new Array();
        yuccaMountainTopographyHybridLayer[0] = G_HYBRID_MAP.getTileLayers()[0];
        yuccaMountainTopographyHybridLayer[1] = new GTileLayer(new GCopyrightCollection(''),1,20);
        yuccaMountainTopographyHybridLayer[1].getTileUrl = yuccaMountainTopographyTiles;
        yuccaMountainTopographyHybridLayer[1].getOpacity = function () {return 0.6;};
        yuccaMountainTopographyHybridLayer[1].isPng = function() {return true;};
        yuccaMountainTopographyHybridLayer[2] = G_HYBRID_MAP.getTileLayers()[1];

        yuccaMountainTopographyMap = new GMapType(yuccaMountainTopographyHybridLayer,
        G_SATELLITE_MAP.getProjection(), "Yucca Mountain Topography",{errorMessage:"Yucca Mountain Topography", textColor:"#0000FF", alt:"Show Yucca Mountain Topography overlay"});

        /** Add All the Maps */
        this.map.addMapType(aeromagneticMap);
        this.map.addMapType(beattyBedrockGeologyMap);
        this.map.addMapType(faultsMap);
        this.map.addMapType(labelsMap);
        this.map.addMapType(yuccaMountainGeologyMap);
        this.map.addMapType(dayGeologyMap);
        this.map.addMapType(yuccaMountainTopographyMap);

        /** Display the Hybrid Map by default */
        this.map.setMapType(G_HYBRID_MAP);
    },

    /**************************************************************************
     * This function is executed whenever the map pans.  If the user attempts
     * to pan outside of the boundries, the map snaps back.
     *************************************************************************/
    checkBounds:function() {

        /** Get the current bounds */
        var currentBounds = this.map.getBounds();

        /** Set the X and Y offset */
        var offsetX = currentBounds.toSpan().lng() / (2 + this.aberration);
        var offsetY = currentBounds.toSpan().lat() / (2 + this.aberration);

        /** Get the current X and Y from the Center */
        var mapCenter = this.map.getCenter();
        var X = mapCenter.lng();
        var Y = mapCenter.lat();

        /** Get the bound checks */
        var checkSW = new GLatLng(mapCenter.lat() - offsetY, mapCenter.lng() - offsetX);
        var checkNE = new GLatLng(mapCenter.lat() + offsetY, mapCenter.lng() + offsetX);

        /** If bound checks are within the allowed bounds, do nothing and return */
        if (this.allowedBounds.containsLatLng(checkSW) &&
            this.allowedBounds.containsLatLng(checkNE)) {
            return;
        }

        /** Get the allowed max/min X and Y */
        var allowedMaxX = this.allowedBounds.getNorthEast().lng();
        var allowedMaxY = this.allowedBounds.getNorthEast().lat();
        var allowedMinX = this.allowedBounds.getSouthWest().lng();
        var allowedMinY = this.allowedBounds.getSouthWest().lat();

        /** Check if the bounds go past the allowed min or max for X and Y */
        if (X < (allowedMinX+offsetX))
            X = allowedMinX + offsetX;

        if (X > (allowedMaxX-offsetX))
            X = allowedMaxX - offsetX;

        if (Y < (allowedMinY+offsetY))
            Y = allowedMinY + offsetY;

        if (Y > (allowedMaxY-offsetY))
            Y = allowedMaxY - offsetY;

        /** Set the map center based upon the newly calculated X and Y */
        this.map.setCenter(new GLatLng(Y,X));

        return;
    },

    /**************************************************************************
     * This function is executed as part of the setup process.  It restricts
     * the zoom level of the maps.
     *************************************************************************/
    setZoomRestrictions:function() {

        /** Get the list of map types */
        var mapTypes = this.map.getMapTypes();

        /** Set the getMinimumResolution and getMaximumResolution methods */
        for (var i=0; i < mapTypes.length; i++) {
            mapTypes[i].getMinimumResolution = function() {return this.minZoom;}.bind(this);
            mapTypes[i].getMaximumResolution = function() {return this.maxZoom;}.bind(this);
        }
    },


    /**************************************************************************
     * This function is executed as part of the setup process.  It adjusts
     * the style settings of various controls so that they appear correclty.
     *************************************************************************/
    adjustControlStyle:function() {
        var menuControlMainDiv = Element.extend(document.getElementById("menumtctl_main"));
        if (menuControlMainDiv) {
            menuControlMainDiv.setStyle({width: "200px"});
        }
        else setTimeout(function(){this.adjustControlStyle();}.bind(this),10);
    },

    /**************************************************************************
     * This function is called at startup and whenever the browser window
     * is resized in an attempt to use the maximum amount of available
     * screen space.
     *************************************************************************/
    setWindowFullscreen:function() {

        var width = 0;
        var height = 0;

        /** Code for all browser except IE */
        if( typeof( window.innerWidth ) == 'number' ) {
            width = window.innerWidth;
            height = window.innerHeight;
        }

        /** Code for IE6+ in standards compliant mode */
        else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
            width = document.documentElement.clientWidth;
            height = document.documentElement.clientHeight;
        }

        /** Code for older IE or non-compliant mode */
        else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
            width = document.body.clientWidth;
            height = document.body.clientHeight;
        }

        width = width - widthAdjust;
        height = height - heightAdjust;

        /** Set Container Size */
        this.toolContainer.setStyle({height: height+"px"});
        this.toolContainer.setStyle({width: width+"px"});

        /** Set Map Panel Height */
        this.mapPanel.setStyle({height: height+"px"});

        /** Set Map Panel Width */
        if (this.sideBarPanel.visible())
            width = width - this.sideBarPanelHeader.getWidth() - 4;

        this.mapPanel.setStyle({width: width + "px"});

        /** Set Sidebar Panel Height */
        var sideBarPanelHeight = this.mapPanel.getHeight() - 2;
        this.sideBarPanel.setStyle({height: sideBarPanelHeight+"px"});

        var featureListHeight = this.mapPanel.getHeight() - this.sideBarPanelHeader.getHeight() - this.sideBarPanelBottomPanel.getHeight() - 2;
        this.featureList.setStyle({height: featureListHeight+"px"});

        var sideBarPanelContentHeight = this.mapPanel.getHeight() - this.sideBarPanelHeader.getHeight() - 2;
        this.sideBarPanelContent.setStyle({height: sideBarPanelContentHeight+"px"});

        this.map.checkResize();
    },

    /**************************************************************************
     * This function is responsible for creating and displaying the graphic
     * popup when a well is selected from the list or its graphic is clicked.
     *************************************************************************/
    displayWellPopup:function(well) {
        new Ajax.Request("welldetails", {
            method: "get",
            requestHeaders: "application/json",
            parameters: "wellId=" + well.featureId,
            asynchronous: true,
            onSuccess: function(transport){
                var response = transport.responseText.evalJSON();
                var HTMLContent;
                HTMLContent = "<div><table width='360px;' style='font-size:10px;'>";

                if (response.wellDetails == "FailedDB") {
                    HTMLContent += "<tr><td color='red'><b>Database Error Occured</b></td><td></td></tr>";
                }

                else if (response.wellDetails == "FailedJSON") {
                    HTMLContent += "<tr><td color='red'><b>JSON Error Occured</b></td><td></td></tr>";
                }

                else if (response.wellDetails == "NoWell") {
                    HTMLContent += "<tr><td color='red'><b>No Well Matched the Given WellId</b></td><td></td></tr>";
                }

                else if (response.wellDetails == "Success") {

                    var wellDetails = response.details;

                    /** Set display of Details */
                    HTMLContent += "<tr><td><b>Details</b></td><td></td></tr>";
                    HTMLContent += "<tr><td colspan='2' bgcolor='#C9C4E9'><img src='../images/line.gif' width='1' height='1' border='0'></td></tr>";
                    HTMLContent += "<tr><td>Name</td><td align='right'>" + wellDetails.name + "</td></tr>";

                    /** Display optional information */
                    if (wellDetails.type != null)
                        HTMLContent += "<tr><td>Type</td><td align='right'>" + wellDetails.type + "</td></tr>";

                    if (wellDetails.phase != null)
                        HTMLContent += "<tr><td>Phase</td><td align='right'>" + wellDetails.phase + "</td></tr>";

                    if (wellDetails.complectionDate != null)
                        HTMLContent += "<tr><td>Completion Date</td><td align='right'>" + wellDetails.computationDate + "</td></tr>";

                    if (wellDetails.groundLevel_ft != null && wellDetails.groundLevel_ft != 0)
                        HTMLContent += "<tr><td>Ground Level(ft)</td><td align='right'>" + wellDetails.groundLevel_ft + "</td></tr>";

                    if (wellDetails.groundLevel_m != null && wellDetails.groundLevel_m != 0)
                        HTMLContent += "<tr><td>Ground Level(m)</td><td align='right'>" + wellDetails.groundLevel_m + "</td></tr>";

                    if (wellDetails.totalDepth_ft != null && wellDetails.totalDepth_ft != 0)
                        HTMLContent += "<tr><td>Total Depth(ft)</td><td align='right'>" + wellDetails.totalDepth_ft + "</td></tr>";

                    if (wellDetails.elevationWellBottom_m != null && wellDetails.elevationWellBottom_m != 0)
                        HTMLContent += "<tr><td>Elevation Well Bottom(m)</td><td align='right'>" + wellDetails.elevationWellBottom_m + "</td></tr>";

                    if (wellDetails.waterLevel_ft != null && wellDetails.waterLevel_ft != 0)
                        HTMLContent += "<tr><td>Water Level(ft)</td><td align='right'>" + wellDetails.waterLevel_ft + "</td></tr>";

                    if (wellDetails.topOfTuff_ft != null && wellDetails.topOfTuff_ft != 0)
                        HTMLContent += "<tr><td>Top of Tuff(ft)</td><td align='right'>" + wellDetails.topOfTuff_ft + "</td></tr>";

                    if (wellDetails.status != null)
                        HTMLContent += "<tr><td>Status</td><td align='right'>" + wellDetails.status + "</td></tr>";

                    if (wellDetails.siteSummary != null)
                        HTMLContent += "<tr><td>Site Summary</td><td align='right'><a href=\"javascript:openNewWindow('"+ wellDetails.siteSummary + "','Site_Summary_for_" + wellDetails.name +"');\">Site Summary</a></td></tr>";

                    if (wellDetails.lithologySummary != null)
                        HTMLContent += "<tr><td>Lithology Summary</td><td align='right'><a href=\"javascript:openNewWindow('"+ wellDetails.lithologySummary + "','Lithology_Summary_for_" + wellDetails.name +"');\">Lithology Summary</a></td></tr>";

                    if (wellDetails.completionDiagram != null)
                        HTMLContent += "<tr><td>Completion Diagram</td><td align='right'><a href=\"javascript:openNewWindow('"+ wellDetails.completionDiagram + "','Completion_Diagram_for_" + wellDetails.name +"');\">Completion Diagram</a></td></tr>";

                    if (wellDetails.waterLevels != null)
                        HTMLContent += "<tr><td>Water Levels</td><td align='right'><a href=\"javascript:openNewWindow('"+ wellDetails.waterLevels + "','Water_Levels_for_" + wellDetails.name +"');\">Water Levels</a></td></tr>";

                    if (wellDetails.anionCationReport != null)
                        HTMLContent += "<tr><td>Anion Cation Report</td><td align='right'><a href=\"javascript:openNewWindow('"+ wellDetails.anionCationReport + "','Anion_Cation_Report_for_" + wellDetails.name +"');\">Anion Cation Report</a></td></tr>";

                    if (wellDetails.chemicalAnalyses != null)
                        HTMLContent += "<tr><td>Chemical Analyses</td><td align='right'><a href=\"javascript:openNewWindow('"+ wellDetails.chemicalAnalyses + "','Chemical_Analyses_for_" + wellDetails.name +"');\">Chemical Analyses</a></td></tr>";

                    if (wellDetails.metalsAnalyses != null)
                        HTMLContent += "<tr><td>Metals Analyses</td><td align='right'><a href=\"javascript:openNewWindow('"+ wellDetails.metalsAnalyses + "','Metals_Analyses_for_" + wellDetails.name +"');\">Metals Analyses</a></td></tr>";

                    if (wellDetails.recompletionDiagram != null)
                        HTMLContent += "<tr><td>Recompletion Diagram</td><td align='right'><a href=\"javascript:openNewWindow('"+ wellDetails.recompletionDiagram + "','Recompletion_Diagram_for_" + wellDetails.name +"');\">Recompletion Diagram</a></td></tr>";

                    if (wellDetails.link != null)
                        HTMLContent += "<tr><td>Link</td><td align='right'><a href=\"javascript:openNewWindow('"+ wellDetails.link + "','Link_for_" + wellDetails.name +"');\">Link</a></td></tr>";

                    if (wellDetails.classification != null)
                        HTMLContent += "<tr><td>Classification</td><td align='right'>" + wellDetails.classification + "</td></tr>";

                    if (wellDetails.quality != null)
                        HTMLContent += "<tr><td>Quality</td><td align='right'>" + wellDetails.quality + "</td></tr>";

                    if (wellDetails.DTN != null)
                        HTMLContent += "<tr><td>DTN</td><td align='right'>" + wellDetails.DTN + "</td></tr>";

                    if (wellDetails.easting_NAD83 != null)
                        HTMLContent += "<tr><td>Easting NAD83</td><td align='right'>" + wellDetails.easting_NAD83 + "</td></tr>";

                    if (wellDetails.northing_NAD83 != null)
                        HTMLContent += "<tr><td>Northing NAD83</td><td align='right'>" + wellDetails.northing_NAD83 + "</td></tr>";

                    if (wellDetails.latitude_NAD83 != null)
                        HTMLContent += "<tr><td>Latitude NAD83</td><td align='right'>" + wellDetails.latitude_NAD83 + "</td></tr>";

                    if (wellDetails.longitude_NAD83 != null)
                        HTMLContent += "<tr><td>Longitude NAD83</td><td align='right'>" + wellDetails.longitude_NAD83 + "</td></tr>";
                }

                HTMLContent += "</table></div>";

                /** Open the graphic with the Details with width 380 */
                well.graphic.openInfoWindowHtml(HTMLContent, {maxWidth:380});
             }
        });
    }
});

/**************************************************************************
* This function opens a pop-up window with the specified target and name.
*************************************************************************/
function openNewWindow(target, windowName) {
    window.open(target,windowName,"width=800,height=600,resizable=yes");
}
