/**
 * @name MapIconMaker
 * @version 1.1
 * @author Pamela Fox
 * @copyright (c) 2008 Pamela Fox
 * @fileoverview This gives you static functions for creating dynamically
 *     sized and colored marker icons using the Charts API marker output.
 */

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */

/**
 * @name MarkerIconOptions
 * @class This class represents optional arguments to {@link createMarkerIcon}, 
 *     {@link createFlatIcon}, or {@link createLabeledMarkerIcon}. Each of the
 *     functions use a subset of these arguments. See the function descriptions
 *     for the list of supported options.
 * @property {Number} [width=32] Specifies, in pixels, the width of the icon.
 *     The width may include some blank space on the side, depending on the
 *     height of the icon, as the icon will scale its shape proportionately.
 * @property {Number} [height=32] Specifies, in pixels, the height of the icon.
 * @property {String} [primaryColor="#ff0000"] Specifies, as a hexadecimal
 *     string, the color used for the majority of the icon body.
 * @property {String} [cornerColor="#ffffff"] Specifies, as a hexadecimal
 *     string, the color used for the top corner of the icon. If you'd like the
 *     icon to have a consistent color, make the this the same as the
 *     {@link primaryColor}.
 * @property {String} [strokeColor="#000000"] Specifies, as a hexadecimal
 *     string, the color used for the outside line (stroke) of the icon.
 * @property {String} [shadowColor="#000000"] Specifies, as a hexadecimal
 *     string, the color used for the shadow of the icon. 
 * @property {String} [label=""] Specifies a character or string to display
 *     inside the body of the icon. Generally, one or two characters looks best.
 * @property {String} [labelColor="#000000"] Specifies, as a hexadecimal 
 *     string, the color used for the label text.
 * @property {Number} [labelSize=0] Specifies, in pixels, the size of the label
 *     text. If set to 0, the text auto-sizes to fit the icon body.
 * @property {String} [shape="circle"] Specifies shape of the icon. Current
 *     options are "circle" for a circle or "roundrect" for a rounded rectangle.
 * @property {Boolean} [addStar = false] Specifies whether to add a star to the
 *     edge of the icon.
 * @property {String} [starPrimaryColor="#FFFF00"] Specifies, as a hexadecimal
 *     string, the color used for the star body.
 * @property {String} [starStrokeColor="#0000FF"] Specifies, as a hexadecimal
 *     string, the color used for the outside line (stroke) of the star.
 */

/**
 * This namespace contains functions that you can use to easily create
 *     dynamically sized, colored, and labeled icons.
 * @namespace
 */
var MapIconMaker = {};

/**
 * Creates an icon based on the specified options in the 
 *   {@link MarkerIconOptions} argument.
 *   Supported options are: width, height, primaryColor, 
 *   strokeColor, and cornerColor.
 * @param {MarkerIconOptions} [opts]
 * @return {GIcon}
 */
MapIconMaker.createMarkerIcon = function(opts) {
  var width = opts.width || 32;
  var height = opts.height || 32;
  var primaryColor = opts.primaryColor || "#ff0000";
  var strokeColor = opts.strokeColor || "#000000";
  var cornerColor = opts.cornerColor || "#ffffff";
   
  var baseUrl = "http://chart.apis.google.com/chart?cht=mm";
  var iconUrl = baseUrl + "&chs=" + width + "x" + height + 
      "&chco=" + cornerColor.replace("#", "") + "," + primaryColor.replace("#", "") + "," + strokeColor.replace("#", "") + "&ext=.png";
  var icon = new GIcon(G_DEFAULT_ICON);
  icon.image = iconUrl;
  icon.iconSize = new GSize(width, height);
  icon.shadowSize = new GSize(Math.floor(width*1.6), height);
  icon.iconAnchor = new GPoint(width/2, height);
  icon.infoWindowAnchor = new GPoint(width/2, Math.floor(height/12));
  icon.printImage = iconUrl + "&chof=gif";
  icon.mozPrintImage = iconUrl + "&chf=bg,s,ECECD8" + "&chof=gif";
  var iconUrl = baseUrl + "&chs=" + width + "x" + height + 
      "&chco=" + cornerColor.replace("#", "") + "," + primaryColor.replace("#", "") + "," + strokeColor.replace("#", "");
  icon.transparent = iconUrl + "&chf=a,s,ffffff11&ext=.png";

  icon.imageMap = [
      width/2, height,
      (7/16)*width, (5/8)*height,
      (5/16)*width, (7/16)*height,
      (7/32)*width, (5/16)*height,
      (5/16)*width, (1/8)*height,
      (1/2)*width, 0,
      (11/16)*width, (1/8)*height,
      (25/32)*width, (5/16)*height,
      (11/16)*width, (7/16)*height,
      (9/16)*width, (5/8)*height
  ];
  for (var i = 0; i < icon.imageMap.length; i++) {
    icon.imageMap[i] = parseInt(icon.imageMap[i]);
  }

  return icon;
}

