﻿    /// <reference path="GISMap.js" />
    /*extern GISMapTool */

    // Indicate that this file is available.
    GISMapTool.areAvailable = true;

    // Setup a generic version for NONE.
    GISMap.mapTool.NONE = new GISMapTool();
    GISMap.mapTool.NONE.name = 'NONE';
    GISMap.mapTool.NONE.onSet = function() {return true;};

    GISMap.mapTool.PAN = new GISMapTool();
    GISMap.mapTool.PAN.name = 'PAN';
    GISMap.mapTool.PAN.mouseCursor = 'move';
    GISMap.mapTool.PAN.onSet = function() {
        /// <summary>STATIC - PAN tool setup.  Determines if this tool can be used.</summary>
        /// <returns type="Boolean" />

        if (!this.hasCapability(GISMap.capability.ALLOW_PAN)) {
            this.debugConsole.error('The server has not returned enough information to allow you to pan the map.');
            return false;
        }
        this.debugConsole.write('GISMap.mapTool.PAN.onSet() - Map tool set to PAN.');

        return true;
    };

    GISMap.mapTool.PAN.onMouseDown = function(X, Y) {
	    /// <summary>STATIC - PAN tool initializer.  Stores the current location in the panMouseX and panMouseY attributes.</summary>
	    /// <param name="X" type="Integer" />
	    /// <param name="Y" type="Integer" />
	    /// <returns type="void" />

	    // We cannot just move the image with the mouse unless the user clicked at the very top-left corner.
	    // We have to figure out how far into the image the mouse was when it was clicked and then move based on that offset.

	    // Store the current mouse position in the div for reference.
	    // We need to take into account the current location of the image.
	    var currLeft = parseInt(this.mapImage.style.left.replace(/px/,''), 10);
	    var currTop = parseInt(this.mapImage.style.top.replace(/px/,''), 10);
	    if (isNaN(currLeft)) {currLeft = 0;}
	    if (isNaN(currTop)) {currTop = 0;}
    	
	    this.mapImage.setAttribute('panMouseX',X - currLeft);
	    this.mapImage.setAttribute('panMouseY',Y - currTop);
    	
	    // That is all, the onMouseMove() function handles the movement.
    };

    GISMap.mapTool.PAN.onMouseMove = function(X, Y) {
	    /// <summary>STATIC - PAN tool updater.  Moves the image as the mouse moves.</summary>
	    /// <param name="X" type="Integer" />
	    /// <param name="Y" type="Integer" />
	    /// <returns type="void" />

	    // Get the difference between the current location and the original starting location.
	    var origX = parseInt(this.mapImage.getAttribute('panMouseX'), 10);
	    var origY = parseInt(this.mapImage.getAttribute('panMouseY'), 10);

	    var newX = X - origX;
	    var newY = Y - origY;

	    if (!isNaN(newX)) {
		    this.mapImage.style.left = newX + 'px';
	    }
	    if (!isNaN(newY)) {
		    this.mapImage.style.top = newY + 'px';
	    }
    };

    GISMap.mapTool.PAN.onMouseUp = function(X, Y) {
        /// <summary>STATIC - PAN tool finalizer.  If the map moved then fire a request to have the map updated.</summary>
        /// <param name="X" type="Integer" />
        /// <param name="Y" type="Integer" />
        /// <returns type="void" />

        this.debugConsole.write('GISMap.mapTool.PAN.onMouseUp() - Determining new map position.');

        // Get the new min and max locations
        var minX = -parseInt(this.mapImage.style.left.replace(/px/, ''), 10);
        var minY = -parseInt(this.mapImage.style.top.replace(/px/, ''), 10);

        if (isNaN(minX)) { minX = 0; }
        if (isNaN(minY)) { minY = 0; }

        var maxX = minX + parseInt(this.mapImage.clientWidth, 10);
        var maxY = minY + parseInt(this.mapImage.clientHeight, 10);

        this.debugConsole.write('GISMap.mapTool.PAN.onMouseUp() - Converting pixels to feet.');

        // Convert these points to feet
        var newMin = this.currentContext.convertPixelsToFeet({ x: minX, y: minY });
        var newMax = this.currentContext.convertPixelsToFeet({ x: maxX, y: maxY });

        // Check to see if there was a change in *any* of the values.
        // For some reason the min and max Y values become inverted.  It doesn't affect mapping, but it does affect comparisons.
        var refreshMap = false;
        if (this.currentContext.minX !== newMin.x) { refreshMap = true; }
        if (this.currentContext.minY !== newMax.y) { refreshMap = true; }
        if (this.currentContext.maxX !== newMax.x) { refreshMap = true; }
        if (this.currentContext.maxY !== newMin.y) { refreshMap = true; }

        if (refreshMap) {
            // Set the current context to the new values and send it to the server.
            this.currentContext.minX = newMin.x;
            this.currentContext.minY = newMin.y;
            this.currentContext.maxX = newMax.x;
            this.currentContext.maxY = newMax.y;

            this.debugConsole.write('GISMap.mapTool.PAN.onMouseUp() - Map has moved, requesting new map.');

            // Request a new image from the server.
            this.getMapImage(this.currentMapTool.name);
        } else {
            this.debugConsole.write('GISMap.mapTool.PAN.onMouseUp() - Aborting PAN, map has not moved.');
        }

        // Reset the original mouse click location
        this.mapImage.setAttribute('panMouseX', null);
        this.mapImage.setAttribute('panMouseY', null);

        return null;
    };

    GISMap.mapTool.PAN.onMouseDoubleClick = function(X, Y, clickedFeet) {
		    // Center the map on this clicked location.

		    // To do this, we simulate a pan.  We act as if the mouse was clicked in the very center, we then
		    // update the pan towards the direction the user chose.  Then the onMouseUp function is called.
    	
		    // Get half of the map (this pixel location is where the clicked location will go, it is center).
		    var halfWidth = this.currentContext.imageWidth / 2;
		    var halfHeight = this.currentContext.imageHeight / 2;

		    // Act as if the user clicked in the very center of the image and started dragging.
		    GISMap.mapTool.PAN.onMouseDown.call(this, halfWidth, halfHeight);

		    // The put the clicked location in the center and then update the pan.
		    var newX = (halfWidth - (X - halfWidth));
		    var newY = (halfHeight - (Y - halfHeight));
		    this.debugConsole.write('GISMap.mapTool.PAN.onMouseDoubleClick() - Moving map by 1/2.');
		    GISMap.mapTool.PAN.onMouseMove.call(this, newX, newY);
		    GISMap.mapTool.PAN.onMouseUp.call(this, newX, newY);
    };

    GISMap.mapTool.ZOOMIN = new GISMapTool();
    GISMap.mapTool.ZOOMIN.name = 'ZOOMIN';
    GISMap.mapTool.ZOOMIN.mouseCursor = 'crosshair';
    GISMap.mapTool.ZOOMIN.onSet = function() {
	    /// <summary>STATIC - PAN tool setup.  Determines if this tool can be used.</summary>
	    /// <returns type="Boolean" />
    	
	    if (!this.hasCapability(GISMap.capability.ALLOW_ZOOMIN)) {
		    this.debugConsole.error('The server has not returned enough information to allow you to zoom in.');
		    return false;
	    }

	    this.debugConsole.write('GISMap.mapTool.ZOOMIN.onSet() - Map tool set to ZOOMIN.');

	    return true;
    };

    GISMap.mapTool.ZOOMIN.onMouseDown = function(X, Y) {
	    /// <summary>STATIC - ZOOMIN/ZOOMOUT tool initializer.</summary>
	    /// <param name="X" type="Integer" />
	    /// <param name="Y" type="Integer" />
	    /// <returns type="void" />

	    // If this is IE6- then use a filter to add PNG alpha-transparency support.
        if (!window.XMLHttpRequest) {
            this.debugConsole.write('GISMap.mapTool.ZOOMIN.onMouseDown() - Using AlphaImageLoader method to load transparent PNG.');
		    this.zoomDiv.style.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='images/zoomBoxBackground.png', sizingMethod='scale')";
           } else {
            this.debugConsole.write('GISMap.mapTool.ZOOMIN.onMouseDown() - Using native method to load transparent PNG.');
		    this.zoomDiv.style.backgroundImage = 'url(images/zoomBoxBackground.png)';
	    }

	    // Store the current mouse location as the top corner of the box.
	    this.zoomDiv.setAttribute('staticX', X);
	    this.zoomDiv.setAttribute('staticY', Y);
	    this.zoomDiv.style.left = X + 'px';
	    this.zoomDiv.style.top = Y + 'px';

	    this.debugConsole.write('GISMap.mapTool.ZOOMIN.onMouseDown() - Storing starting point for zoom box (X = ' + X + 'px; Y = ' + Y + 'px;)');
    	
	    // Reset the values.
	    this.zoomDiv.style.width = '0px';
	    this.zoomDiv.style.height = '0px';
    };

    GISMap.mapTool.ZOOMIN.onMouseMove = function(X, Y, preventBoundsCheck) {
	    /// <summary>STATIC - ZOOMIN/ZOOMOUT tool updater.  Resizes the zoom box.</summary>
	    /// <param name="X" type="Integer" />
	    /// <param name="Y" type="Integer" />
	    /// <param name="preventBoundsCheck" type="Boolean" />
	    /// <returns type="void" />

	    // If the box hasn't been shown, then show it.
	    if (this.zoomDiv.style.display !== 'block') {
		    this.zoomDiv.style.display = 'block';
	    }

	    var staticX = parseInt(this.zoomDiv.getAttribute('staticX'), 10);
	    var staticY = parseInt(this.zoomDiv.getAttribute('staticY'), 10);

	    var zoomWidth = 0;
	    var zoomHeight = 0;
	    var zoomLeft = 0;
	    var zoomTop = 0;

	    // Determine the new top, left corner and the width and height.
	    if (X >= staticX) {
		    zoomWidth = X - staticX;
		    zoomLeft = staticX;
	    } else {
		    zoomWidth = staticX - X;
		    zoomLeft = X + 2;
	    }
    	
	    if (Y >= staticY) {
		    zoomHeight = Y - staticY;
		    zoomTop = staticY;
	    } else {
		    zoomHeight = staticY - Y;
		    zoomTop = Y + 2;
	    }

	    // Make sure the mouse cursor isn't going outside the mapImage.
	    if (!preventBoundsCheck) {
		    if (zoomTop < 0) {
			    zoomHeight = zoomHeight + zoomTop;
			    zoomTop = 0;
		    }
		    if (zoomLeft < 0) {
			    zoomWidth = zoomWidth + zoomLeft;
			    zoomLeft = 0;
		    }
		    if ((zoomLeft + zoomWidth) > (this.mapImage.clientWidth - 2)) {
			    zoomWidth = this.mapImage.clientWidth - zoomLeft - 2;
		    }
		    if ((zoomTop + zoomHeight) > (this.mapImage.clientHeight - 2)) {
			    zoomHeight = this.mapImage.clientHeight - zoomTop - 2;
		    }
	    }

	    // Set the new dimensions of the zoomBox.
	    this.zoomDiv.style.top = zoomTop + 'px';
	    this.zoomDiv.style.left = zoomLeft + 'px';
    	
	    this.zoomDiv.style.width = zoomWidth + 'px';
	    this.zoomDiv.style.height = zoomHeight + 'px';
	    return null;
    };

    GISMap.mapTool.ZOOMIN.onMouseUp = function(X, Y) {
	    /// <summary>STATIC - ZOOMIN tool finalizer.  Determines what extent is encompassed by the zoom box.</summary>
	    /// <param name="X" type="Integer" />
	    /// <param name="Y" type="Integer" />
	    /// <returns type="void" />
    	
	    // If the width and height of the zoomBox is less than 2 then return
        if ((parseInt(this.zoomDiv.style.width.replace(/px/, ''), 10) < 2) && (parseInt(this.zoomDiv.style.height.replace(/px/, ''), 10) < 2)) {
            this.debugConsole.write('GISMap.mapTool.ZOOMIN.onMouseUp() - Aborting ZOOMIN, selected area too small. (Width = ' + parseInt(this.zoomDiv.style.width.replace(/px/, ''), 10) + '; Height = ' + parseInt(this.zoomDiv.style.height.replace(/px/, ''), 10) + ';');
		    this.removeZoomBox();
		    return null;
	    }

	    this.debugConsole.write('GISMap.mapTool.ZOOMIN.onMouseUp() - Calculating new envelope.');

	    // Convert the starting and stopping points to feet, update the current context and request a new image from the server.
	    var minX = parseInt(this.zoomDiv.style.left.replace(/px/,''), 10);
	    var minY = parseInt(this.zoomDiv.style.top.replace(/px/,''), 10);
	    var maxX = parseInt(parseInt(minX, 10) + parseInt(this.zoomDiv.style.width.replace(/px/,''), 10), 10) + 2; // Add two for the borders, I would have prefered to get the
	    var maxY = parseInt(parseInt(minY, 10) + parseInt(this.zoomDiv.style.height.replace(/px/,''), 10), 10) + 2; // actual border width, but I cannot figure it out in short order.

	    this.debugConsole.write('GISMap.mapTool.ZOOMIN.onMouseUp() - Converting pixels to feet.');

	    var newMin = this.currentContext.convertPixelsToFeet({x:minX, y:minY});
	    // The base + length is the ending point (cannot use mouse position because it could be beyond bounds of map)
	    var newMax = this.currentContext.convertPixelsToFeet({x:maxX, y:maxY});

	    // If we are zooming out then we want to resize the entire map to be the size of the box.
	    var minXoffset = 0;
	    var maxXoffset = 0;
	    var minYoffset = 0;
	    var maxYoffset = 0;
	    var tmp = 0;

	    this.debugConsole.write('GISMap.mapTool.ZOOMIN.onMouseUp() - Setting envelope.');

	    // Set the current context to the new values and send it to the server.
	    this.currentContext.minX = newMin.x;
	    this.currentContext.minY = newMin.y;
	    this.currentContext.maxX = newMax.x;
	    this.currentContext.maxY = newMax.y;

	    // Hide the zoom box and reset the data.
	    this.debugConsole.write('GISMap.mapTool.ZOOMIN.onMouseUp() - Removing zoom box.');
	    this.removeZoomBox();

	    // Request a new image from the server.
	    this.debugConsole.write('GISMap.mapTool.ZOOMIN.onMouseUp() - Requesting new map.');
	    this.getMapImage(this.currentMapTool.name);
    	
	    return null;
    };

    GISMap.mapTool.ZOOMIN.onMouseDoubleClick = function(X, Y, clickedFeet) {
		    // Only zoom if the doubleClickZoomPercent is not zero.
		    if (this.doubleClickZoomPercent === 0) {return null;}

		    // Zoom in by a bit.
		    // Simulate a zoom, but this zoom is allowed to extend outside the box area.

		    this.debugConsole.write('GISMap.mapTool.ZOOMIN.onMouseDoubleClick() - Calculating size of zoom box.');
    		
		    // Get half of the map (this pixel location is where the clicked location will go, it is center).
		    var zWidth = (this.doubleClickZoomPercent / 100) * this.currentContext.imageWidth;
		    var zHeight = (this.doubleClickZoomPercent / 100) * this.currentContext.imageHeight;

		    var myself = this;
		    var flashFunction = function() {
			    // Act as if the user clicked zWidth, zHeight up from the current location.
			    GISMap.mapTool.ZOOMIN.onMouseDown.call(myself, X - zWidth, Y - zHeight);

			    //	The user dragged the mouse past the "current" location to zWidth, zHeight over.
			    GISMap.mapTool.ZOOMIN.onMouseMove.call(myself, X + zWidth, Y + zHeight, true);
		    };

		    var updateFunction = function() {
		        this.debugConsole.write('GISMap.mapTool.ZOOMIN.onMouseDoubleClick() - Simulating call to onMouseDown.');
			    // Act as if the user clicked zWidth, zHeight up from the current location.
			    GISMap.mapTool.ZOOMIN.onMouseDown.call(myself, X - zWidth, Y - zHeight);

			    this.debugConsole.write('GISMap.mapTool.ZOOMIN.onMouseDoubleClick() - Simulating call to onMouseMove.');
			    //	The user dragged the mouse past the "current" location to zWidth, zHeight over.
			    GISMap.mapTool.ZOOMIN.onMouseMove.call(myself, X + zWidth, Y + zHeight, true);

			    this.debugConsole.write('GISMap.mapTool.ZOOMIN.onMouseDoubleClick() - Simulating call to onMouseUp.');
			    if (GISMap.mapTool.ZOOMIN === myself.currentMapTool) {
				    GISMap.mapTool.ZOOMIN.onMouseUp.call(myself, 0, 0);
			    } else if (GISMap.mapTool.ZOOMOUT === myself.currentMapTool) {
				    GISMap.mapTool.ZOOMOUT.onMouseUp.call(myself, 0, 0);
			    }
		    };

		    this.debugConsole.write('GISMap.mapTool.ZOOMIN.onMouseDoubleClick() - Queuing zoom box flashing.');
		    // Set this to run for a few seconds
		    window.setTimeout(flashFunction, 1);
		    window.setTimeout(function() {myself.removeZoomBox();}, 76);
		    window.setTimeout(flashFunction, 175);
		    window.setTimeout(function() {myself.removeZoomBox();}, 251);
		    window.setTimeout(flashFunction, 350);
		    window.setTimeout(function() {myself.removeZoomBox();}, 426);
		    window.setTimeout(flashFunction, 525);
		    window.setTimeout(function() {myself.removeZoomBox();}, 601);
		    window.setTimeout(updateFunction, 700);
    		
		    return null;
    };

    GISMap.mapTool.ZOOMOUT = GISHelper.Clone(GISMap.mapTool.ZOOMIN);
    GISMap.mapTool.ZOOMOUT.name = 'ZOOMOUT';
    GISMap.mapTool.ZOOMOUT.onSet = function() {
	    /// <summary>STATIC - PAN tool setup.  Determines if this tool can be used.</summary>
	    /// <returns type="Boolean" />
    	
	    if (!this.hasCapability(GISMap.capability.ALLOW_ZOOMOUT)) {
		    this.debugConsole.error('The server has not returned enough information to allow you to zoom out.');
		    return false;
	    }

	    this.debugConsole.write('GISMap.mapTool.ZOOMOUT.onSet() - Setting map tool to ZOOMOUT.');

	    return true;
    };

    GISMap.mapTool.ZOOMOUT.onMouseUp = function(X, Y) {
	    /// <summary>STATIC - ZOOMOUT tool finalizer.  Determines what extent is encompassed by the zoom box.</summary>
	    /// <param name="X" type="Integer" />
	    /// <param name="Y" type="Integer" />
	    /// <returns type="void" />
    	
	    // If the width and height of the zoomBox is less than 2 then return
        if ((parseInt(this.zoomDiv.style.width.replace(/px/, ''), 10) < 2) && (parseInt(this.zoomDiv.style.height.replace(/px/, ''), 10) < 2)) {
            this.debugConsole.write('GISMap.mapTool.ZOOMOUT.onMouseUp() - Aborting ZOOMIN, selected area too small. (Width = ' + parseInt(this.zoomDiv.style.width.replace(/px/, ''), 10) + '; Height = ' + parseInt(this.zoomDiv.style.height.replace(/px/, ''), 10) + ';');
		    this.removeZoomBox();
		    return null;
	    }

	    this.debugConsole.write('GISMap.mapTool.ZOOMOUT.onMouseUp() - Calculating new envelope.');
	    // Convert the starting and stopping points to feet, update the current context and request a new image from the server.
	    var minX = parseInt(this.zoomDiv.style.left.replace(/px/,''), 10);
	    var minY = parseInt(this.zoomDiv.style.top.replace(/px/,''), 10);
	    var maxX = parseInt(parseInt(minX, 10) + parseInt(this.zoomDiv.style.width.replace(/px/,''), 10), 10) + 2; // Add two for the borders, I would have prefered to get the
	    var maxY = parseInt(parseInt(minY, 10) + parseInt(this.zoomDiv.style.height.replace(/px/,''), 10), 10) + 2; // actual border width, but I cannot figure it out in short order.

	    this.debugConsole.write('GISMap.mapTool.ZOOMOUT.onMouseUp() - Converting pixels to feet.');

	    var newMin = this.currentContext.convertPixelsToFeet({x:minX, y:minY});
	    // The base + length is the ending point (cannot use mouse position because it could be beyond bounds of map)
	    var newMax = this.currentContext.convertPixelsToFeet({x:maxX, y:maxY});

	    this.debugConsole.write('GISMap.mapTool.ZOOMOUT.onMouseUp() - Transforming ZOOMIN calculation into a ZOOMOUT calculation.');

	    // If we are zooming out then we want to resize the entire map to be the size of the box.
	    var minXoffset = 0;
	    var maxXoffset = 0;
	    var minYoffset = 0;
	    var maxYoffset = 0;
	    var tmp = 0;

	    minXoffset = (parseFloat(this.currentContext.minX) - parseFloat(newMin.x)) * 2;
	    maxXoffset = (parseFloat(this.currentContext.maxX) - parseFloat(newMax.x)) * 2;
	    newMin.x = parseFloat(this.currentContext.minX) + minXoffset;
	    newMax.x = parseFloat(this.currentContext.maxX) + maxXoffset;

	    // Swap the values in newMin.y and newMax.y
	    tmp = newMin.y;
	    newMin.y = newMax.y;
	    newMax.y = tmp;

	    minYoffset = (parseFloat(this.currentContext.minY) - parseFloat(newMin.y)) * 2;
	    maxYoffset = (parseFloat(this.currentContext.maxY) - parseFloat(newMax.y)) * 2;
	    newMin.y = parseFloat(this.currentContext.minY) + minYoffset;
	    newMax.y = parseFloat(this.currentContext.maxY) + maxYoffset;

	    this.debugConsole.write('GISMap.mapTool.ZOOMOUT.onMouseUp() - Setting envelope.');
	    // Set the current context to the new values and send it to the server.
	    this.currentContext.minX = newMin.x;
	    this.currentContext.minY = newMin.y;
	    this.currentContext.maxX = newMax.x;
	    this.currentContext.maxY = newMax.y;

	    // Hide the zoom box and reset the data.
	    this.debugConsole.write('GISMap.mapTool.ZOOMOUT.onMouseUp() - Removing zoom box.');
	    this.removeZoomBox();

	    // Request a new image from the server.
	    this.debugConsole.write('GISMap.mapTool.ZOOMOUT.onMouseUp() - Requesting new map.');
	    this.getMapImage(this.currentMapTool.name);
    	
	    return null;
    };

    GISMap.mapTool.SELECT = GISHelper.Clone(GISMap.mapTool.ZOOMIN);
    GISMap.mapTool.SELECT.name = 'SELECT';
    GISMap.mapTool.SELECT.mouseCursor = 'pointer';
    GISMap.mapTool.SELECT.preventClick = false;
    GISMap.mapTool.SELECT.onSet = function() {
	    /// <summary>STATIC - SELECT tool setup.  Determines if this tool can be used.</summary>
	    /// <returns type="Boolean" />

	    if (!this.hasCapability(GISMap.capability.ALLOW_SELECTPARCEL)) {
		    this.debugConsole.error('The server has not returned enough information to allow you to select a parcel.');
		    return false;
	    }

	    this.debugConsole.write('GISMap.mapTool.SELECT.onSet() - Map tool set to SELECT.');

        // Store the static method onMouseDown, which is really a version of ZOOMIN.onMouseDown.
	    var tmpFunction = GISMap.mapTool.SELECT.onMouseDown;

	    this.debugConsole.write('GISMap.mapTool.SELECT.onSet() - Modifying SELECT.onMouseDown method.');
	    // Inject code which resets preventClick.
	    GISMap.mapTool.SELECT.onMouseDown = function(X, Y) {
		    GISMap.mapTool.SELECT.preventClick = false;
		    if (tmpFunction) {
			    tmpFunction.call(this, X, Y);
		    }
	    };

	    return true;
    };

    GISMap.mapTool.SELECT.onMouseClick = function(X, Y, clickedFeet) {
	    /// <summary>STATIC - SELECT tool.  Determines where the user clicked and sends this information to the server.</summary>
	    /// <param name="X" type="Integer" />
	    /// <param name="Y" type="Integer" />
	    /// <param name="clickedFeet" type="Object{x, y}" />
	    /// <returns type="void" />

	    // Since this tool can also be used for selecting an area, only use the single-click method if the onmouseup didn't already handle it.
	    if (GISMap.mapTool.SELECT.preventClick) {
		    return null;
	    }

	    // Set the select variables in currentContext as the feet.
	    this.currentContext.selectedX = clickedFeet.x;
	    this.currentContext.selectedY = clickedFeet.y;

	    // We don't want the buffer to move around, clear it.
	    // Stop highlighting parcels (this is for buffer and rectangle selection).
	    this.currentContext.bufferDistance = 0;
	    this.currentContext.highlightedParcels = '';

	    // Request the ParcelID from the server.  Once we have it we can request the map image and parcel data.
	    this.debugConsole.write('GISMap.mapTool.SELECT.onMouseClick() - Requesting the ParcelID of the clicked location.');
	    this.isLoadingMap(true);
	    this.communicationsHandler.request_getSelectedParcelID.call(this);

	    return null;
    };

    GISMap.mapTool.SELECT.onMouseUp = function(X, Y) {
	    /// <summary>PRIVATE - SELECT tool finalizer (use if dragged the mouse).  Determines which parcels are encompassed by the zoom box.</summary>
	    /// <param name="X" type="Integer" />
	    /// <param name="Y" type="Integer" />
	    /// <returns type="void" />
    	
	    // If the width and height of the zoomBox is less than 2 then return.  This will be treated as a click.
        if ((parseInt(this.zoomDiv.style.width.replace(/px/, ''), 10) < 2) && (parseInt(this.zoomDiv.style.height.replace(/px/, ''), 10) < 2)) {
            this.debugConsole.write('GISMap.mapTool.SELECT.onMouseUp() - Aborting SELECT multiple, selected area too small. (Width = ' + parseInt(this.zoomDiv.style.width.replace(/px/, ''), 10) + '; Height = ' + parseInt(this.zoomDiv.style.height.replace(/px/, ''), 10) + ';');
		    this.removeZoomBox();
		    return null;
	    }
    	
	    GISMap.mapTool.SELECT.preventClick = true;

	    this.debugConsole.write('GISMap.mapTool.SELECT.onMouseUp() - Calculating new envelope.');
	    // Convert the starting and stopping points to feet, update the current context and request a new image from the server.
	    var minX = parseInt(this.zoomDiv.style.left.replace(/px/,''), 10);
	    var minY = parseInt(this.zoomDiv.style.top.replace(/px/,''), 10);
	    var maxX = parseInt(parseInt(minX, 10) + parseInt(this.zoomDiv.style.width.replace(/px/,''), 10), 10) + 2; // Add two for the borders, I would have prefered to get the
	    var maxY = parseInt(parseInt(minY, 10) + parseInt(this.zoomDiv.style.height.replace(/px/,''), 10), 10) + 2; // actual border width, but I cannot figure it out in short order.

	    this.debugConsole.write('GISMap.mapTool.SELECT.onMouseUp() - Converting pixels to feet.');

	    var newMin = this.currentContext.convertPixelsToFeet({x:minX, y:minY});
	    // The base + length is the ending point (cannot use mouse position because it could be beyond bounds of map)
	    var newMax = this.currentContext.convertPixelsToFeet({x:maxX, y:maxY});

	    // Hide the zoom box and reset the data.
	    this.debugConsole.write('GISMap.mapTool.SELECT.onMouseUp() - Removing zoom box.');
	    this.removeZoomBox();

	    // Request all of these parcels from the server.
	    this.debugConsole.write('GISMap.mapTool.SELECT.onMouseUp() - Requesting that the parcels be selected.');
	    this.isLoadingMap(true);
	    this.communicationsHandler.request_getSelectedParcels.call(this, newMin.x, newMin.y, newMax.x, newMax.y);

	    return null;
    };

    GISMap.mapTool.SELECT.onMouseDoubleClick = null;

    GISMap.mapTool.MEASURE = new GISMapTool();
    GISMap.mapTool.MEASURE.name = 'MEASURE';
    GISMap.mapTool.MEASURE.lineElement = null;
    GISMap.mapTool.MEASURE.distanceBox = null;
    GISMap.mapTool.MEASURE.startCoordinate = null;
    GISMap.mapTool.MEASURE.mouseCursor = 'crosshair';
    GISMap.mapTool.MEASURE.onSet = function() {
	    /// <summary>STATIC - MEASURE tool setup.  Determines if this tool can be used.</summary>
	    /// <returns type="Boolean" />

        this.debugConsole.write('GISMap.mapTool.MEASURE.onSet() - Map tool set to MEASURE.');


        this.debugConsole.write('GISMap.mapTool.MEASURE.onSet() - Creating HTML elements to display measurements.');
	    // Create the box which will hold the distance display.
	    var containerElement = document.createElement('div');
	    containerElement.className = 'measureBar';
	    containerElement.appendChild(document.createTextNode('Distance: '));
	    var subElement = document.createElement('span');
	    subElement.appendChild(document.createTextNode('0'));
	    containerElement.appendChild(subElement);
	    containerElement.appendChild(document.createTextNode(' ft.'));
	    GISMap.mapTool.MEASURE.distanceBox = containerElement;
	    this.mapContainer.appendChild(containerElement);

	    return true;
    };

    GISMap.mapTool.MEASURE.onUnset = function (reason) {
	    // Hide the line.
        if (GISMap.mapTool.MEASURE.lineElement) {
            this.debugConsole.write('GISMap.mapTool.MEASURE.onUnset() - Hiding the measure line.');
		    GISMap.mapTool.MEASURE.lineElement.parentNode.removeChild(GISMap.mapTool.MEASURE.lineElement);
		    GISMap.mapTool.MEASURE.lineElement.onclick = null;
		    GISMap.mapTool.MEASURE.lineElement = null;
	    }
    	
	    // Hide the distance information box.
	    if (GISMap.mapTool.MEASURE.distanceBox) {
	        this.debugConsole.write('GISMap.mapTool.MEASURE.onUnset() - Hiding the measurement display box.');
		    GISMap.mapTool.MEASURE.distanceBox.parentNode.removeChild(GISMap.mapTool.MEASURE.distanceBox);
		    GISMap.mapTool.MEASURE.distanceBox = null;
	    }
    };

    GISMap.mapTool.MEASURE.onMouseDown = function(X, Y) {
	    /// <summary>STATIC - PAN tool initializer.  Stores the current location in the panMouseX and panMouseY attributes.</summary>
	    /// <param name="X" type="Integer" />
	    /// <param name="Y" type="Integer" />
	    /// <returns type="void" />

	    X += 2;
	    Y += 2;

	    // Create a new line using VML.
	    var initialFrom = (X) + 'px,' + (Y) + 'px';
	    if (!GISMap.mapTool.MEASURE.lineElement) {
	        this.debugConsole.write('GISMap.mapTool.MEASURE.onMouseDown() - Creating a line HTML element using VML.');
		    GISMap.mapTool.MEASURE.lineElement = document.createElement('v:line');
		    GISMap.mapTool.MEASURE.lineElement.style.cursor = GISMap.mapTool.MEASURE.mouseCursor;
		    GISMap.mapTool.MEASURE.lineElement.style.position = 'absolute';
		    GISMap.mapTool.MEASURE.lineElement.style.zIndex = 2;
    	
		    // Set the stroke and opacity
		    var strokeElement = document.createElement('v:stroke');
		    strokeElement.color = 'red';
		    strokeElement.weight = 2;
		    strokeElement.opacity = 0.75;
		    strokeElement.startarrow = 'diamond';
		    strokeElement.endarrow = 'oval';
		    GISMap.mapTool.MEASURE.lineElement.appendChild(strokeElement);
    		
		    // Add a shadow
		    var shadowElement = document.createElement('v:shadow');
		    shadowElement.on=true;
		    shadowElement.opacity = 0.5;
		    shadowElement.strokecolor = 'black';
		    GISMap.mapTool.MEASURE.lineElement.appendChild(shadowElement);
    		
		    this.mapContainer.appendChild(GISMap.mapTool.MEASURE.lineElement);
    	
		    // It goes away if clicked.
		    var GISMapObj = this;
		    GISMap.mapTool.MEASURE.lineElement.onclick = function() {
			    GISMap.mapTool.MEASURE.lineElement.parentNode.removeChild(GISMap.mapTool.MEASURE.lineElement);
			    GISMap.mapTool.MEASURE.lineElement.onclick = null;
			    GISMap.mapTool.MEASURE.lineElement = null;
		    };
	    }
    	
	    GISMap.mapTool.MEASURE.lineElement.from = initialFrom;
	    GISMap.mapTool.MEASURE.lineElement.to = initialFrom;
    	
	    // Set the initial position in pixels (from and to will be converted to "pt", which is worthless).
	    this.debugConsole.write('GISMap.mapTool.MEASURE.onMouseDown() - Storing starting point for measurement line (X = ' + X + 'px; Y = ' + Y + 'px;)');
	    GISMap.mapTool.MEASURE.startCoordinate = {x:X,y:Y};
    	
	    // That is all, the onMouseMove() function handles the movement.
    };

    GISMap.mapTool.MEASURE.onMouseMove = function(X, Y) {
	    /// <summary>STATIC - MEASURE tool updater.  Moves the image as the mouse moves.</summary>
	    /// <param name="X" type="Integer" />
	    /// <param name="Y" type="Integer" />
	    /// <returns type="void" />

	    X += 2;
	    Y += 2;

	    // Do not go outside the bounds of the image.
	    if (X < 1) {X = 1;}
	    if (Y < 1) {Y = 1;}
	    if (X >= (this.currentContext.imageWidth - 1)) {X = this.currentContext.imageWidth - 1;}
	    if (Y >= (this.currentContext.imageHeight - 1)) {Y = this.currentContext.imageHeight - 1;}
	    GISMap.mapTool.MEASURE.lineElement.to = (X) + 'px,' + (Y) + 'px';

	    var startPixels = GISMap.mapTool.MEASURE.startCoordinate;
	    var startFeet = this.currentContext.convertPixelsToFeet(startPixels);
	    var endFeet = this.currentContext.convertPixelsToFeet({x:X,y:Y});

	    // Get distance between these points.
	    var span = GISMap.mapTool.MEASURE.distanceBox.getElementsByTagName('span')[0];
	    span.innerHTML = GISMap.mapTool.MEASURE.addCommas(GISMap.mapTool.MEASURE.getDistance(startFeet, endFeet));

	    return null;
    };

    GISMap.mapTool.MEASURE.onMouseUp = function(X, Y) {
	    /// <summary>STATIC - MEASURE tool finalizer.  If the map moved then fire a request to have the map updated.</summary>
	    /// <param name="X" type="Integer" />
	    /// <param name="Y" type="Integer" />
	    /// <returns type="void" />

	    X += 2;
	    Y += 2;

	    // Convert "from" to feet.
	    this.debugConsole.write('GISMap.mapTool.MEASURE.onMouseUp() - Converting pixels to feet.');
	    var startPixels = GISMap.mapTool.MEASURE.startCoordinate;
	    var startFeet = this.currentContext.convertPixelsToFeet(startPixels);
	    var endFeet = this.currentContext.convertPixelsToFeet({x:X,y:Y});

	    // Get distance between these points.
	    this.debugConsole.write('GISMap.mapTool.MEASURE.onMouseUp() - Calculating and displaying distance.');
	    var span = GISMap.mapTool.MEASURE.distanceBox.getElementsByTagName('span')[0];
	    span.innerHTML = GISMap.mapTool.MEASURE.addCommas(GISMap.mapTool.MEASURE.getDistance(startFeet, endFeet));

	    GISMap.mapTool.MEASURE.startCoordinate = null;

	    return null;
    };

    GISMap.mapTool.MEASURE.getDistance = function(start, end) {
	    var xd = end.x - start.x;
	    var yd = end.y - start.y;
	    var distance = Math.round(Math.sqrt((xd * xd) + (yd * yd)));
	    return distance;
    };

    GISMap.mapTool.MEASURE.addCommas = function(nStr)
    {
	    nStr += '';
	    var x = nStr.split('.');
	    var x1 = x[0];
	    var x2 = x.length > 1 ? '.' + x[1] : '';
	    var rgx = /(\d+)(\d{3})/;
	    while (rgx.test(x1)) {
		    x1 = x1.replace(rgx, '$1' + ',' + '$2');
	    }
	    return x1 + x2;
    };