var log = new Date().getTime() + "\tStart" + "<br>\n";
function debug(msg) {
     log += new Date().getTime() + "\t" + msg + "<br>\n";
// alert(arg);
}

function popupLog()
{
    var win = window.open("log.html", "logWindow");
    win.focus();
}




/* Start of FMF API */
/* TESTING */
/* Constants */
var DP_BASE_URL = 'http://www.indiadevelopmentindicators.org';
var DP_BASE_JSON = 'http://www.indiadevelopmentindicators.org/map/json';
var DP_BASE_IMAGE = 'http://www.indiadevelopmentindicators.org/0113/static/images';
var DP_BASE_IMAGE_INFO ='http://www.indiadevelopmentindicators.org/static/images/map/info_tool';
var DP_DIALOG = 100;
var DP_DIALOG_PLACE = DP_DIALOG + 1;
var DP_DIALOG_LEGEND = DP_DIALOG + 2;
var DP_DIALOG_INDICATOR = DP_DIALOG + 3;
var DP_DIALOG_CUSTOM = DP_DIALOG + 4;
var DP_FEATURE_BOUNDARY_METRO = 'sbound_msa_pmsa';
var DP_FEATURE_BOUNDARY_PLACE = 'sbound_place';
var DP_FEATURE_BOUNDARY_STATE = 'sbound_state';
var DP_FEATURE_BOUNDARY_ZCTA = 'sbound_zcta';
var DP_FEATURE_BOUNDARY_COUNTY = 'sbound_county';
var DP_FEATURE_BOUNDARY_CENSUS_TRACT = 'sbound_tract';
var DP_FEATURE_BOUNDARY_BLOCK_GROUP = 'sbound_blockgroup';
var DP_FEATURE_BOUNDARY_CUSTOM_GEOGRAPHY = 'sbound_custom_geo';
var DP_FEATURE_LABEL_METRO = 'slabel_msa_pmsa';
var DP_FEATURE_LABEL_PLACE = 'slabel_place';
var DP_FEATURE_LABEL_STATE = 'slabel_state';
var DP_FEATURE_LABEL_ZCTA = 'slabel_zcta';
var DP_FEATURE_LABEL_COUNTY = 'slabel_county';
var DP_FEATURE_LABEL_CENSUS_TRACT = 'slabel_tract';
var DP_FEATURE_LABEL_BLOCK_GROUP = 'slabel_blockgroup';
var DP_FEATURE_LABEL_CUSTOM_GEOGRAPHY = 'slabel_custom_geo';
var DP_FEATURE_LAYER_BASE = 'base';
var DP_FEATURE_LAYER_POLY = 'poly';
var DP_FEATURE_LAYER_POINTLINE = 'pointline';
var DP_FEATURE_LAYER_CITIES = 'cities';
var DP_FEATURE_LAYER_THEMATIC = 'thematic';

var DP_FEATURE_LAYER_BOUNDARY_DYNAMIC = 'label_follow_thematic';
var DP_FEATURE_LAYER_LABEL_DYNAMIC = 'boundary_follow_thematic';

function DPBrowserIsCompatible() {
	if (!window.RegExp) return false;
	return true;
}


var FEATURE_PRIORITY = new Array();
FEATURE_PRIORITY[DP_FEATURE_LAYER_BASE] = 0;
FEATURE_PRIORITY[DP_FEATURE_LAYER_THEMATIC] = 1;
FEATURE_PRIORITY[DP_FEATURE_LAYER_POLY] = 2;
FEATURE_PRIORITY[DP_FEATURE_LAYER_POINTLINE] = 3;
FEATURE_PRIORITY[DP_FEATURE_LAYER_CITIES] = 4;
FEATURE_PRIORITY[DP_FEATURE_LAYER_BOUNDARY_DYNAMIC] = 5;
FEATURE_PRIORITY[DP_FEATURE_LAYER_LABEL_DYNAMIC] = 6;

var BOUNDARY_DATASET_ID_TO_LAYERPREFIX = {"8":"tract","99":"custom_geo","10":"place","13":"zcta","9":"msa_pmsa","2":"state","14":"county",28:"cb_2_geo_dc_ward",29:"cb_2_geo_dc_cluster",34:"cb_nyc_commdist",35:"cb_nyc_schdist",36:"cb_nyc_plcprct",37:"cb_nyc_subboro",38:"us_la_subsidence",39:"world",41:"oecd_region", 40:"world_state", 7:"world_country"};

var ZOOMLEVEL_TO_MPP = [-1,2.381249,4.762497,9.524995,19.04999,38.099979,76.199959,152.399918,304.799835,609.599671,1219.199342,2438.398683,4876.797367,9753.594733,19507.189466,39014.378932];
var ZOOMLEVEL_THEMATIC_DATASETS = {"6":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,43,42],"11":[null,null,null,null,null,43,42],"3":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,43,42],"7":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,43,42],"9":[null,null,null,null,null,null,43,42],"12":[null,null,null,null,null,43,42],"2":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,43,42],"15":[null,null,null,7,null,null,43,42],"14":[null,null,null,7,null,null,43,42],"8":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,43,42],"1":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,43,42],"4":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,43,42],"13":[null,null,null,7,null,null,43,42],"10":[null,null,null,null,null,null,43,42],"5":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,43,42]};
var THEMATIC_BOUNDARY_ZOOMLEVELS = {"7":{"upper":1,"lower":3},"42":{"upper":1,"lower":15},"43":{"upper":1,"lower":15}};

var BOUNDARY_DATASET_ID_TO_DISPLAY_NAME = {"8":"Census Tract","99":"Custom Geography","10":"Place","13":"Zip Code","9":"Metropolitan Area","2":"State","14":"County",28:"D.C. Ward",29:"D.C. Cluster",34:"NYC Community District",35:"NYC School District",36:"NYC Police Precinct",37:"NYC Subborough Area",38:"LA Subsidence Contour","7":"Country",41:"OECD Region",40:"World State", 39:"World"};

var LAYER_AVAILABILITY = {"cities":[3,9],"sbound_place":[1,7],"slabel_place":[2,7],"sbound_zcta":[1,8],"slabel_zcta":[1,8],"sbound_tract":[1,8],"slabel_tract":[1,7],"sbound_county":[2,11],"slabel_county":[3,11],"sbound_msa_pmsa":[4,11],"slabel_msa_pmsa":[5,12],"sbound_blkgrp":[1,4],"slabel_blkgrp":[1,4],"sbound_cb_2_dc_ward":[1,15],"slabel_cb_2_dc_ward":[1,15],"sbound_cb_2_dc_cluster":[1,8],"slabel_cb_2_dc_cluster":[1,15],"sbound_cb_nyc_commdist":[1,8],"slabel_cb_nyc_commdist":[1,8],"sbound_cb_nyc_schdist":[1,8],"slabel_cb_nyc_schdist":[1,8],"sbound_cb_nyc_plcprct":[1,8],"slabel_cb_nyc_plcprct":[1,8],"sbound_cb_nyc_subboro":[1,8],"slabel_cb_nyc_subboro":[1,8],"sbound_us_la_subsidence":[1,8],"slabel_us_la_subsidence":[1,8],"slabel_world_country":[0,0],"sbound_world_country":[12,15], "slabel_world_region":[11,15], "sbound_world_region":[11,15], "sbound_oecd_region":[11,15], "slabel_oecd_region":[11,15], "sbound_world_state":[11,15], "slabel_world_state":[11,15], "world":[11,15]};


var QUANTILES_TO_PCTILES = {4: [ '25','50','75'],5: [ '20','40','60','80'],8: [ '12.5','25','37.5','50','62.5','75','87.5']};
var LEGEND_COLORS = {"candy": {"8": [ 0xFF0000,0xFF9900,0xFFFF00,0x88FF00,0x66CCFF,0x003399,0x0000FF,0x9900FF ], "4": [ 0xFF0000,0xFFFF00,0x66CCFF,0x0000FF ], "5": [ 0xFF0000,0xFFFF00,0x66CCFF,0x0000FF,0x9900FF ]},"bluegreen": {"8": [ 0xF7FCFD,0xE5F5F9,0xCCECE6,0x99D8C9,0x66C2A4,0x41AE76,0x238B45,0x005824 ], "4": [ 0xEDF8FB,0xB2E2E2,0x66C2A4,0x238B45 ], "5": [ 0xEDF8FB,0xB2E2E2,0x66C2A4,0x2CA25F,0x006D2C ]},"rust": {"8": [ 0xFFF8ED,0xFAE2C3,0xF7D5AD,0xF2BA85,0xF2AE72,0xF09F5D,0xEB8038,0xE65C12 ], "4": [ 0xFCEED9,0xF5C898,0xF2AE72,0xEB8038 ], "5": [ 0xFCEED9,0xF5C898,0xF2AE72,0xED8E4A,0xE86D25 ]},"olive": {"6": [ 0xFFFFCC,0xDDDDAA,0xBBBB88,0xAAAA77,0x777744,0x555522 ], "8": [ 0xFFFFCC,0xEEEEBB,0xDDDDAA,0xBBBB88,0xAAAA77,0x888855,0x777744,0x555522 ], "4": [ 0xFFFFCC,0xCCCC99,0x999966,0x555522 ], "5": [ 0xFFFFCC,0xDDDDAA,0xAAAA77,0x888855,0x555522 ]},"green": {"8": [ 0xF7FCF5,0xE5F5E0,0xC7E9C0,0xA1D99B,0x74C476,0x41AB5D,0x238B45,0x005A32 ], "4": [ 0xEDF8E9,0xBAE4B3,0x74C476,0x238B45 ], "5": [ 0xEDF8E9,0xBAE4B3,0x74C476,0x31A354,0x006D2C ]},"blue": {"8": [ 0xFAFFFF,0xC5E0E6,0xADD1DB,0x81B3CC,0x6FA5C7,0x5E95BF,0x4477B3,0x3559A1 ], "4": [ 0xDFF1F2,0x96C2D4,0x6FA5C7,0x4477B3 ], "5": [ 0xDFF1F2,0x96C2D4,0x6FA5C7,0x4F85B8,0x3C6AAB ]},"beige": {"8": [ 0xFAE9D4,0xEBCCB0,0xE6C0A1,0xD9A380,0xD1936F,0xC98561,0xBD6846,0xB04D30 ], "4": [ 0xF2DBC2,0xDEB18E,0xD1936F,0xBD6846 ], "5": [ 0xF2DBC2,0xDEB18E,0xD1936F,0xC47854,0xB85C3B ]},"grey": {"8": [ 0xFFFFFF,0xF0F0F0,0xD9D9D9,0xBDBDBD,0x969696,0x737373,0x525252,0x252525 ], "4": [ 0xF7F7F7,0xCCCCCC,0x969696,0x525252 ], "5": [ 0xF7F7F7,0xCCCCCC,0x969696,0x636363,0x252525 ]}};