/**
 * Creates a flat icon based on the specified options in the 
 *     {@link MarkerIconOptions} argument.
 *     Supported options are: width, height, primaryColor,
 *     shadowColor, label, labelColor, labelSize, and shape..
 * @param {MarkerIconOptions} [opts]
 * @return {GIcon}
 */
MapIconMaker.createFlatIcon = function (opts) {
  var width = opts.width || 32;
  var height = opts.height || 32;
  var primaryColor = opts.primaryColor || "#ff0000";
  var shadowColor = opts.shadowColor || "#000000";
  var label = MapIconMaker.escapeUserText_(opts.label) || "";
  var labelColor = opts.labelColor || "#000000";
  var labelSize = opts.labelSize || 0;
  var shape = opts.shape ||  "circle";
  var shapeCode = (shape === "circle") ? "it" : "itr";

  var baseUrl = "http://chart.apis.google.com/chart?cht=" + shapeCode;
  var iconUrl = baseUrl + "&chs=" + width + "x" + height + 
      "&chco=" + primaryColor.replace("#", "") + "," + 
      shadowColor.replace("#", "") + "ff,ffffff01" +
      "&chl=" + label + "&chx=" + labelColor.replace("#", "") + 
      "," + labelSize;
  var icon = new GIcon(G_DEFAULT_ICON);
  icon.image = iconUrl + "&chf=bg,s,00000000" + "&ext=.png";
  icon.iconSize = new GSize(width, height);
  icon.shadowSize = new GSize(0, 0);
  icon.iconAnchor = new GPoint(width / 2, height / 2);
  icon.infoWindowAnchor = new GPoint(width / 2, height / 2);
  icon.printImage = iconUrl + "&chof=gif";
  icon.mozPrintImage = iconUrl + "&chf=bg,s,ECECD8" + "&chof=gif";
  icon.transparent = iconUrl + "&chf=a,s,ffffff01&ext=.png";
  icon.imageMap = []; 
  if (shapeCode === "itr") {
    icon.imageMap = [0, 0, width, 0, width, height, 0, height];
  } else {
    var polyNumSides = 8;
    var polySideLength = 360 / polyNumSides;
    var polyRadius = Math.min(width, height) / 2;
    for (var a = 0; a < (polyNumSides + 1); a++) {
      var aRad = polySideLength * a * (Math.PI / 180);
      var pixelX = polyRadius + polyRadius * Math.cos(aRad);
      var pixelY = polyRadius + polyRadius * Math.sin(aRad);
      icon.imageMap.push(parseInt(pixelX), parseInt(pixelY));
    }
  }

  return icon;
};


/**
 * Utility function for doing special chart API escaping first,
 *  and then typical URL escaping. Must be applied to user-supplied text.
 * @private
 */