function DPMapConfig() {
  this.container = '';
  this.static_map = '';
  this.default_map_type = '';
}



/* DPMap - start */

function DPMap( config ) {

        this._container = config.container;
        this.test = 'wahoo';
	this.breaks = config.breaks;
        this.blurOutsideElements = function () { }
	var map;
        if (GBrowserIsCompatible()) {
          this.map = new GMap2(config.container);
          map = this;

          var types = this.map.getMapTypes();
          types.each( function ( type ) {

                          type.getMaximumResolution = function() { return 10; };

                          type.getMinimumResolution = function() { return 4; }; 
                          if ( config.default_map_type == type.getName() ) {
                             map.setMapType( type );
                         }
                       });

         if (! config.default_map_type) {
             this.map.setMapType(G_SATELLITE_MAP);
         }


	  this.map.hybrid_labels_overlay = new GTileLayerOverlay(G_HYBRID_MAP.getTileLayers()[1], { zPriority: 10 });
          this.map.dpmap = this;
          this.map.disableInfoWindow();


	  this.map_copyright_collection = new GCopyrightCollection("Thematic");
          this.map_copyright_id = safeParseInt( Math.random());
          this.map_copyright = new GCopyright(this.map_copyright_id, 7, new GLatLngBounds(new GLatLng(-360, -360), new GLatLng(360, 360)), "Our Stuff");
          
	  this.map_custom_layer = new GTileLayer(this.map_copyright_collection,7,13);

          this.thematic_boundary = '';
          this.map_custom_layer.opacity = 0.5;
          this.map_custom_layer.getOpacity = function () {return this.opacity;};

	  this.map_boundary_layer = new GTileLayer(this.map_copyright_collection,1,13);

          this.updateCustomTileUrl();

          var opts = new GTileLayerOverlay();
          opts.zPriority = 2;
          this.map_overlay = new GTileLayerOverlay(this.map_custom_layer,opts);
          this.map_overlay.getOpacity = function () {return this.opacity;};

          var opts2 = new GTileLayerOverlay();
          opts2.zPriority = 11;

          this.map_boundary_overlay = new GTileLayerOverlay(this.map_boundary_layer,opts2);


          GEvent.addListener(this.map,"maptypechanged", function() { 

            if (this.getCurrentMapType() == G_HYBRID_MAP) {
              this.addOverlay( this.hybrid_labels_overlay );
            } else {
              if (this.hybrid_labels_overlay) {
                 this.removeOverlay( this.hybrid_labels_overlay );
              }
            }
          });
var map = this.map;
          this.on_map_click = function(overlay, ll, overlay_latlng) { 

              if (!ll && !overlay_latlng) {
                      return;
              }else if(!ll) {
                   ll = overlay_latlng;
              }
     	             
              try {
                  var latlng = new DPLatLng(ll.lat(), ll.lng());
                  var bounds = new DPLatLngBounds(latlng, latlng);
                  var containment = new DPContainment(bounds, this.dpmap.getIndicator());
                  var table = this.dpmap.getContainmentWindowContent(containment.containments(),this.dpmap.getIndicator(),bounds,this);
                  var info_window = this.getInfoWindow();
		  info_window.hide();

                  if (this.info_window_open &&
		      info_window.getPoint() == overlay_latlng
                       ) {
                         this.info_window_open = false;
                         return;
                  } else {
                         this.info_window_open = true;
                  }
		  map.enableInfoWindow();
                  this.openInfoWindow(ll,table);

               } catch(e) { window.alert(e.message); }
          }


          GEvent.addListener(this.map,"infowindowclose", function() { map.disableInfoWindow()});
          this.map_click_handler = GEvent.addListener(this.map,"click", this.on_map_click);


          //this need to go after the setCenter call

          // the map has to be given a center or the map will not be 
          // set up properly. This is part of the google way
          if (config.place) {

             this.centerWithPlace(config.place,config.zoom_level);
             this.setPlace(config.place);
          } else {
             this.map.setCenter(new GLatLng(37.4419, -122.1419), 13);
          }

          if (config.indicator) {
             this.setIndicator( config.indicator );            
          }


          this.map.addOverlay( this.map_overlay );
          // disabled until #2054 is completed
          //this.map.addOverlay( this.map_boundary_overlay );
          this.opacityMapControl = new OpacityControl(this.map_overlay);

          this.typeMapControl = new GMapTypeControl() ;

          if (!config.static_map) {
             this.map.addControl(this.getMapControl());
             this.map.addControl( this.opacityMapControl);
             this.map.addControl(this.typeMapControl,
               new GControlPosition(G_ANCHOR_TOP_RIGHT));
          } else {
             this.freezeMap();
          }

	  var dpmap = this;
	  GEvent.addListener(this.map,"zoomend",function (old_z,new_z) { dpmap.setZoom( new_z );   });
    }

}

DPMap.prototype.showLatLng = function() {
  GEvent.addListener(this.map,"mousemove", function (latlng) { 
if ( $('log') ) {
 $('log').innerHTML= latlng.lat() + " , " + latlng.lng();
}
});

}
DPMap.prototype.removeControls = function () {

   this.map.removeControl( this.typeMapControl  );
   this.map.removeControl( this.opacityMapControl  );
   this.map.removeControl( this.mapControl  );

}

DPMap.prototype.freezeMap = function() {
   this.map.disableDragging();
   this.map.disableInfoWindow();
   this.map.disableDoubleClickZoom();
   this.map.disableScrollWheelZoom();
   if ( this.map_click_handler ) {
      GEvent.removeListener( this.map_click_handler );
   }
}

DPMap.prototype.getMapControl = function () {
  this.mapControl = new GLargeMapControl();
  return this.mapControl;
}

DPMap.prototype.centerWithGoogleLatLng = function( place , zoom) {
  var geo = new GClientGeocoder(); 
  var latlng = '';
  var map = this;

  geo.setBaseCountryCode(".in");
  
  var country = "India";
  
  if (country) {
   country = ", " + country;
  }

  geo.getLatLng( place.name + country , function(lat) { 
     if (lat) {

        map.map.setCenter( lat );
        map.zoomToPlace( place, zoom );
    
     } else {
        map.centerWithPlace( place, zoom );
     }
  });

}

DPMap.prototype.centerAndActivatePlace = function ( place ) {

   this.centerToPlace( place ) ;
   this.setPlace( place );
   try {
     this._trigger_mapupdate();
   } catch(e) { }
} 

DPMap.prototype.getMapTypeName = function (  ) {
  return this.map.getCurrentMapType().getName();
}
DPMap.prototype.setMapType = function ( type  ) {
  return this.map.setMapType( type );
}

DPMap.prototype.centerToPlace = function ( place, zoomLevel ) {
 
   this.centerWithGoogleLatLng( place, zoomLevel );
   

}

DPMap.prototype.zoomToPlace = function (place, zoomLevel ) {

  if( !zoomLevel ) {
    var bounds = place.getBounds().toGLatLngBounds();
    var z = this.map.getBoundsZoomLevel(bounds);
     if ( z ) {

       this.map.setZoom(z );
    }
  } else {
    this.map.setZoom(zoomLevel);
  }
}


DPMap.prototype.centerWithPlace = function (place, zoomLevel) {
  var coords = place.getCenterLatLng();
  this.map.setCenter(new GLatLng(coords.lat, coords.lng));  
  this.zoomToPlace( place , zoomLevel );
}


DPMap.prototype.addLegend = function( container ) {
 this.legend = new DPLegend($(container),this);
 this.opacityMapControl.setLegend(this.legend);
}

DPMap.prototype.getActiveThematicBoundary = function( ) {
return this.indicator?this.indicator.getActiveThematicBoundary() : '';
}

DPMap.prototype.setActiveThematicBoundary = function( boundary ) {
    this.thematic_boundary = boundary;
    this.indicator.setActiveThematicBoundary( boundary );
    this.updateCustomTileUrl();
     if (this.legend) {
      this.legend.update();
     }
}
DPMap.prototype.zoomTo = function() {

}


DPMap.prototype.getThematicBoundaryId = function(dataset_id) {
   if (!this.bdids) { return ; }

   for( var x = 0; x < this.bdids.length; x++) {
      var bdid = this.bdids[ x ];
      if (bdid[0] == dataset_id) {
         return bdid[4];
      }
   }
   return '';
}

DPMap.prototype.updateCustomTileUrl = function() {
  var pl = this.place ? this.place.id : '';
  var cid = this.indicator ? this.indicator.id : '';
  var eck = this.indicator ? this.indicator['eck'] : '';
  var tb = this.getActiveThematicBoundary();
  var tileServers = ['http://www.indiadevelopmentindicators.org'];
  var btype_id = this.getThematicBoundaryId(tb);
  var breaks = this.breaks? this.breaks : 8;

  this.map_custom_layer.getTileUrl = function (tile,zoom) {

        var len = tileServers.length;
        var idx = (( tile.x % len ) + ( tile.y % len)) % len;
        var server_host = tileServers[idx];
//	console.log(server_host + '/map/google/thematic?x=' + tile.x + '&y=' + tile.y + '&z=' + zoom + '&cid=' + cid + '&breaks_count=' + breaks +'&btype_id=' +btype_id + '&eck=' + eck);
        return server_host + '/map/google/thematic?x=' + tile.x + '&y=' + tile.y + '&z=' + zoom + '&cid=' + cid + '&breaks_count=' + breaks + '&btype_id=' +btype_id + '&eck=' + eck;
	
}

  this.map_boundary_layer.getTileUrl = function(tile,zoom) {
        var len = tileServers.length;
        var idx = (( tile.x % len ) + ( tile.y % len)) % len;
        var server_host = tileServers[idx];                
        return server_host + '/map/google/boundaries?x=' + tile.x + '&y=' + tile.y + '&z=' + zoom + "&btype_id=" +btype_id;
  }

    // disabled until #2054 is completed
    if (this.map_boundary_overlay) {
        this.map.removeOverlay( this.map_boundary_overlay);
        if (btype_id) {
          this.map.addOverlay( this.map_boundary_overlay );
        }
    }


  if (this.map_overlay) {
     // Redraw the overlay with the new url. 
     // Need to have someway to delay this if we are resetting
     // Multiple things, ex indicator and place

     this.map.removeOverlay( this.map_overlay);
     if (cid && pl) { 
       this.map.addOverlay( this.map_overlay );
     }
  }

}

function customTileUrl (tile,zoom) {
   return "http://www.indiadevelopmentindicators.org/map/google/tile/?x=" + tile.x + "&y=" + tile.y + "&z=" + zoom + "&color=ccc";

}




DPMap.prototype.getDPInfoWindowContentContainment = function (containmentResult,indicator,bounds,currmap) {
  this.blurOutsideElements();
//	if (!this.allow_containment_window) { return; }
  lastmap = currmap; 
  var i;

  var nbCols = 1;
  if (containmentResult.length) {
        for (i=0; i<containmentResult.length; i++) {
         if (containmentResult[i].value) {
          nbCols = 2;
         }
        }
  } else {

        var info_content = new InfoBoxContent();
        info_content.addHeader({value: "No Locations" });
        info_content.addRow([ { value: "No locations found for your click."} ]);
        return info_content;
  }
  if (nbCols == 2) {
        var nm = indicator.name;
        if (nm.length > 18) {
            nm = nm.substr(0,18)+'...';
        }
  }

try {
    var info_content = new InfoBoxContent();

}catch(e) { window.alert(e.message); }

    if (containmentResult[0].incobrand == 1) {
        info_content.addHeader({value: "Location"});
    } else {
        var div = $(Builder.node("div", {}, [
                    "Visit ",
                    Builder.node("a", {href: "http://beta.dataplace.org", target: '_blank'}, ["DataPlace.org "])
                    ]));
        info_content.addHeader({value: div});
    }
    info_content.addHeader({value: "Type"});
    if (nm) {
          var currYear = document.getElementById('legendIndYear');
          var indText = indicator.name;
	
	if (currYear) {
           indText += "  in  " + currYear.options[currYear.selectedIndex].text;
	}
          var indName = Builder.node("span", {title: indText}, [nm]);
          info_content.addHeader({value: indName});
    }
    insideCobrand = 1;
    if (containmentResult[0].incobrand == 0) {
        var div1 = $(Builder.node("div", {}, [
                    "Click on one of the locations below ",
                    Builder.node("BR"),
                    "to see data for that location on ",
                    Builder.node("BR"),
                    Builder.node("a", {href: "http://beta.dataplace.org", target: '_blank'}, ["DataPlace.org "])
                    ]));
        info_content.addRow([{value: div1}]);
        insideCobrand = 0;
    }
    var current_boundary = this.getActiveThematicBoundary();
    containmentResult.each(
                           function (cont,index) {
                               var row_array = Array();
                               var boundary_type = "Zoom to " + cont.boundary_type;
			       if ( cont.place ) {
                               if (cont.incobrand == 1 ) {
                                   var place_id = cont.place.id;
                                   var javascript = "javascript:Event.stop(event);";
				   javascript += "try { containment_onclick('" + place_id + "','" + cont.place.boundaryDatasteId + "' ); } catch(e) {}"; 

				   if( cont.value ) {
                                     javascript += "if (lastmap.dpmap.getIndicator() ) {";
                                     javascript += "lastmap.dpmap.getIndicator().setActiveThematicBoundary(" + cont.place.boundaryDatasetId + ");";
                                     javascript += "};";
                                   }

                                   javascript += "lastmap.dpmap.centerAndActivatePlace(new DPPlace('" + place_id +  "'));";
//                                   javascript += "lastmap.dpmap.closeInfoWindow();";
//                                  javascript += "lastmap.dpmap.centerToPlace('" + place_id + "'));";
                                   var name = $(Builder.node("a",{ onmouseup: javascript}, cont.location));
                               } else {
                                   var name = $(Builder.node("a",{ href: "http://beta.dataplace.org/map?pl=" + cont.place.id, target: '_blank' }, cont.location));
                               }
                               } else {
	                         name = $(Builder.node("div", {style: "line-height: normal; overflow-y: auto; overflow-x: hidden; height: 100px;"}, ""));
	name.innerHTML =  cont.location;
                                 boundary_type = "Point Location";
                               }
                               var name1;
                               var boundary_type; 

                              if (current_boundary == cont.boundary_dataset_id) {


 name1 = $(Builder.node("span", {title: boundary_type}, Builder.node("strong",[name])));
                                 boundary_type = $(Builder.node("strong",[cont.boundary_type]));
                               } else {
                                 name1 = $(Builder.node("span", {title: boundary_type}, [name]));
                                 boundary_type = cont.boundary_type;
   				}                                                        
                               row_array[0] =  {value: name1, align: 'left'};
                               row_array[1] = {value: boundary_type };
                               var valueDiv;
                               if (nbCols > 1) {
                                   var val = cont.value;
                                   if (val == null) {
                                       val = 'N/A';
                                       valueDiv = $(Builder.node("div", {title: "Data not available for this geography."}, [val])).setStyle({color: '#dddddd'});
                                   } else {
                                       val = cont.formatted_value;
                           if (current_boundary == cont.boundary_dataset_id) {

                                       valueDiv = $(Builder.node("div", {}, Builder.node("strong",[val])));
} else {
                                       valueDiv = $(Builder.node("div", {}, [val]));

}
                                   }
                                   row_array[2] ={ value: valueDiv, align: 'right'};
                               }



                               info_content.addRow( row_array );
                           }
                           
                           );
  
     return info_content;
}



DPMap.prototype.getContainmentWindowContent = function (containmentResult,indicator,bounds,currmap) {
    var info_content = this.getDPInfoWindowContentContainment(containmentResult,indicator,bounds,currmap);
   var table = info_content.toTableNode(300);
   return table;
}


DPMap.prototype.openContainmentWindow = function(containmentResult, indicator, bounds, currmap) {
	this.blurOutsideElements();
	if (!this.allow_containment_window) { return; }
	var latlng = bounds.getCenterLatLng();
        var info_content = this.getDPInfoWindowContentContainment(containmentResult,indicator,bounds,currmap);
	this.openInfoWindow(latlng, info_content);
}

DPMap.prototype.closeContainmentWindow = function() {
}

DPMap.prototype.getInfoWindow = function() {
	return this._infoWindow;
}

DPMap.prototype.openInfoWindow = function(latlng, dp_info_content) {
	if (!this._enableInfoWindow) return;
	if (this._infoWindow) { this.closeInfoWindow(); return; }

        this.map.openInfoWindowHtml(latlng,"Hello");

//	this._infoWindow = new DPInfoWindow(this, dp_info_content);
//	this._infoWindow.showAt(latlng);
}