MapIconMaker.escapeUserText_ = function (text) {
  if (text === undefined) {
    return null;
  }
  text = text.replace(/@/, "@@");
  text = text.replace(/\\/, "@\\");
  text = text.replace(/'/, "@'");
  text = text.replace(/\[/, "@[");
  text = text.replace(/\]/, "@]");
  return encodeURIComponent(text);
};



function HtmlControl(html, options){
	this.html=html;
	this.isVisible=true;
	this.isPrintable=false;	
	this.isSelectable=false;
	if(options){
		this.isVisible=(options.visible===false)?false:true;
		this.isPrintable=(options.printable===true)?true:false;
		this.isSelectable=(options.selectable===true)?true:false;
	}
}

HtmlControl.prototype=new GControl();

HtmlControl.prototype.initialize=function(map){
	this.div=document.createElement('div');
    this.div.className = 'info';
	this.div.innerHTML=this.html;
	this.setVisible(this.isVisible);
	map.getContainer().appendChild(this.div);
	return this.div;
};

HtmlControl.prototype.getDefaultPosition=function(){
	return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(0,0));
};

HtmlControl.prototype.selectable=function(){
	return this.isSelectable;
};

HtmlControl.prototype.printable=function(){
	return this.isPrintable;
};

HtmlControl.prototype.setVisible=function(bool){
	this.div.style.display=bool ? '':'none';
	this.isVisible=bool;
};

HtmlControl.prototype.visible=function(){
	return this.isVisible;
}

/**
 *  Hides the information box placed on a map
 *
 *  @param  HTMLElement HTML element where this call made from
 */
function hideInfo( where ) {
    
    /* Locate the outer <div> of the information box, and add class "hidden"
       to it to make box invisible */
       
    var info = getParentByClass( where, 'info' );
    info.className += ' hidden';  
}


/**
 *  Draws a Google map
 *
 *  @param  string  widgetId - HTML id of outermost map widget <div>
 *  @param  float   centreLat - latitude of map centre
 *  @param  float   centreLng - longitude of map centre
 *  @param  int     zoom - zoom level of map
 *  @param  array   layerTypes - type of resource in each layer
 *  @param  array   point - location, type and info for each point
 */