DPMap.prototype.getZoom = function() {
   return this.map.getZoom();
}

DPMap.prototype.getZoomLevel = DPMap.prototype.getZoom; 
DPMap.prototype.getCenterLatLng = DPMap.prototype.getZoom; 
DPMap.prototype.getCenterLatLng = function() {
   var center =  this.map.getCenter(); 
   return new DPLatLng(center.lat(),center.lng());
}

DPMap.prototype.setZoom = function( level ) {

   if (this.legend) {
     this.legend.updateZoomLevel( level ) ;
   }
   this.updateCustomTileUrl();
   return this.map.setZoom(level);
}

DPMap.prototype._trigger_mapupdate = function() {

  if (this._onmapupdate) { this._onmapupdate(); }
  if (this.legend) {
   var i = this.getIndicator();
   if ( i ) {
     this.legend.update(i.id, i.getActiveYear(),this);
   }
  }
  this.updateCustomTileUrl();

}




// this should be called unless sure they are not going to be updated
// right away. This prevents multiple map redraws.,
DPMap.prototype.updateMap = function(place,indicator,zoom_level) {
  this.setPlace(place,true);
  this.setIndicator(indicator,true);
  this._trigger_mapupdate();
  this.centerToPlace( place , zoom_level);
}


DPMap.prototype.setPlace = function(place, stop_update) {
  this.place = place;
   this.map.disableInfoWindow();
  if (this.geo_xml) {
    this.map.removeOverlay( this.geo_xml );
  }


  var sh = this.getServerHost();
//  console.log( sh + "/map/google/kml_highlight/" + this.place.id + "?color=99ffffff");
  this.geo_xml = new GGeoXml( sh + "/map/google/kml_highlight/" + this.place.id + "?color=99ffffff");
  this.map.addOverlay( this.geo_xml );
  

  if (!stop_update) {
    this._trigger_mapupdate();
  }
}
DPMap.prototype.getServerHost = function () {

  var tileServers = ['http://www.indiadevelopmentindicators.org'];
  var idx = (Math.round((Math.random()* tileServers.length)));
  return tileServers[0];
}

DPMap.prototype.getPlace = function() {
   return this.place;
}


DPMap.prototype.getBoundaryTypeList = function() {
    var current_dataset = (indicator ? indicator.tbound_dataset : null);
    var map = this;
    var zoomlevel = this.zoomlevel;    
    var indicator = this.indicator;
    if(! indicator ) { return Array(); }
    var cid = indicator.id;
    if (this.lastZoomlevel != zoomlevel || indicator != this.lastIndicator || cid != this.lastCid) {

    new Ajax.Request('http://www.indiadevelopmentindicators.org/ajax/json/ind_boundary_datasets', {
        asynchronous:false,
        method: 'get',
        parameters: {cid: cid, zoomlevel: zoomlevel, current_dataset_id: current_dataset},
        evalScripts: true,
        onSuccess:function(res,json) {
            if (!json) json = eval("("+res.responseText+")");
            map.bdids = $A(json['bdids']);
map.updateCustomTileUrl();
map.lastZoomlevel = zoomlevel;
map.lastIndicator = indicator;
map.lastCid = cid;

          }

    }
);
}
return map.bdids? map.bdids : Array();
}
DPMap.prototype.updateBoundaryTypeSelect = function() {

    var bdids = this.getBoundaryTypeList();
    var current_zoom_level = this.zoomLevel;
    var select = Builder.node("select",{id: 'boundarytype', style: "width: 150px"});
    var current_bid = 0;
    select.mapid = this._container.id;
    var count = 0;
    var current_select = 0;
    var bid_select = 0;
    var selected = 0;

    bdids.each(function(y) { 
            var zoomlevels = '';
	    if (y[2] ) {
  		zoomlevels = ' (N/A)';

	    }else if (THEMATIC_BOUNDARY_ZOOMLEVELS[y[0]]) {
	         zoomlevels =  " (zoom levels " + THEMATIC_BOUNDARY_ZOOMLEVELS[y[0]].lower + ' - ' + THEMATIC_BOUNDARY_ZOOMLEVELS[y[0]].upper + ")";
            }
	var span = Builder.node("span",{style: "font-size: 10px"}, zoomlevels);
            var opt = $(Builder.node('option',{value: y[0]},y[1]));
		opt.appendChild( span );
            opt.disabled = y[2];

            if (y[0] == current_bid) {
                selected = count;
            }
            select.appendChild( opt );
            count++;
        });
    $(select).selectedIndex = selected;
    
$('legend1_data_resolution').replace(select);
    
}


DPMap.prototype.setIndicatorById = function( id ) {
  var map = this;
  new DPIndicator(id,function(ind) { map.setIndicator(ind);  });

}


DPMap.prototype.setIndicator = function(indicator, stop_update) {
     // close any open info window.
   this.map.disableInfoWindow();

   if (!indicator) {
     return;
   }

   this.indicator = indicator;

  if ( ! indicator.activeThematicSet() ) {
    this.indicator.updateActiveThematicBoundaryByZoomlevel(this.getZoom());
   }
  if (!stop_update) {
    this._trigger_mapupdate();
  }
//  this.updateBoundaryTypeSelect();
}


DPMap.prototype.getIndicator = function() {
   return this.indicator;
}

DPMap.prototype.unlinkMap = function() {
 
 GEvent.removeListener(this._drag_handle);
}

DPMap.prototype.showMap = function (width) {
    width = width? width : "100%";
   this._container.show();
   this._container.style.width = width;
   this.resize();
}

DPMap.prototype.resize = function() {
   this.map.checkResize();
}

DPMap.prototype.linkMap = function ( tomap ) {

var tmp_map = this.map;
this.map.setCenter( tomap.dpmap.map.getCenter());
this._drag_handle = GEvent.addListener(this.map,'drag',function() { 
tomap.dpmap.map.setCenter( tmp_map.getCenter());
});

}





function DPPlace(id, loadCallback, metadata) {
	this.id = id;
	this._loadCallback = loadCallback;
	if (metadata) {
		this._loadFromArgs(metadata);
		return;
	}
	this._loaded = false;
	this._populate();
}

DPPlace.fromJSON = function(json) {
 return new DPPlace(null, null, json);
}

DPPlace._server = DP_BASE_JSON;
DPPlace._placeCache = {};

DPPlace.prototype._populate = function() {
	if (DPPlace._placeCache[this.id]) {
		this._loadFromArgs(DPPlace._placeCache[this.id]);
		return;
	}
	var s = document.createElement("script");
	var randomCallback = 'raiseCallback';
	for(var i=0; i<10; i++)
		randomCallback += (Math.round((Math.random()*9)));
	DPPlace[randomCallback] = DPPlace._makeCallback(s, this);
	s.src = DPPlace._server + '/placedetail?f=DPPlace.'+randomCallback+'&place=' + this.id;
	document.getElementsByTagName("head")[0].appendChild(s);
}

DPPlace.prototype._loadFromArgs = function(args) {
		this.id = args.id;
		this.name = args.name;
		this.longname = args.longname;
		this.boundaryDatasetId = args.boundaryDatasetId;
       // console.log(args.display[1] + ',' + args.display[0]); 
		this.center = null;
		if( args.lat && args.long ) {
			this.center = new DPLatLng(args.lat, args.long);
		}
		var min = new DPLatLng(args.display[1], args.display[0]);
		var max = new DPLatLng(args.display[3], args.display[2]);
		this.bounds = new DPLatLngBounds(min, max);
		this._loaded = true;
		DPPlace._placeCache[this.id] = args;
		var me = this;
		if (this._loadCallback) {
			this._loadCallback(me);
		}
}

DPPlace.prototype.getCenterLatLng = function() {
	if(this.center ) return this.center;
	else return this.bounds.getCenterLatLng();
}

DPPlace.prototype.getBounds = function() {
	return this.bounds;
}

DPPlace._makeCallback = function(script, place) {
	return function(args) {
		place._loadFromArgs(args);
	}
}





var DP_ERR = 500;
var DP_ERR_INVALID_DOM_EVENT = DP_ERR + 1;
var DP_ERR_INVALID_MAP_EVENT = DP_ERR + 2;
var DP_ERR_INVALID_DIALOG_TYPE = DP_ERR + 3;
var DP_ERR_INVALID_INDICATOR_DATA = DP_ERR + 4;
var DP_ERR_INVALID_PLACE_DATA = DP_ERR + 5;
var DP_ERR_INVALID_GEOCODER_DATA = DP_ERR + 6;
var DP_ERR_INVALID_CONTAINMENT_DATA = DP_ERR + 7;

function DPException(errno) {
	this.errno = errno;
	this.message = DPException._messages[errno]; // + arguments.callee.caller.toSource(); // FIXME " " + arguments.slice(1);
}

DPException._messages = {
	DP_ERR_INVALID_DOM_EVENT : "Invalid DOM event",
	DP_ERR_INVALID_MAP_EVENT : "Invalid map event",
	DP_ERR_INVALID_CONTAINMENT_DATA : "Invalid containment data"
};




function DPBounds(minX, minY, maxX, maxY) {
	this.minX = minX;
	this.minY = minY;
	this.maxX = maxX;
	this.maxY = maxY;
}

DPBounds.prototype.min = function() {
	return new DPPoint(this.minX, this.minY);
}

DPBounds.prototype.max = function() {
	return new DPPoint(this.maxX, this.maxX);
}

DPBounds.prototype.toString = function() {
	return "((" + this.minX + "," + this.minY + "), (" + this.maxX + "," + this.maxY + "))";
}

DPBounds.fromDPLatLngBounds = function(bounds) {
	var nw = bounds.getNorthWest();
	var se = bounds.getSouthEast();
	return new DPBounds(nw.lng, nw.lat, se.lng, se.lat);
}




function DPLatLngBounds(nw, se) {
	this.nw = nw;
	this.se = se;
}


DPLatLngBounds.prototype.toGLatLngBounds = function() {
  // google uses op

  var g_nw = this.nw.toGLatLng();
  var g_se = this.se.toGLatLng();

  var g_ne = new GLatLng(g_nw.lat,g_se.lng);
  var g_sw = new GLatLng(g_se.lat,g_nw.lng);
  return new GLatLngBounds(g_nw, g_se);

}


DPLatLngBounds.prototype.toMercator = function() {
	var nw = this.nw.toMercator();
	var se = this.se.toMercator();
	var b = new DPBounds(0, 0, 0, 0);
	b.minX = nw.x;
	b.minY = nw.y;
	b.maxX = se.x;
	b.maxY = se.y;
	return b;
}

DPLatLngBounds.prototype.getSouthEast = function() {
	return this.se.copy();
}

DPLatLngBounds.prototype.getNorthWest = function() {
	return this.nw.copy();
}

DPLatLngBounds.prototype.equals = function(other) {
}

DPLatLngBounds.prototype.contains = function(latlng) {
}

DPLatLngBounds.prototype.getCenterLatLng = function() {
//       var pt = this.toGLatLngBounds();
//      var center = pt.getCenter();   
	var pt = new DPLatLng((this.nw.lat + this.se.lat) / 2, (this.nw.lng + this.se.lng) / 2);
	return pt;
}

DPLatLngBounds.prototype.toString = function() {
	return '(' + this.nw.toString() + ', ' + this.se.toString() + ')';
}




function DPLatLng(lat, lng) {
	this.lat = lat;
	this.lng = lng;
}

DPLatLng.prototype.toString = function() {
	return '(' + this.lat + ', '+ this.lng+')';
}

DPLatLng.prototype.toHash = function() {
    return $H({lat: this.lat, lng: this.lng});
}

DPLatLng.prototype.copy = function() {
	return new DPLatLng(this.lat, this.lng);
}

DPLatLng.prototype.toMercator = function() { // input lat/long
	var pt = this.toDPPoint().toRadians(); // convert to radians
    var eConstant = Math.pow(((1 - (DPLatLng._eccentricity * Math.sin(pt.y))) / (1 + (DPLatLng._eccentricity * Math.sin(pt.y)))), DPLatLng._eccentricity*0.5);
	pt.x = pt.x * DPLatLng._earthRadius; // radians to meters
//	pt.y = Math.log(Math.tan(Math.PI * 0.25 + pt.y * 0.5) * eConstant) * DPLatLng._earthRadius;

	pt.y = Math.log(Math.tan((0.25*Math.PI)+(0.5*pt.y)) * eConstant) * DPLatLng._earthRadius; // radians to meters

	return pt;
}

DPLatLng.fromMercator = function(point) {
	var rads = new DPPoint(point.x, point.y);

	rads.x /= DPLatLng._earthRadius;
	rads.y /= DPLatLng._earthRadius;

	var es = DPLatLng._eccentricity * DPLatLng._eccentricity;
	var es2 = es * es;
	var es3 = es2 * es;
	var es4 = es3 * es;
	var Merc_ab = es * 0.5 + 5.0 * es2 / 24.0 + es3 / 12.0 + 13.0 * es4 / 360.0;
	var Merc_bb = 7.0 * es2 / 48.0 + 29.0 * es3 / 240.0 + 811.0 * es4 /  11520.0;
	var Merc_cb = 7.0 * es3 / 120.0 + 81.0 * es4 / 1120.0;
	var Merc_db = 4279.0 * es4 / 161280.0;
	var xphi = Math.PI * 0.5 - 2.0 * Math.atan(1.0 / Math.exp(rads.y));
	rads.y = xphi + Merc_ab * Math.sin(2.0 * xphi) + Merc_bb * Math.sin(4.0 * xphi) + Merc_cb * Math.sin(6.0 * xphi) + Merc_db * Math.sin(8.0 * xphi);
	
	return DPLatLng.fromDPPoint(rads.toDegrees());
}

DPLatLng._earthRadius = 6378137.0;
DPLatLng._eccentricity = 0.0818191913108718138;

DPLatLng.prototype.toDPPoint = function() {
	return new DPPoint(this.lng, this.lat);
}


DPLatLng.prototype.toGLatLng = function() {
	return new GLatLng(this.lat, this.lng);
}

DPLatLng.fromDPAddress = function(addr) {
	return new DPLatLng(addr.latlng.lat, addr.latlng.lng);
}

DPLatLng.fromDPPoint = function(point) {
	return new DPLatLng(point.y, point.x);
}


function DPPoint(x, y) {
 this.x = x;
 this.y = y;
}

DPPoint.prototype.toDegrees = function() {
	return new DPPoint(rad2deg(this.x), rad2deg(this.y));
}

DPPoint.prototype.toRadians = function() {
	return new DPPoint(deg2rad(this.x), deg2rad(this.y));
}

DPPoint.prototype.toString = function() {
	return '(' + this.x + ', ' + this.y + ')';
}

DPPoint.fromDPLatLng = function(latlng) {
 return new DPPoint(latlng.lng, latlng.lat);
}





function DPLegend (element,map){
     this.element = element;
     this.map = map;
     this.legend_int_id = safeParseInt(Math.random() * 1000);
     this.legend_id = "legend_table_" + this.legend_int_id;
     this.opacity = this.map.map_custom_layer.getOpacity();
     this.element.appendChild(Builder.node("div",{id: this.legend_id }));

    this.addBoundaryTypeSelect();
    var bdids = this.map.getBoundaryTypeList();
    if (!bdids.size()) { 
//       this.b_select.hide();
    }     
    this.addYearSelect();
     this.update();
}

DPLegend.prototype.setOpacity = function ( opacity ) { 
   this.opacity = opacity;
   var breaks = 10;

   for (var x = 0; x <= breaks; x++ ) {    

    var ele = $('legend_' + this.legend_int_id + "_img_" + x );
     if (ele) {
        ele.style.opacity = opacity;
     }
   }

}

DPLegend.prototype.showBoundaryType = function () {return true; }
DPLegend.prototype.showYear = function () {return true; }
DPLegend.prototype.showTableOnStart = function () {return 1; }
DPLegend.prototype.addBoundaryTypeSelect = function() {
     var b_select = this.getBoundaryTypeSelect();
     if (this.showBoundaryType()) {
       this.element.appendChild( b_select );
     }
    this.b_select = b_select;
}
DPLegend.prototype.redoBoundaryTypeSelect = function () { 
  
  if (this.b_select) {
    this.element.removeChild(this.b_select);
  }
  this.addBoundaryTypeSelect();
  
   
}

DPLegend.prototype.addYearSelect = function() {

try {
     var y_select = this.getYearSelect(); 
     if (this.showYear() && y_select) {
        this.element.appendChild(y_select);
        this.year_select_container = y_select;
     }
 } catch(e) {window.alert('here' + e.message);}

}
DPLegend.prototype.redoYearSelect = function () { 
  
  if (this.year_select_container) {
    this.element.removeChild(this.year_select_container);
  }
  this.addYearSelect();
  
   
}

DPLegend.prototype.update = function(cid,year,map) {
    if (map) { this.map = map; }
    var boundary_type_id = this.map.getActiveThematicBoundary();

    var div = this.div;
    var legend_id = this.legend_id;

    this.redoYearSelect(); 
//    if (!this.year_select) { this.addYearSelect(); } 
   
    if ( this.year_select) { 
       if (!year ) {
         year = this.year_select.options[this.year_select.selectedIndex].text;
       }
       if ( !cid ) { 
         cid = this.year_select.options[this.year_select.selectedIndex].value;
       }
    }

    if (!year && !cid){
     return;

    }
    var pl = this.map.getPlace().id;
    var zoomlevel;
    var rand = Math.floor(Math.random()* 30001);
    var current_dataset;
    var legend = this;

    new Ajax.Request('http://www.indiadevelopmentindicators.org/map/google/ajax', {
        method: 'get',
        parameters: {cid: cid, zoomlevel: zoomlevel, current_dataset_id: current_dataset, 'pl': pl, what: 'map_template','boundary_type_id': boundary_type_id,'year': year, 'rand': rand, 'legend_on': this.showTableOnStart(), 'theme':'candy', 'color_id_prefix': this.legend_int_id, 'opacity': this.opacity},

        onSuccess:function(res,json) {
               $(legend_id).innerHTML =  res.responseText;
               legend.redoBoundaryTypeSelect();
               legend.redoYearSelect();
          }

    }
);


}