function OWMap( widgetId, centreLat, centreLng, zoom, layerTypes, points ) {
    
    var map = null;                     // the Google map object
    var widget = null;                  // widget's top level DOM element
    var canvas = null;                  // HTML DOM element where map located
    var menu = [];                      // DOM element of layer menu checkboxes
    var layers = [];                    // markers in each layer
    var visible = [];                   // visibility of each layer
    var pLatLng = [];                   // lat,long of each displayed marker.
    var myMarker = null;                // marker for one's own position
    var myLatLng = null;                // position of myMarker if moved
    var info = null;                    // information overlay
    var infoHtml = null;                // HTML to put in info message.
    var editing = false;                // whether editing map
    var gAjax = null;                   // object for issing Ajax requests
    var ajaxBusy = false;               // whether an Ajax call in progress

    /* Initialise the private variables which hold visibility of each layer
       and the markers in each layer */
       
    for ( i in layerTypes ) {
        visible.push( true );
        pLatLng.push( [ [], [] ] );
        layers.push( [] );
    }
    
    /* Create browser-independent object to handle AJAX requests */
    
    gAjax = GXmlHttp.create();

    var _setEditable = function() {
		
        /* If already editing, don't do anything */
        
        if ( editing ) return;
        editing = true;
        
        /* Add any information as an HTML control */
           
        if ( info ) map.addControl( info );
        
        /* Add a draggable marker to map that user can drag to their
           location. "dragend" event is registered so that new coords are
           stored in member variables if user does drag marker */
           
        myMarker = new GMarker( map.getCenter(), { draggable: true } );
        GEvent.addListener( myMarker, "dragend", function() {
            myLatLng = myMarker.getLatLng();
        });
        map.addOverlay( myMarker );
        
        /* Call standard OW function to show widget input form */
        
        show2( widget, 'input' );
    }
    
    var _setEditable2 = function() {
        
        /* Replace change location button with editing ones */
        
        _visible( 'change', 'a', widget, false );
        _visible( 'edit', 'a', widget, true );
        
        /* Add any information as an HTML control to the map */
           
        if ( info ) map.addControl( info );
        
        /* Hide all layers so user can just see marker for own position */
        
        for ( var i = 0; i < visible.length; i++ )
            if ( visible[i] == true && layers[i].length > 0 ) {
                for ( j in layers[i] ) layers[i][j].hide();
                visible[i] = false;
            }

        /* Make the address field editable */
        
        var address = getElementsByClassName( 'address', 'input', widget );
        address[0].className = removeClass( address[0].className, 'readonly' );
        address[0].readOnly = false;
        
        /* Add a draggable marker to map that user can drag to their
           location. "dragend" event is registered so that new coords are
           stored in member variables if user does drag marker */
           
        myMarker = new GMarker( map.getCenter(), { draggable: true } );
        GEvent.addListener( myMarker, "dragend", function() {
            myLatLng = myMarker.getLatLng();
        });
        map.addOverlay( myMarker );
        
    }
    
    this.setEditable = _setEditable;
    this.setEditable2 = _setEditable2;
    
    /*
     *  inner function draw which actually calls Google functions to draw map
     *
     *  @param  string showMyMarker whether to show "myposition" marker
     */
    this.draw = function( showMyMarker ) {
        
        var showMyMarker = showMyMarker === 'onclick' || showMyMarker === 'show'
                           ? showMyMarker : null;
        
        /* Locate <div> canvas where map will be painted */
        
        widget = document.getElementById( widgetId );
        canvas = getElementsByClassName( 'map-canvas', '*', widget );
        if ( !canvas ) return;
        
        /* Locate map layer menu checkboxes, if any, and store in menu
           member variable to facilitate enabling/disabling them */
        
        for ( var i in layerTypes ) menu.push( null );
        var menuTop = getElementsByClassName( 'map-menu', 'div', widget );
        if ( menuTop[0] !== undefined ) {
            var checkbox;
            for ( var i in layerTypes ) {
                checkbox = getElementsByClassName( layerTypes[i], 'td', 
                                                                 menuTop[0] );
                if ( checkbox[0] === undefined ) continue;
                checkbox = getElementsByClassName( '*', 'input', checkbox[0] );
                if ( checkbox[0] !== undefined ) {
                    menu[i] = checkbox[0];
                    menu[i].checked = true;
                }
            }
        }

        /* Instantiate the Google map object and set its centre & zoom level */    
    
        map = new GMap2( canvas[0] );
        map.setCenter( new GLatLng( centreLat, centreLng ), zoom );
        map.addControl( new GSmallMapControl() );
        
        /* function _requestPoints will be called after map moved */
        
        if ( widgetId !== 'group-map' && widgetId !== 'project-map' )
            GEvent.addListener( map, 'moveend', _requestPoints );

        /* Display myMarker if input argument so dictates */
        
        if ( showMyMarker !== null ) {
            
            /* Construct base URL for marker event Ajax requests to
               lookup location and re-center map */
               
            var url = '/ajax/map-widget?config=' + 
                      $( 'input[name=config]', widget ).attr( 'value' ) +
                      '&latlng=';
                      
            if ( showMyMarker === 'show' ) {
                myMarker = new GMarker( map.getCenter(), { draggable: true } );
                GEvent.addListener( myMarker, "dragend", function() {
                    $( 'div.error div', widget ).removeClass( 'hidden' );
                    ow.ajax.url( url +  myMarker.getLatLng().toUrlValue() +
                                 '&zoom=' + map.getZoom() );
                } );
                GEvent.addListener( map, 'click', function( marker, point ) {
                    myMarker.setLatLng( point );
                    $( 'div.error div', widget ).removeClass( 'hidden' );
                    ow.ajax.url( url +  myMarker.getLatLng().toUrlValue() +
                                 '&zoom=' + map.getZoom() );
                } );
                map.addOverlay( myMarker );
            } else {
                GEvent.addListener( map, 'click', function( marker, point ) {
                    $( 'div.error div', widget ).removeClass( 'hidden' );
                    ow.ajax.url( url +  point.toUrlValue() + '&zoom=' +
                                                            map.getZoom()  );
                } );
            }
        }
        
        /* Loop through points to be displayed creating a marker at each point,
           with an associated information window */
       
        _drawMarkers( points );
        
        /* See if there is information to be shown as an information box
           overlaid on the map - if so, add it as an HTML control */
           
        info = getElementsByClassName( 'info', 'div', widget );
        if ( info.length != 0 && info[0].innerHTML.length > 0 ) {
            var editNow = info[0].className.indexOf( 'initial' ) == -1 ? false 
                                                                       : true;
            info = new HtmlControl( info[0].innerHTML );
            if ( editNow ) _setEditable2();
        }
    }

    /*
     * Sets/unsets visibility of specified elements 
     */
    function _visible( className, tag, elem, visible ) {

        var elems = getElementsByClassName( className, tag, elem );
        
        if ( visible == true ) { 
            for ( var i = 0; i < elems.length; i++ ) 
                elems[i].className = 
                        removeClass( elems[i].className, 'hidden' );
        } else {
            for ( var i = 0; i < elems.length; i++ )
                elems[i].className += ' hidden';
        } 
    }
        
        
    /**
     *  Draws the markers on the map
     *
     *  @param  array   points - array of points to draw
     */
    function _drawMarkers( points ) {

        var latLng = null;              // marker lat, long as GLatLng object
        var sLatLng = null;             // marker lat, long as string
        var html = null;                // HTML for point info window
        var bounds = null;              // area to zoom into if marker clicked
        var layerId = null;             // layer id for point
        var link = null;                // link to associated resource 
        var info = null;                // info to show about resource
        
        for ( i in points ) {
            
            layerId = points[i][2];
            
            /* If marker is already visible no need to draw again */
            
            sLatLng = points[i][0] + ',' + points[i][1];
            if ( _in_array( sLatLng, pLatLng[layerId][0] ) != -1 ) continue;

            latLng = new GLatLng( points[i][0], points[i][1] );
            
            html = '';
            bounds = null;
            var scc = layerTypes[layerId] === 'SEV' ? true : false;
            
            if ( points[i][4] instanceof Array ) {
                
                /* There is an array of titles and links */
                
                for ( j in points[i][4] ) {
                    html += '<a href="' + points[i][5][j] +
                            '" target="_blank">' + 
                    points[i][4][j] + '</a><br/>';
                    if ( j == 4 ) break;
                }

            } else if ( points[i].length == 6 && scc === true ) {
                link = points[i][5];
                info = points[i][4] + '|||';
                info = info.split( '|', 4 );
                
                html = '<div style="width: 250px; height: 110px;"/>';
                html += '<div class="scroll">';
                html += '<div class="marker">'
                html += '<img src="/wp-content/themes/OneClimateV3/images' +
                        '/calendar.png" />';
                html += '<h2>' + info[0] + '</h2>';
                html += '<div class="text">' + info[2] + '</div>';
                html += '</div>';
                html += '</div>';
                                                                      
            } else if ( points[i].length == 6 ) {
                
                /* Just a single resource to describe */
                
                link = points[i][5];
                info = points[i][4] + '|||';
                info = info.split( '|', 4 );
                if ( info[2].length > 1 )
                    info[2] = info[2].replace( '#~', '<img src="/wp-content/themes/OneClimateV3/images/at.gif" />' );
                
                html = '<div style="width:250px; height: 80px" />' +
                       '<table style="position: absolute;" class="marker-info"'
                       + 'cellspacing="0"><tr>';
                if ( info[1].length > 0 )
                    html += '<td class="image"><a href="' + link + 
                            '" target="_blank"><img src="' + info[1] + 
                            '" /></a></td>';
                else if ( layerTypes[layerId] == 'CDR' || 
                                            layerTypes[layerId] == 'LOB' )
                    html += '<td class="image"><img src="/wp-content/themes/' + 
                            'OneClimateV3/oneclimate/scc.png" /></td>';
                    
                if ( link == '' )
                    html += '<td class="text"><h2>'+ info[0] + '</h2>';
                else
                    html += '<td class="text"><h2><a href="' + link + 
                            '" target="_blank">' + info[0] + '</a></h2>';
                if ( info[2].length > 0 ) html += '<p>' + info[2] + '</p>';
                html += '</td></tr></table>';
                        
            } else {
                
                /* just an area to zoom into */
                
                bounds = points[i][4];
            }
                
            marker = _createMarker( latLng, bounds, html, layerTypes[layerId], 
                                                                points[i][3] );
            layers[layerId].push( marker );
            pLatLng[layerId][0].push( sLatLng );
            pLatLng[layerId][1].push( ( layers[layerId].length - 1 ) );
            map.addOverlay( marker ); 
            if ( points[i].length == 6 && info instanceof Array &&
                 info.length > 3 && info[3] === 'open' )
                marker.openInfoWindowHtml( html, { maxWidth: '250', 
                                suppressMapPan: true, 
                                buttons:{close:{height:12,width:22}} } );
            if ( !visible[layerId] ) marker.hide();
            
        }
        
        /* Disable checkboxes for layers that don't have visible markers */
           
        for ( var i in menu )
            menu[i].disabled = layers[i].length === 0 ? true : false;

    }
       
    function _requestPoints() {
        
        /* If ajaxBusy is true then a previous request has not completed 
           - so abort it */
           
        if ( ajaxBusy ) gAjax.abort();
        ajaxBusy = false;
        
        /* Construct AJAX URL to request new set of points which specifies 
           map bounds and zoom level, and types shown on map */
           
        var bounds = map.getBounds();
        // alert( bounds + ' zoom = ' + map.getZoom() );
        gAjax.open( 'GET', '/map?zoom=' + map.getZoom() + '&type=' + 
                    layerTypes + '&sw=' + bounds.getSouthWest().toUrlValue() +
                    '&ne=' + bounds.getNorthEast().toUrlValue(), true );
                    
        /* Register _updatePoints as response handler - it wil draw
           returned points on map */
           
        gAjax.onreadystatechange = _updatePoints;
        
        /* Send request, and set Ajax busy flag to true */
        
        gAjax.send( '' );
        ajaxBusy = true;
    }
    
    /**
     *  Process AJAX response to update points on map
     */
    function _updatePoints() {
        if ( gAjax.readyState != 4 ) return;
        
        ajaxBusy = false;
        
        // eval( alert( gAjax.responseText ) );
        var response = eval( '(' + gAjax.responseText + ')' );
        
        /* Construct a 2-D array containing points in response */
        
        var newLatLng = [];
        for ( var i in layerTypes ) newLatLng.push( [] );
        for ( var i in response.points[1] )
            newLatLng[response.points[1][i][2]].push( 
                 response.points[1][i][0] + ',' + response.points[1][i][1] );
                 
        /* for ( var i in newLatLng )
            alert( 'New points in layer ' + i + ': ' + newLatLng[i] + "\n" +
                   'old points: ' + pLatLng[i][0] ); */

        /* Now loop over pLatLng which holds the previously displayed points
           removing any not in the response - i.e. points that have disappeared
           as the map was moved */
           
        for ( var layerId in layerTypes ) {
            for ( var i = pLatLng[layerId][0].length - 1; i >= 0 ; i-- )
                if ( _in_array( pLatLng[layerId][0][i], 
                                            newLatLng[layerId] ) == -1 ) {
                    markerPtr = pLatLng[layerId][1][i];
                    map.removeOverlay( layers[layerId][markerPtr] );
                    layers[layerId].splice( markerPtr, 1 );
                    pLatLng[layerId][0].splice( i, 1 );
                    pLatLng[layerId][1].splice( i, 1 );
                    for ( var j in pLatLng[layerId][1] )
                        if ( pLatLng[layerId][1][j] > markerPtr ) 
                                                    pLatLng[layerId][1][j]--;
            }
        }

        /* for ( var i in pLatLng )
            alert( 'After drop layer ' + i + ': ' + pLatLng[i][0] ); */
        
        /* Add new markers */
        
        _drawMarkers( response.points[1] );

        /* for ( var i in pLatLng )
            alert( 'After add layer ' + i + ': ' + pLatLng[i][0] ); */
        
        ajaxBusy = false;
    }
    
    /* Inner function "toggleLayer" toggles the visibility of specified
       "layer" */
       
    this.toggleLayer = function( layer ) {
        
        if ( layer >= layerTypes.length ) return;
        
        if ( visible[layer] ) {
            for ( i in layers[layer] ) layers[layer][i].hide();
            visible[layer] = false;
        } else {
            for ( i in layers[layer] ) layers[layer][i].show();
            visible[layer] = true;
        }
        
    }
    
    
    /**
     *  Submits user position to server
     *
     *  @param  HTMLElement HTML element where function called from
     */
    this.update = function( where ) {
        
        /* If user has moved the "my position" marker, then store the
           lat, long values in hidden inut field so that it gets sent
           to the server */
           
        if ( myLatLng != null ) {
            var elem = getElementsByClassName( 'latlng', 'input', widget );
            if ( elem.length != 0 )
                elem[0].value = myLatLng.lat() + ',' + myLatLng.lng();
            myLatLng = null;
        }
        
        /* Call standard method to submit widget updates */
        
        editing = false;
        editWidget( where );
    }
    
    /**
     *  Performs tidy up when user opts to cancel editing 
     */
    this.cancelEdit = function() {
        
        /* Remove the overlay with the "my position" marker */
        
        map.removeOverlay( myMarker );
        myMarker = null;
        myLatLng = null;
        
        /* Remove the map information control */
        
        map.removeControl( info );
        
        /* Call the standard method to hide the widget edit form */
        
        editing = false;
        hide2( widget, 'input' );        
    }
    
    
    /**
     *  Performs tidy up when user opts to cancel editing 
     */
    this.cancelEdit2 = function() {
        
        /* Make change button visible again */
        
        _visible( 'edit', 'a', widget, false );
        _visible( 'change', 'a', widget, true );
        
        /* Remove the overlay with the "my position" marker */
        
        map.removeOverlay( myMarker );
        myMarker = null;
        myLatLng = null;
        
        /* Remove the map information control */
        
        map.removeControl( info );
        
        /* Re-show layers that were previously visible */
        
        for ( var i = 0; i < menu.length; i++ )
            if ( menu[i].checked == true && layers[i].length > 0 ) {
                for ( j in layers[i] ) layers[i][j].show();
                visible[i] = true;
            }
            
        /* Make the address field editable */
        
        var address = getElementsByClassName( 'address', 'input', widget );
        address[0].className += ' readonly';
        address[0].readOnly = true;
            
        /* Call the standard method to hide the widget edit form */
        
        editing = false;
        hide2( widget, 'input' );        
    }


    /**
     *  Create a marker on map
     *
     *  @param  gLatLng point - lat, lon where marker placed
     *  @param  string|null bounds - area to zoom to if marker clicked
     *  @param  string html to display in info window (if bounds null)
     *  @param  string type of resource e.g. NWS, EVT
     *  @param  int count of individual resources in point
     */
    function _createMarker( point, bounds, html, type, count ) {
    
        /* Define icon characteristics - starting with colour which
           is dependent on resource type */
       
        var opts = {};
        switch( type ) {
            case 'USR': opts.primaryColor = '#0069A5';      break;
            case 'CDR': opts.primaryColor = '#ED008C';      break;
            case 'LOB': opts.primaryColor = '#0063DC';      break;
            case 'SUB':
            case 'PRT':
            case 'GRP': opts.primaryColor = '#FF0000';      break;
            case 'SCH': opts.primaryColor = '#FF9900';      break;
            case 'EVT':
            case 'UEV':
            case 'UAE':
            case 'CLC': opts.primaryColor = '#006600';      break;
            case 'SEV': opts.primaryColor = '#8819AA';      break;
            case 'XEV': opts.primaryColor = '#33CC33';      break;
            case 'PRJ':
            case 'NWS': opts.primaryColor = '#000000';      break;
            case 'PRO': opts.primaryColor = '#99CC33';      break;
            case 'PRA': opts.primaryColor = '#339933';      break;
            default   :
            if ( parseInt( type.slice( 1 ) ) == NaN )  
                opts.primaryColor = '#000000';
            else if ( type.charAt( 0 ) === 'G' )
                opts.primaryColor = '#FF0000';
            else if ( type.charAt( 0 ) === 'H' )
                opts.primaryColor = '#006600';
            else
                opts.primaryColor = '#AA0000';
        }
        if ( count > 1 ) {
        
            /* Circle marker for clusters - size depends upon cluster count */
        
            opts.label = '' + count;
            opts.labelColor = '#FFFFFF';
            opts.width = 24;
            if ( count < 10 )
                opts.width = 24;
            else if ( count < 100 )
                opts.width = 32;
            else
                opts.width = 40;
            opts.height = opts.width;
            icon = MapIconMaker.createFlatIcon( opts );
        
        } else {
        
            /* Pin marker for individual resources */
        
            opts.width = type == 'SUB' || opts.primaryColor === '#AA0000' 
                                       ? ( type == 'NUL' ? 1 : 20 ) : 25;
            opts.height = opts.width;
            icon = MapIconMaker.createMarkerIcon( opts );
        }
        
        /* Create marker object at required location, and open info window
           when user clicks on map */
       
        var marker = new GMarker( point, icon );
        if ( bounds == null ) {
            // html = '<div class="info">' + html + '</div>';
            GEvent.addListener( marker, "click", function() {
                marker.openInfoWindowHtml( html, {maxWidth: '250', 
                                    buttons:{close:{height:12,width:22}} } );
            } );
        } else
            GEvent.addListener( marker, 'click', function() {
                var area = bounds.split( ',' );
                for ( var i in area ) area[i] = parseFloat( area[i] );
                var sw = new GLatLng( area[0], area[1] );
                var ne = new GLatLng( area[2], area[3] );
                var centre = new GLatLng( 0.5 * ( area[0] + area[2] ),
                                          0.5 * ( area[1] + area[3] ) );
                area = new GLatLngBounds( sw, ne );
                var zoom = map.getBoundsZoomLevel( area );
                map.setZoom( zoom );
                map.panTo( centre );
                // alert( area + ' zoom: ' + zoom + ' centre: ' + centre );
                
            } );
        
        return marker;
    }
    
    function _in_array( needle, haystack ) {
        if ( haystack.length == 0 ) return -1;
        for ( var i in haystack )
            if ( haystack[i] == needle ) return i;
        return -1;
    }
        
}

ow.map = {
    find:   function( id ) {
        var url = id.split( '-' ).slice( 0, -2 ).join( '-' );
        url = '/ajax/' + url + '?config=' + 
              $( '#' + id + ' input[name=config]' ).attr( 'value' ) + 
              '&user-location=' +
              $( '#' + id + ' input[name=user-location]' ).attr( 'value' ); 
        $( '#' + id + ' div.error div' ).removeClass( 'hidden' );
        ow.ajax.url( url );
    },
    
    findOnEnter: function( id, event ) {

        if ( !event ) var event = window.event;
    
        if ( event.keyCode ) 
            var key = event.keyCode;
        else if ( event.which ) 
            var key = e.which;
    
        if ( key != 13 ) return true;
        event.preventDefault();
        ow.map.find( id ); 
        return false;
    }
}