DPLegend.prototype.updateZoomLevel = function( level ) {

   $(this.legend_id + "_zoom_level").innerHTML = "Current Zoom Level: " + level;

}
DPLegend.prototype.getCurrentBid = function() {
    var bdids = this.map.getBoundaryTypeList();
    var select = Builder.node("select",{id: 'boundarytype', style: "width: 150px"});
    if (!this.map.getIndicator()) { return; }
    var current_bid =this.map.getActiveThematicBoundary();

    // if current_bid is not a valid bdid then search for a valid bdid to use
    // FIXME: this code has the fault of not choosing the correct bdid if
    //        there are more than once choice
    var foundCurrentBid = false;
    for( var i = 0; i < bdids.length && ! foundCurrentBid ; i++ )
	if( bdids[i][0] == current_bid && ! bdids[i][2] )
          foundCurrentBid = true;
    if( ! foundCurrentBid ) {
	current_bid = null;
	for( var i = 0; i < bdids.length && ! current_bid ; i++  )
	  if( ! bdids[i][2] )
            current_bid = bdids[i][0];
        if( current_bid ) {
           this.map.getIndicator().setActiveThematicBoundary(current_bid);
	}	
    }
  return current_bid;
}


DPLegend.prototype.getYearSelect = function() {
    var ind = this.map.getIndicator();
    if (!ind || ind.hideYearSelect()) {
        var hide_years = ind? ind.hideYearSelect(): '';
        return;
    }
    var active_year = ind.getActiveYear();
    var years = ind.getAvailableYears();

    var select = Builder.node("select",{id: 'legendIndYear'});
    this.year_select = select;
    var count = 0;
    var selected = 0;
    var years_hash = $H();

    for ( var year in years) {
	    years_hash[year] = $(Builder.node('option',{value: years[year]}, year));
    }

    var year_list = years_hash.keys();	    
    year_list.sort(function(a,b) { return  b - a; });

    for ( var x = 0; x < year_list.length; x++) {
	var year = year_list[x];
	if (year == active_year) {
	    selected = x
        }

	select.appendChild(years_hash[year]);
    }

    $(select).selectedIndex = selected;
    select.map = this.map;
    var dpl = this;
    Event.observe(select,'change',function ( e ) {
            var select = Event.element( e );
            var selected = select.options[select.selectedIndex].value;
            var map = select.map;

            var current_bid = dpl.getCurrentBid();
            var ind = new DPIndicator(selected,function(ind) { map.setIndicator(ind);  
  });

           ind.setActiveColor( map.getIndicator().getActiveColor() );
           ind.setActiveThematicBoundary(current_bid);
           new Ajax.Request('http://www.indiadevelopmentindicators.org/mod_active', { method: 'get', parameters: { add_cid: selected } });
});
    var text = Builder.node("span","Year");
    var br = Builder.node("br");
    var br2 = Builder.node("br");

   return Builder.node("div",[br,text,br,,select]);
}



DPLegend.prototype.getBoundaryTypeSelect = function() {
    var bdids = this.map.getBoundaryTypeList();
    var current_zoom_level = this.map.zoomLevel;
    var select = Builder.node("select",{id: 'boundarytype', style: "width: 150px"});
    var current_bid = this.getCurrentBid();
    select.map = this.map;
    var count = 0;
    var current_select = 0;
    var bid_select = 0;
    var selected = 0;

    bdids.each(function(y) { 
            var zoomlevels = '';
	    if (y[2] ) {
  		zoomlevels = ' (N/A)';

	    }else if (THEMATIC_BOUNDARY_ZOOMLEVELS[y[0]]) {
	         zoomlevels =  " (zoom levels " + THEMATIC_BOUNDARY_ZOOMLEVELS[y[0]].lower + ' - ' + THEMATIC_BOUNDARY_ZOOMLEVELS[y[0]].upper + ")";
            }
	var span = Builder.node("span",{style: "font-size: 10px"}, zoomlevels);
            var opt = $(Builder.node('option',{value: y[0]},y[1]));
		opt.appendChild( span );
            opt.disabled = y[2];

            if (y[0] == current_bid) {
                selected = count;
            }
            select.appendChild( opt );
            count++;
        });


    $(select).selectedIndex = selected;
    var level_text = Builder.node("span", {id: this.legend_id + "_zoom_level"},"Current Zoom Level: " + this.map.getZoom());

    var text = Builder.node("span","Data Resolution");
    var br = Builder.node("br");
    var br2 = Builder.node("br");

    
    Event.observe(select,'change',function (e) {
	try {
            var select = Event.element( e );
            var map = select.map;

            var selected = select.options[select.selectedIndex].value;
            var zoom_levels = THEMATIC_BOUNDARY_ZOOMLEVELS[selected];
            if (zoom_levels) {
              if (map.getZoom() < zoom_levels.upper) {
               map.setZoom( zoom_levels.upper );
	      } else if (map.getZoom() > zoom_levels.lower) {
               map.setZoom( zoom_levels.lower );
              }
            } 
            map.setActiveThematicBoundary(selected);
        }catch (e) { window.alert(e.message);}
     }
    );

    return Builder.node("span",[ level_text, br2, text, br, select]);
}


DPLegend.prototype.copy = function() {
  return new DPLegend();
}

DPLegend.prototype.redraw = function() {
}

DPLegend.prototype.remove = function() {
  this.div.parentNode.removeChild(this.div);
}





/* properties:
 * 
 * boundaryType
 * datasetName
 * id
 * name
 * quantiles 
 */


function DPIndicator(id, loadCallback, metadata) {
	this.id = id;
	if (metadata) {
		for (var metadatum in metadata) {
			this[metadatum] = metadata[metadatum];
		}
		this._loaded = true;
		if (loadCallback) {
			var me = this;
			loadCallback(me);
		}
		this.setDefaults();
	} else {
		this._loaded = false;
		this._loadCallback = loadCallback;
		this._populate();
	}
}


DPIndicator.prototype.getName = function () {
  return this.name;
}
DPIndicator._server = DP_BASE_JSON;

DPIndicator.prototype._populate = function() {
	var s;
	var usescript = true; //(navigator&&navigator.userAgent.toLowerCase().indexOf("msie") == -1);

	if (usescript) {
		s = document.createElement("script");
	}

	var randomCallback = 'raiseCallback';
	for(var i=0; i<10; i++)
		randomCallback += (Math.round((Math.random()*9)));
	DPIndicator[randomCallback] = DPIndicator._makeCallback(this);
	var url = DPIndicator._server + '/indicator?f=DPIndicator.' + randomCallback + '&id=' + this.id;
    var request = DPXmlHttp.create();

	if (usescript) {
		s.src = url;
		document.getElementsByTagName("head")[0].appendChild(s);
	} else {

		request.open("GET", url, true);
		request.onreadystatechange= function() {
			if (request.readyState==4 && request.status == 200) {
				// 	alert('txt is ' + request.responseText);
                eval(request.responseText);
			}
		}
		request.send(null);
	}
    
}

DPIndicator._makeCallback = function(indicator) {
	return function(args) {
		for (var arg in args) {
			indicator[arg] = args[arg];
		}
		indicator._loaded = true;
		indicator.setDefaults();
		if (indicator._loadCallback) {
			indicator._loadCallback(indicator);
		}

		if (indicator._onuserdataload) {
            for (var cb=0; cb<indicator._onuserdataload.length; cb++) {
                indicator._onuserdataload[cb]();
            }
        }
	}
}

DPIndicator.prototype.setDefaults = function() {
	if (!this.year) {
		for (var year in this.years) {
		    if(this.years[year] == this.id) { this.year = year; }
        }
	}
	if (!this.color) {
		this.color = 'olive';
	}
	if (!this.nbranges) {
		this.setActiveNbRanges(8);
	}
}

DPIndicator.prototype.getLayerName = function(zoomlevel) {
	//	cid_21727.color_olive.cats_8.tbound_type_
	this.updateActiveThematicBoundaryByZoomlevel(zoomlevel);
// alert('this.tbound_dataset = ' + this.tbound_dataset);
// alert(BOUNDARY_DATASET_ID_TO_LAYERPREFIX[this.tbound_dataset]);
	var name = 'cid_' + (this.year ? this.years[this.year] : '') + '.color_' + this.color + '.cats_' + this.nbranges + '.tbound_type_' + (this.tbound_dataset ? BOUNDARY_DATASET_ID_TO_LAYERPREFIX[this.tbound_dataset] : '') + ',eck_' + this.eck;
	debug('layer name for zoom ' + zoomlevel + ' is ' + name);
	return name;
}

DPIndicator.prototype.toString = function() {
	var str = '[DPIndicator -- ';
	for (var b in this) {
		if (typeof this[b] != 'function') {
			str += b + "=" + this[b] + ','
		}
	}
	str += ']';
	return str;
}

DPIndicator.prototype.getAvailableYears = function() {
	return this.years;
}

DPIndicator.prototype.hideYearSelect = function() {
	return this.hideYears;
}

DPIndicator.prototype.getActiveYear = function() {
	return this.year;
}

DPIndicator.prototype.setActiveYear = function(year) {
	this.year = year;
	if (this._onuseryearchange) for(var cb=0; cb<this._onuseryearchange.length; cb++) this._onuseryearchange[cb](year);
}

DPIndicator.prototype.getActiveColor = function() {
	return this.color;
}

DPIndicator.prototype.getActiveId = function() {
	return this.years[this.year];
}

DPIndicator.prototype.setActiveColor = function(color) {
		this.color = color;
		if (this._onusercolorchange) for (var cb = 0;cb < this._onusercolorchange.length;cb++) this._onusercolorchange[cb](color);
}

DPIndicator.prototype.getActiveNbRanges = function() {
	if (this.pointDataInfo) {
		return;
	}

	return this.nbranges;
}

DPIndicator.prototype.getActiveBreaks = function() {
	if (this.pointDataInfo) {
		return;
	}
    if (!this.year || !this.pctiles[this.year]) { return Array(); }


    return this.pctiles[this.year][this.tbound_dataset]? this.pctiles[this.year][this.tbound_dataset] : Array();
}

DPIndicator.prototype.setActiveNbRanges = function(nbranges) {
	this.nbranges = nbranges;
	if (this._onusernbrangeschange) for (var cb = 0;cb < this._onusernbrangeschange.length;cb++) this._onusernbrangeschange[cb](nbranges);
};

DPIndicator.prototype.getActiveThematicBoundary = function() {
	return this.tbound_dataset;
};


DPIndicator.prototype.setActiveThematicBoundary = function(boundary_dataset_id) {
	
        if (!boundary_dataset_id || boundary_dataset_id == this.tbound_dataset) {
		return;
	}
	this.tbound_dataset = boundary_dataset_id;
	this.active_thematic_boundary_set = true;
	if (this._onuserthematicboundarychange) for (var cb=0; cb<this._onuserthematicboundarychange.length; cb++) {
		this._onuserthematicboundarychange[cb](boundary_dataset_id);
	}

};

DPIndicator.prototype.activeThematicSet = function() {
  return this.active_thematic_boundary_set? true: false;
};

DPIndicator.prototype.getFormat = function() {
	return this.format;
};

DPIndicator.prototype.getAvailableThematicBoundaries = function(zoomlevel) {
	if (this.pointDataInfo) {
		return;
	}

	var rv = new Array();
	var ztd = ZOOMLEVEL_THEMATIC_DATASETS[zoomlevel];
	var bounds = this.pctiles[this.year];

	if (!ztd) {
		return;
	}

	for (var i = 0; i < ztd.length; i++) {
		var bound_allowed = ztd[i];
		if (bounds[bound_allowed]) {
			rv.push(bound_allowed);
		}
	}

	return rv;
};

DPIndicator.prototype.updateActiveThematicBoundaryByZoomlevel = function(zoomlevel,dynamic) {

	if (this.pointDataInfo) {
		return;
	}
	var ztd = ZOOMLEVEL_THEMATIC_DATASETS[zoomlevel];
	if( ! ztd ) return;
	/* are selected tbound_datasets sticky if they're valid?  if so, uncomment this */
    // dynamic is true when we want to automatically select the best boundary
    // type as we zoom + recenter.
	if (this.tbound_dataset && !dynamic) {
		for (var i = 0;i < ztd.length;i++) {
			if (ztd[i] == this.tbound_dataset) {
				return;
			}
		}		
	}
	for (var i = 0; i < ztd.length; i++) {
		if (this.year && this.pctiles[this.year] && this.pctiles[this.year][ztd[i]]) {
			debug('setting tbound_dataset at zoom ' + zoomlevel + ' to ' + BOUNDARY_DATASET_ID_TO_LAYERPREFIX[ztd[i]] + ' (' + i + ')');
            this.setActiveThematicBoundary(ztd[i]);
			return;
		}
	}
}

DPIndicator.prototype._addListener = function(source, event, callback) {
	DPIndicator._validateEvent(event);
	if (typeof this['_onuser' + event] == 'undefined') {
		this['_onuser' + event] = [callback];
	} else {
		this['_onuser' + event].push(callback);
	}	
	// calls  the callback if already loaded && event is a load event
	if(this._loaded && event == 'dataload') callback(); 
}

DPIndicator._validateEvent = function(event) {
	switch(event) {
		case 'thematicboundarychange':
		case 'nbrangeschange':
		case 'colorchange':
		case 'yearchange':
		case 'featurechange':
		case 'dataload':
			return;
		default:
			throw new DPException(DP_ERR_INVALID_MAP_EVENT, event);
	}
}




function DPContainment(bounds, indicator) {
    var b = bounds.toMercator();
    b.minX++;
    b.minY++;
//    console.log(b);
    //var response= '"no response";';
    var response= 'no response';
    this.ajax = new Ajax.Request( DP_BASE_JSON + '/containment', {
        asynchronous: false,
        method: 'get',
        parameters: {
            minx: b.minX,
            miny: b.minY,
            maxx: b.maxX,
            maxy: b.maxY,
            nf: 1,
            cid: indicator ? indicator.id: ''
        },
/*
	onException: function(req, e) {
		alert('DPContainment Ajax.Request onException: ' + e.message);
	},
	onFailure: function(res) {
		alert('DPContainment onFailure:' + res);
	},
*/
        onSuccess: function(res) {
        response = res.responseText;
    }
	    });

    this.containment_result = eval( response );
}

DPContainment.prototype.containments = function() {
return this.containment_result;
}
DPContainment._server = DP_BASE_JSON;



function DPContainmentQuery() {
}

DPContainmentQuery._server = DP_BASE_JSON;

DPContainmentQuery._makeCallback = function(script, bounds, indicator, callback) {
	return function(containmentResult) {
		try {
			if (callback) {
				callback(containmentResult, indicator, bounds);
			}
		} catch(e) {
	window.alert(e.message);
			throw new DPException(DP_ERR_INVALID_CONTAINMENT_DATA, e);
		}
		DPContainmentQuery._cleanUp(script);
	};
}

DPContainmentQuery._cleanUp = function(script) {
	try {
		document.body.removeChild(script);
	} catch(e) {
	}
}

DPContainmentQuery.containmentQuery = function(bounds, indicator, callback) {
	var s = document.createElement('script');
	var b = bounds.toMercator();
	b.minX++;
	b.minY++;
	var randomCallback = 'raiseCallback';
	for(var i=0; i<10; i++)
		randomCallback += (Math.round((Math.random()*9)));
	var cid = '';
	if (indicator) cid = indicator.id;
	DPContainmentQuery[randomCallback] = DPContainmentQuery._makeCallback(s, bounds, indicator, callback);
	s.src = DPContainmentQuery._server + '/containment?minx='+b.minX+'&miny='+b.minY+'&maxx='+b.maxX+'&maxy='+b.maxY+'&f=DPContainmentQuery.'+randomCallback + (indicator ? ('&cid=' + indicator.id) : '');
	document.getElementsByTagName("head")[0].appendChild(s);
}

function DPContainmentResult(boundary_type, btype_id,buid,incobrand, location, value,formatted_value, place) {
	this.location = location;
	this.value = value;
	this.place = place;
	this.incobrand = incobrand;
	this.boundary_type = boundary_type;
	this.boundary_dataset_id = buid;
	this.boundary_type_id = btype_id;
	this.formatted_value = formatted_value;
}




function safeParseInt( val ) {
    return Math.round(parseFloat(val));
}

function OpacityControl( overlay, legend ) {
  this.overlay = overlay;
  this.legend = legend;
}

OpacityControl.prototype = new GControl();
OpacityControl.prototype.setLegend = function (l) {
  this.legend = l;
} 



// This function positions the slider to match the specified opacity
OpacityControl.prototype.setSlider = function(pos) {
  var left = Math.round((58*pos));
  this.slide.left = left;
  this.knob.style.left = left+"px";
}

// This function r.eads the slider and sets the overlay opacity level
OpacityControl.prototype.setOpacity = function() {
  this.overlay.getTileLayer().opacity = this.slide.left/58;
  if (this.legend) {
    this.legend.setOpacity( this.overlay.getTileLayer().opacity );
  }
  this.map.removeOverlay(this.overlay);
  this.map.addOverlay(this.overlay);
}

// This gets called by the API when addControl(new OpacityControl())
OpacityControl.prototype.initialize = function(map) {
  var that=this;
  this.map = map;

  // Is this MSIE, if so we need to use AlphaImageLoader
  var agent = navigator.userAgent.toLowerCase();
  if ((agent.indexOf("msie") > -1) && (agent.indexOf("opera") < 1)){this.ie = true} else {this.ie = false}

  // create the background graphic as a <div> containing an image
  var container = document.createElement("div");
  container.style.width="70px";
  container.style.height="21px";

  // Handle transparent PNG files in MSIE
  if (this.ie) {
    var loader = "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader("+
      "src='http://www.maptiler.org/img/opacity-slider.png', sizingMethod='crop');";
    container.innerHTML = '<div style="height:21px; width:70px; ' +loader+ '" ></div>';
  } else {
    container.innerHTML = '<div style="height:21px; width:70px; background-image: url(http://www.maptiler.org/img/opacity-slider.png)"></div>';
  }

  // create the knob as a GDraggableObject
  // Handle transparent PNG files in MSIE
  if (this.ie) {
    var loader = "progid:DXImageTransform.Microsoft.AlphaImageLoader("+
      "src='http://www.maptiler.org/img/opacity-slider.png', sizingMethod='crop');";
    this.knob = document.createElement("div");
    this.knob.style.height="21px";
    this.knob.style.width="13px";
    this.knob.style.top="0px";
    this.knob.style.overflow="hidden";
    this.knob_img = document.createElement("div");
    this.knob_img.style.height="21px";
    this.knob_img.style.width="83px";
    this.knob_img.style.filter=loader;
    this.knob_img.style.position="relative";
    this.knob_img.style.left="-70px";
    this.knob.appendChild(this.knob_img);
  } else {
    this.knob = document.createElement("div");
    this.knob.style.height="21px";
    this.knob.style.top="0px";
    this.knob.style.width="13px";
    this.knob.style.backgroundImage="url(http://www.maptiler.org/img/opacity-slider.png)";
    this.knob.style.backgroundPosition="-70px 0px";
  }
  container.appendChild(this.knob);
  this.slide=new GDraggableObject(this.knob, {container:container});
  this.slide.setDraggableCursor('pointer');
  this.slide.setDraggingCursor('pointer');
  this.container = container;

  // attach the control to the map
  map.getContainer().appendChild(container);

  // init slider
  this.setSlider( this.overlay.getTileLayer().opacity );

  // Listen for the slider being moved and set the opacity
  GEvent.addListener(this.slide, "dragend", function() {that.setOpacity()});
  //GEvent.addListener(this.container, "click", function( x, y ) { alert(x, y) });

  return container;
}

// Set the default position for the control
OpacityControl.prototype.getDefaultPosition = function() {
  return new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(260, 0));
}






function DPXmlHttp() {};
DPXmlHttp.create = function() {
  	var xmlhttp=false;
		/*@cc_on @*/
		/*@if (@_jscript_version >= 5)
		// JScript gives us Conditional compilation, we can cope with old IE versions.
		// and security blocked creation of the objects.
		 try {
		  xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
		 } catch (e) {
		  try {
		   xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
		  } catch (E) {
		   xmlhttp = false;
		  }
		 }
		@end @*/
		if (!xmlhttp && typeof XMLHttpRequest!='undefined') {
		  xmlhttp = new XMLHttpRequest();
		}
	return xmlhttp;
}


function err(msg) {
        eval(msg);
}

function deg2rad(degrees) {
        return degrees * Math.PI / 180.0;
}

function rad2deg(radians) {
        return radians * 180.0 / Math.PI;
}



var $utils = {};

Array.prototype.contains = function(input){
	for (var i in this) {
		if (this[i] == input) {
			return this[i];
		}
	}
	return null;
};

$utils.instanceOf = function (object, constructorFunction) {
  while (object != null) {
    if (object == constructorFunction.prototype)
     {return true}
	 object = object.__proto__;
  }
  return false;
}

// Correctly handle PNG transparency in Win IE 5.5 or higher.
// http://homepage.ntlworld.com/bobosola. Updated 02-March-2004

$utils.fixPNG = function(img) 
{
	if (navigator.appName == 'Netscape') return;
	var i;
	var imgName = img.src.toUpperCase()
	if (imgName.substring(imgName.length-3, imgName.length) == "PNG")
    {
		 var imgID = (img.id) ? "id='" + img.id + "' " : ""
		 var imgClass = (img.className) ? "class='" + img.className + "' " : ""
		 var imgTitle = (img.title) ? "title='" + img.title + "' " : "title='" + img.alt + "' "
		 var imgStyle = "display:inline-block;" + img.style.cssText 
		 if (img.align == "left") imgStyle = "float:left;" + imgStyle
		 if (img.align == "right") imgStyle = "float:right;" + imgStyle
		 if (img.parentElement.href) imgStyle = "cursor:hand;" + imgStyle		
		 var strNewHTML = "<span " + imgID + imgClass + imgTitle
		 + " style=\"" + "width:" + img.width + "px; height:" + img.height + "px;" + imgStyle + ";"
	     + "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader"
		 + "(src=\'" + img.src + "\', sizingMethod='scale');\"></span>" 
		 img.outerHTML = strNewHTML
		 i = i-1
    }
}

$utils.px = function(obj, attr, n) {
	 obj['style'][attr] = Math.round(n) + 'px';
}

$utils.abso = function(obj) {
	 obj.style.position = 'absolute';
}

$utils.alpha = function(obj, p) {
	 obj.style.opacity = p;
	  obj.style.filter = 'alpha(opacity='+(p*100)+');';
}

$utils.hide = function(obj) {
	    obj.style.visibility = 'visible';
}

$utils.show = function(obj) {
	    obj.style.visibility = 'hidden';
}

$utils.center = function(obj, parent) {
 var height, width;
 if (!parent) {
   height = GetWindowHeight();
   width = GetWindowWidth();
 } else {
   var oldparentvis = parent.style.visibility;
   var oldparentdisp = parent.style.display;
   parent.style.visibility = 'hidden';
   parent.style.display = '';
   height = parent.offsetHeight;
   width = parent.offsetWidth;
   parent.style.visibility = oldparentvis;
   parent.style.display = oldparentdisp;
 }
 var oldvis = obj.style.visibility;
 var olddisp = obj.style.display;
 obj.style.visibility = 'hidden';
 obj.style.display = '';
 $utils.px(obj, 'top', (height / 2) - (obj.offsetHeight / 2));
 $utils.px(obj, 'left', (width / 2) - (obj.offsetWidth / 2));
 obj.style.visibility = oldvis;
 obj.style.display = olddisp;
}

$utils.bg = function(obj, color) {
	 obj.style.background = color;
}

$utils.toggle = function(obj) {
	if (obj.style.display == 'block' || !obj.style.display)
		obj.style.display = 'none';
	else
		obj.style.display = 'block';
}

$utils.roundTo = function(dec, n) {
	return Math.round((Math.pow(10, dec) * n)) / Math.pow(10, dec);
}

$utils.roundToStr = function(dec, n) {
	rv = Math.round((Math.pow(10, dec) * n)) / Math.pow(10, dec) + '';

	if (dec == 0) {
		return rv;
	}

	lp = rv.lastIndexOf('.');

	if (lp == -1) {
		rv += '.';
		decs = 0;
	} else {
		decs = rv.length - lp - 1;
	}

	while (decs++ < dec) {
		rv += '0';
	}
	return rv;
}

$utils.commify = function(str) {
	var s = str.toString();
	for (;;) {
		var out = s.replace(/^(\d+)(\d{3})/, "$1,$2");
		
		if (out == s) {
			return out;
		} else {
           s = out;
       }
	}
}

$utils.updateSelectElement = function(select,args) {
	select.style.fontSize = args['fontsize'];
	select.onchange = args['onchange'];
	for (var i = 0;i < args['options'].length;i++) {
		var opt = document.createElement('option');
		opt.value=args['options'][i];
		opt.innerHTML = args['optionstext'] ? args['optionstext'][i] : args['options'][i];
		if (opt.value == args['selected']) {
			opt.selected = true;
		}
		select.appendChild(opt);
	//      select.add(opt, null);
  }

	return select;
}


$utils.createSelectElement = function(args) {
	var select=document.createElement('select');
	select.style.fontSize = args['fontsize'];
	select.onchange = args['onchange'];
	for (var i = 0;i < args['options'].length;i++) {
		var opt = document.createElement('option');
		opt.value=args['options'][i];
		opt.innerHTML = args['optionstext'] ? args['optionstext'][i] : args['options'][i];
		if (opt.value == args['selected']) {
			opt.selected = true;
		}
		select.appendChild(opt);
	//      select.add(opt, null);
  }

	return select;
}

$utils.keys = function(obj) {
	var keys = new Array();
	for (var key in obj) {
		keys.push(key);
	};
	return keys;
};

$utils.ucfirst = function(str) {
	return str.substr(0, 1).toUpperCase() + str.substr(1,str.length);
};

$utils.funcname = function(f) {
	var m = f.toString().match(/function (\w*)/);
	return (m && m[1]) ? m[1] : ("anonymous (" + (f.toSource ? f.toSource() : 'unknown') + ")");
}


$utils.stacktrace = function() {
	var s = "";
	var seen = {};
	for (var a = arguments.callee.caller; a !=null; a = a.arguments.callee.caller) {
		s += "->"+$utils.funcname(a) + "\n";
		if (seen[a]) {s+="<*>"; break;}
		seen[a] = 1;
	}
	return s;
}

$utils.getObjectPageXY = function(elem) {
  // Climb tree of parents to get x,y position of this object within the whole page
  // Use this function to calculate an offset to subtract from the mouse event pageX + pageY,
  // when you want to get the coordinates of the mouse relative to a particular object
  var r={"x":0,"y":0};
  while(elem) {
    r.x+=elem.offsetLeft; r.y+=elem.offsetTop; elem=elem.offsetParent;
  }
  return r;
}

