<commit_before>	
		////////////////////////////////////////////////////////////////////////////////
    //																																						//
		//  	 CLASS TO MANAGE ALL THE STUFF IN THE MAP (variable -> carto_map)			  //
		//     Actually, this is map of the application, and everything that					//
		//		 occurs on/over/with/in the map will be manage here.										//
		//		 																																				//
		//		 Overlays:  																														//
		//			 .selection_area_ 																										//
		//			 .info_window_    																										//					
    //			 .tooltip_                                                         		//
		//       .delete_windsow_                                                  		//
    //       .map_canvas_                                                      		//
		//																																						//
		////////////////////////////////////////////////////////////////////////////////



    function CartoMap (latlng,zoom) {
      this.center_ = latlng;                          // Center of the map at the beginning
      this.zoom_ = zoom;                              // Zoom at the beginning
      this.bounds_ = new google.maps.LatLngBounds();  // A latlngbounds for the map
      this.query_mode = false;												// Query mode

      this.points_ = {};                              // Points belong to the map
      this.fakeMarker_ = null;
      this.fakePolygon_ = null;
      this.fakePolyline_ = null;
                        
      this.status_ = "select";                        // Status of the map (select, add, )
      this.columns_ = null;
      this.cache_buster = 0;
                                                    
      this.show();                                    // First step is show the map canvas
      // this.showLoader();                            // Show loader
      this.createMap();                               // Create the map
    }
    
    
    
    ////////////////////////////////////////
    //  INIT MAP												  //
    ////////////////////////////////////////
    CartoMap.prototype.createMap = function () {
      
      // Generate a google map
      var myOptions = {
        zoom: this.zoom_,
        center: this.center_,
        disableDefaultUI: true,
        mapTypeId: google.maps.MapTypeId.ROADMAP
      }
      
      this.map_ = new google.maps.Map(document.getElementById("map"),myOptions);
      
			// Change view mode and watch if query mode is activated
			this.query_mode = ($('body').attr('query_mode') === 'true');
			$('body').attr('view_mode','map');
      
      this.addMapOverlays();
      this.addMapListeners();
      this.addToolListeners();
      this.startWax();
    }
    
    

		////////////////////////////////////////
    //  ADD ALL NECESSARY OVERLAYS				//
    ////////////////////////////////////////
    CartoMap.prototype.addMapOverlays = function () {
      var me = this;
      
      head.js('/javascripts/admin/maps/Overlays/mapCanvasStub.js',
              '/javascripts/admin/maps/Overlays/CartoTooltip.js',
              '/javascripts/admin/maps/Overlays/CartoInfowindow.js',
              '/javascripts/admin/maps/Overlays/CartoDeleteWindow.js',
              '/javascripts/admin/maps/polygonEdit.js',
              '/javascripts/admin/maps/polylineEdit.js',
        function(){
          me.selection_area_  = new google.maps.Polygon({strokeWeight:1});                          // Selection polygon area
    			me.info_window_     = new CartoInfowindow(new google.maps.LatLng(-260,-260),me.map_);     // InfoWindow for markers
    			me.tooltip_         = new CartoTooltip(new google.maps.LatLng(-260,-260),me.map_);				// Over tooltip for markers and selection area
          me.delete_window_   = new CartoDeleteWindow(new google.maps.LatLng(-260,-260), me.map_);  // Delete window to confirm remove one/several markers
					me.map_canvas_ 			= new mapCanvasStub(me.map_);
				}
      );
    }
    
    

		////////////////////////////////////////
    //  MAP AND TOOLS LISTENERS						//
    ////////////////////////////////////////
		/* Event listeners of the map */
    CartoMap.prototype.addMapListeners = function() {
      var me = this;
      
      google.maps.event.addListener(this.map_, 'zoom_changed', function() {
				$('span.slider').slider('value',me.map_.getZoom());
			});
			
			google.maps.event.addListener(this.map_, 'click', function(ev) {
        if (me.status_=="add") {
          me.addMarker(ev.latLng, {lat_:ev.latLng.lat(), lon_:ev.latLng.lng()}, true);
        }
			});
    }
      
		/* Event listeners of the map tools */
    CartoMap.prototype.addToolListeners = function() {
      var me = this;
      
      // Map tools
      $('div.general_options ul li.map a').hover(function(){
        // Change text
        var text = $(this).text().replace('_',' ');
        $('div.general_options div.tooltip p').text(text);
        // Check position
        var right = -($(this).offset().left-$(window).width());
        var offset = $('div.general_options div.tooltip').width()/2;
        // near right edge
        if (right-13-offset<0) {
          right = 16 + offset;
          $('div.general_options div.tooltip span.arrow').css({left:'83%'});
        } else {
          $('div.general_options div.tooltip span.arrow').css({left:'50%'});
        }
        $('div.general_options div.tooltip').css({right:right-13-offset+'px'});        
        // Show
        $('div.general_options div.tooltip').show();
      },function(){
        $('div.general_options div.tooltip').hide();
      });
      // Change map status
      $('div.general_options ul li.map a').click(function(ev){
        stopPropagation(ev);
        var status = $(this).attr('class');
        me.setMapStatus(status);
      });
      
      
      //Zooms
      $('a.zoom_in').click(function(ev){
        stopPropagation(ev);
        var new_zoom = me.map_.getZoom()+1;
        if (new_zoom<=20) {
          me.map_.setZoom(new_zoom);
          $('span.slider').slider('value',new_zoom);
        }
      });
      $('a.zoom_out').click(function(ev){
        stopPropagation(ev);
        var new_zoom = me.map_.getZoom()-1;
        if (new_zoom>=0) {
          me.map_.setZoom(new_zoom);
          $('span.slider').slider('value',new_zoom);
        }
      });
      
      // Zoom slider
      $('span.slider').slider({
        orientation: 'vertical',
        min:0,
        max:20,
        value:1,
        stop: function(event,ui){
          me.map_.setZoom(ui.value);
        }
      });
      
      
      // SQL Map console
			$('body').bind('query_refresh',function(ev){
				var view_map = ($('body').attr('view_mode') == 'map');
			  if (view_map && me.query_mode) {
					stopPropagation(ev);
					me.query_mode = false;
					me.refresh();
        }
			});
			// Try query
      $('div.sql_window a.try_query').livequery('click',function(ev){
        var map_status = ($('body').attr('view_mode') == "map");
        if (map_status) {
          stopPropagation(ev);
	        $('body').attr('query_mode','true');
					me.query_mode = true;
					setAppStatus();
          me.refresh();
          
          
          // Get results from api
          $.ajax({
				    method: "GET",
				    url: global_api_url+'queries?sql='+escape('SELECT count(*) FROM ('+escape(editor.getValue().replace('/\n/g'," "))+') as count'),
				 		headers: {"cartodbclient":"true"},
				    success: function(data) {
				      $('span.query h3').html(data.rows[0].count + ' row' + ((data.rows[0].count>1)?'s':'') + ' matching your query <a class="clear_table" href="#clear">CLEAR VIEW</a>');
				    },
				    error: function(e) {
				    }
				  });
        }
      });
    }


    
		////////////////////////////////////////
    //  WAX AND TOOLS LISTENERS						//
    ////////////////////////////////////////    
    CartoMap.prototype.startWax = function() {
      // interaction placeholder
      var me = this;
      var currentCartoDbId;
      this.tilejson = this.generateTilejson();

      this.waxOptions = {
        callbacks: {
          out: function(){
            me.over_marker_ = false;
            me.map_.setOptions({ draggableCursor: 'default' });
          }, 
          over: function(feature, div, opt3, evt){
            if (me.status_ == "select" && !me.query_mode) {
              me.over_marker_ = true;
              me.map_.setOptions({ draggableCursor: 'pointer' });
              me.tooltip_.open(evt.latLng,feature);
            }
          },
          click: function(feature, div, opt3, evt){
            
            // click, load information - show geometry - hide layers
            // Get results from api
            $.ajax({
  				    method: "GET",
  				    url: global_api_url+'queries?sql='+escape('SELECT ST_GeometryType(the_geom) FROM '+table_name+' WHERE cartodb_id = ' + feature),
  				 		headers: {"cartodbclient":"true"},
  				    success: function(data) {
  				      var type = (data.rows[0].st_geometrytype).toLowerCase();
  				      if (type == "st_point") {
  				        me.info_window_.openWax(feature);
  				      } else if (type=="st_multipolygon" || type=="st_polygon") {
  				        
  				      } else {
  				        
  				      }
  				      
  				    },
  				    error: function(e) {
  				    }
  				  });
            
            me.hideOverlays();
          }
        },
        clickAction: 'full'
      };


      this.wax_tile = new wax.g.connector(this.tilejson);
      this.map_.overlayMapTypes.insertAt(0,this.wax_tile);
      this.interaction = wax.g.interaction(this.map_, this.tilejson, this.waxOptions);
    }
    
    CartoMap.prototype.hideWax = function() {
      
    }
    
    CartoMap.prototype.showWax = function() {
      
    }
    
    CartoMap.prototype.refreshWax = function() {
      
    }


    ////////////////////////////////////////
    //  SET MAP && MARKER STATUS			    //
    ////////////////////////////////////////
		/* Set map status */
		CartoMap.prototype.setMapStatus = function(status) {
      this.status_ = status;
			this.setMarkerStatus(status);
			
			$('div.general_options li.map').each(function(i,ele){
				$(ele).removeClass('selected');
			});
			$('div.general_options').find('li.map').filter(function(){return $(this).text() == status}).addClass('selected');
			
      if (status=="select_area") {
      	this.enableSelectionTool()
      } else {
      	this.disableSelectionTool()
      }
			
			this.hideOverlays()
    }

		/* Set markers status */		
		CartoMap.prototype.setMarkerStatus = function(status) {
			_.each(this.points_,function(marker,i){
				marker.setDraggable((status=="select")?true:false);
				marker.setClickable((status=="select")?true:false);
			});
		}



    ////////////////////////////////////////
    //  POLYGON OPERATIONS OVER THE MAP		//
    ////////////////////////////////////////
		/* Disable selection area  */
		CartoMap.prototype.disableSelectionTool = function() {
			this.map_.setOptions({draggable:true});
	    $('div#map').unbind('mousedown');
	    if (this.selection_area_!=null) this.selection_area_.setMap(null);
		}

		/* Enable selection area  */	
		CartoMap.prototype.enableSelectionTool = function() {
			var me = this;
	  	this.map_.setOptions({draggable:false});
	     
	    // Selection tool
	    $('div#map').mousedown(function(ev){
	     	if (me.status_=="select_area") {
	     		google.maps.event.clearListeners(me.selection_area_, 'mouseover');
	        google.maps.event.clearListeners(me.selection_area_, 'mouseout');
          me.tooltip_.hide();
	              
	        var position = {};
	        position.x = ev.pageX-($('div#map').offset().left);
	        position.y = ev.pageY-($('div#map').offset().top);
	        var latlng = me.map_canvas_.transformCoordinates(new google.maps.Point(position.x,position.y));
	        
	        me.selection_area_.setOptions({fillOpacity: 0});
	        me.drawing = true;
	        me.selection_area_.setPath([latlng,latlng,latlng,latlng]);
	        me.selection_area_.setMap(me.map_);
	        
	        $('div#map').mousemove(function(ev){
	          position.x = ev.pageX-($('div#map').offset().left);
	          position.y = ev.pageY-($('div#map').offset().top);
	          var latlng = me.map_canvas_.transformCoordinates(new google.maps.Point(position.x,position.y));
	               
	         	me.selection_area_.setPath([
							me.selection_area_.getPath().getAt(0),
              new google.maps.LatLng(me.selection_area_.getPath().getAt(0).lat(),latlng.lng()),
              latlng,
              new google.maps.LatLng(latlng.lat(),me.selection_area_.getPath().getAt(0).lng()),
              me.selection_area_.getPath().getAt(0)]);
     				});
	        
	         	$('div#map').mouseup(function(ev){
	          	var position = {};
	           	position.x = ev.pageX-($('div#map').offset().left);
	           	position.y = ev.pageY-($('div#map').offset().top);
	           	var latlng = me.map_canvas_.transformCoordinates(new google.maps.Point(position.x,position.y));
	           
	           	$('div#map').unbind('mouseup');
	           	$('div#map').unbind('mousemove');
	
            	drawing = false;
              me.selection_area_.setOptions({fillOpacity: 0.40});
              google.maps.event.clearListeners(me.map_, 'mousemove');
              google.maps.event.clearListeners(me.selection_area_, 'click');
    
							var markers = me.markersInPolygon(me.selection_area_);
	    
	            google.maps.event.addListener(me.selection_area_,'mouseover',function(){
	              me.tooltip_.openPolgyon(latlng,markers);
	             	me.over_marker_ = true;
								me.tooltip_.show();
	            });
            
	            google.maps.event.addListener(me.selection_area_,'mouseout',function(){
	             	me.over_marker_ = false;
	            });
	
							me.tooltip_.show();
	         	});
	       	}
	     });
		}

		/* Calculate markers in selection area  */			
		CartoMap.prototype.markersInPolygon = function(selection_polygon) {
	   	//Check if the polygon contains this point
	    function Contains(polygon, point) { 
	       var j=0; 
	       var oddNodes = false; 
	       var x = point.lng(); 
	       var y = point.lat(); 
	       for (var i=0; i < polygon.getPath().getLength(); i++) { 
	         j++; 
	         if (j == polygon.getPath().getLength()) {j = 0;} 
	         if (((polygon.getPath().getAt(i).lat() < y) && (polygon.getPath().getAt(j).lat() >= y)) || ((polygon.getPath().getAt(j).lat() < y) && (polygon.getPath().getAt(i).lat() >= y))) { 
	           if ( polygon.getPath().getAt(i).lng() + (y - polygon.getPath().getAt(i).lat()) /  (polygon.getPath().getAt(j).lat()-polygon.getPath().getAt(i).lat()) *  (polygon.getPath().getAt(j).lng() - polygon.getPath().getAt(i).lng())<x ) { 
	             oddNodes = !oddNodes; 
	           } 
	         } 
	       } 
	       return oddNodes; 
	     };
       
	    var markers_polygon = [];
	 		_.each(this.points_, function(element){
	      if (Contains(selection_polygon,element.getPosition())) {
	        markers_polygon.push(element);
	      }
	     });
	     return markers_polygon;
		}
    
   

    ////////////////////////////////////////
    //  REQUEST POINTS TO DRAW IN THE MAP	//
    ////////////////////////////////////////
    CartoMap.prototype.getPoints = function() {
      var me = this;
      
      var api_key = "",
					query_url = "";

			if (this.query_mode) {
			  var value = editor.getValue();
			  var sql_ = value.split(/(from|FROM|From)/,3);
			  sql_[1] = ", ST_AsGeoJSON(the_geom) as coordinates_ FROM ";
			  value = sql_.join('');
			  
				query_url = global_api_url+'queries?sql='+ escape(value);
				var now = new Date();
			} else {
				query_url = global_api_url+'queries?sql='+ escape("select *,ST_AsGeoJSON(the_geom) as coordinates_ from " + table_name);
			}
			
			

      $.ajax({
        method: 'GET',
        url: query_url,
        headers: {'cartodbclient':true},
        success: function(result) {
					if (me.query_mode) {
						var arrived = new Date();
						var total = result.total_rows;
	          $('div.sql_window p.errors').fadeOut();
						$('span.query h3').html(total + ' row' + ((total>1)?'s':'') + ' matching your query <a class="clear_table" href="#clear">CLEAR VIEW</a>');
						$('span.query p').text('This query took '+(arrived - now)/1000+' seconds');
					}
					me.drawMarkers(result.rows);
        },
        error: function(e) {
					if (me.query_mode) {
						var json = $.parseJSON(e.responseText);
	          var msg = '';

	          _.each(json.errors,function(text,pos){
	            msg += text + ', ';
	          });
	          msg = msg.substr(0,msg.length-2);
						
						$('span.query h3').html('No results for this query <a class="clear_table" href="#clear">CLEAR VIEW</a>');
						$('span.query p').text('');
						$('div.sql_window p.errors').text(msg).fadeIn();
					}
          me.hideLoader();
        }
      });
    }
    
    

    ////////////////////////////////////////
    //  DRAW MARKERS WITH CANVAS ASYNC		//
    ////////////////////////////////////////
    /* Add fake google marker to the map */
    CartoMap.prototype.addFakeMarker = function(feature) {
      var me = this;
      
      $.ajax({
        method: "GET",
        url:global_api_url + 'tables/'+table_name+'/records/'+feature,
        headers: {"cartodbclient": true},
        success:function(data){
          me.removeWax();

          var coordinates = $.parseJSON(data.the_geom).coordinates;
          var latlng = new google.maps.LatLng(coordinates[1],coordinates[0]);

          var image = new google.maps.MarkerImage(me.generateDot('#FF6600'),
                new google.maps.Size(20, 20),
                new google.maps.Point(0,0),
                new google.maps.Point(10, 10));

          var marker = new google.maps.Marker({
            position: latlng,
            icon: image,
            flat: true,
            clickable: true,
            draggable: true,
            raiseOnDrag: false,
            animation: false,
            map: me.map_,
            data: data
          });


    			google.maps.event.addListener(marker,'mouseover',function(){
    				me.over_marker_ = true;
            if (me.status_ == "select" && !me.marker_dragging_ && !me.info_window_.isVisible(marker.data.cartodb_id) && !me.delete_window_.isVisible(marker.data.cartodb_id)) {
    					var latlng = this.getPosition();
    					me.tooltip_.open(latlng,[this]);
    				} else {
    					me.tooltip_.hide();
    				}
          });

    			google.maps.event.addListener(marker,'mouseout',function(){
    				me.over_marker_ = false;
    				setTimeout(function(){
    					if (!me.over_marker_) me.tooltip_.hide();
    				},100);
          });

          google.maps.event.addListener(marker,'dragstart',function(ev){
    				me.marker_dragging_ = true;
            this.data.init_latlng = ev.latLng;

    				// Hide all floating map windows
    				me.hideOverlays();
          });

          google.maps.event.addListener(marker,'dragend',function(ev){
    				me.marker_dragging_ = false;
            var occ_id = this.data.cartodb_id;
            var params = {};
            params.the_geom = '{"type":"Point","coordinates":['+ev.latLng.lng()+','+ev.latLng.lat()+']}';
            params.cartodb_id = occ_id;
            me.updateTable('/records/'+occ_id,params,ev.latLng,this.data.init_latlng,"change_latlng","PUT");
          });

          google.maps.event.addListener(marker,'click',function(ev){
            ev.stopPropagation();
            if (me.status_=="select") {
              me.info_window_.open(this);
            }
          });


    			// Click on map to recover wax layer and remove marker
    			google.maps.event.addListener(map,'click',function(ev){
    			  me.fakeMarker_.setMap(null);
    			  me.fakeMarker_ = null;

    			  // Refresh tiles
    			  me.refreshWax();
    			});
    			
    			me.fakeMarker_ = marker;
        },
        error:function(e){}
      });
      

    }
    
    /* Add record to database */
    CartoMap.prototype.addMarker = function(latlng) {
      var params = {};
      params.the_geom = '{"type":"Point","coordinates":['+latlng.lng()+','+latlng.lat()+']}';
      this.updateTable('/records',params,latlng,null,"add_point","POST");
    }
    
    /* Generate canvas image to fill marker   */
    CartoMap.prototype.generateDot = function(color) {
      if (this[color]==null) {
        var radius = 8;
        var el = document.createElement('canvas');
        el.width = (radius * 2) + 2;
        el.height = (radius * 2) + 2;

        var ctx = el.getContext('2d');
        ctx.fillStyle = '#FFFFFF';
        ctx.beginPath();
        ctx.arc(radius+1, radius+1, radius, 0, Math.PI * 2, true);
        ctx.closePath();
        ctx.fill();

        ctx.lineWidth = 1;
        ctx.strokeStyle = "#000000";
        ctx.stroke();
        ctx.fill();

        ctx.fillStyle = color;
        ctx.beginPath();
        ctx.arc(radius+1, radius+1, radius-2, 0, Math.PI * 2, true);
        ctx.closePath();
        ctx.fill();
        
        this[color] = el.toDataURL();
      }
      return this[color];
    }
    


    ////////////////////////////////////////
    //  REMOVE POINTS AND REFRESH WAX 		//
    ////////////////////////////////////////
    CartoMap.prototype.removeMarkers = function(cartodb_ids) {
      var params = {};
      params.cartodb_ids = cartodb_ids;
      this.updateTable('/records/'+cartodb_ids,params,null,null,"remove_points","DELETE");
    }

    CartoMap.prototype.removeFakeMarker = function() {
      this.fakeMarker_.setMap(null);
      this.fakeMarker_ = null;
    }

    
    
    ////////////////////////////////////////
    //  GET TABLE COLUMNS TO FILL INFO	  //
    ////////////////////////////////////////
    CartoMap.prototype.getColumns = function() {
			var me = this;
			
      $.ajax({
        dataType: 'json',
        type: 'GET',
        headers: {"cartodbclient": true},
        url: global_api_url+'tables/'+table_name +'/columns',
        success: function(data) {
					var new_array = [];
					_.each(data,function(ele,i){
						if (ele[0] != 'created_at' && ele[0] != 'updated_at' && ele[0] != 'the_geom' && ele[0] != 'cartodb_id') {
							new_array.push(ele[0]);
						}
					});
					
					me.columns_ = new_array;
					// Remove before that
					me.info_window_.columns_ = new_array;
        },
        error: function(e, textStatus) {
          me.info_window_.columns_ = [];
        }
      });
    }


    
    ////////////////////////////////////////
    //  REFRESH / CLEAR / CLEAN OVERLAYS  //
    ////////////////////////////////////////
    /* Reset map */
    CartoMap.prototype.refresh = function(sql) {
			this.query_mode = ($('body').attr('query_mode') === 'true');
			$('body').attr('view_mode','map');
		
      // Refresh wax layer
      this.tilejson = this.generateTilejson();
      this.refreshWax();
      
      // Remove the fake marker
			if (this.fakeMarker_!=null)
			  this.fakeMarker_.setMap(null);
    }
    
		/* Hide all overlays (no markers) */ 
		CartoMap.prototype.hideOverlays = function() {
		  // Tooltips
			this.delete_window_.hide();
			this.info_window_.hide();
			this.tooltip_.hide();
		}
    
    /* Refresh wax tiles */
    CartoMap.prototype.refreshWax = function() {
      // Add again wax layer
      if (this.map_) {
        this.cache_buster++;
        this.map_.overlayMapTypes.clear();
        this.wax_tile = new wax.g.connector(this.tilejson);
        this.map_.overlayMapTypes.insertAt(0,this.wax_tile);
        wax.g.interaction(this.map_, this.tilejson, this.waxOptions);
      }
    }
    
    /* Remove WAX layer */
    CartoMap.prototype.removeWax = function() {
      if (this.map_) {
        this.map_.overlayMapTypes.clear();
      }
    }

    /* Generate another tilejson */
    CartoMap.prototype.generateTilejson = function() {
      var that = this;
      // SQL?
      var query;
      if (this.query_mode) {
        query = '&sql='+editor.getValue();
      } else {
        query = '';
      }
            
      return {
        tilejson: '1.0.0',
        scheme: 'xyz',
        tiles: [TILEHTTP + '://' + user_name + '.' + TILESERVER + '/tiles/' + table_name + '/{z}/{x}/{y}.png8?cache_buster={cache}'+query],
        grids: [TILEHTTP + '://' + user_name + '.' + TILESERVER + '/tiles/' + table_name + '/{z}/{x}/{y}.grid.json'],
        formatter: function(options, data) { 
          currentCartoDbId = data.cartodb_id;
          return data.cartodb_id; 
        },
        cache_buster: function(){
          return that.cache_buster;
        }
      }
    }



    ////////////////////////////////////////<commit_msg>udpated cartomap.js with new functions for clearing interactivity<commit_after>	
		////////////////////////////////////////////////////////////////////////////////
    //																																						//
		//  	 CLASS TO MANAGE ALL THE STUFF IN THE MAP (variable -> carto_map)			  //
		//     Actually, this is map of the application, and everything that					//
		//		 occurs on/over/with/in the map will be manage here.										//
		//		 																																				//
		//		 Overlays:  																														//
		//			 .selection_area_ 																										//
		//			 .info_window_    																										//					
    //			 .tooltip_                                                         		//
		//       .delete_windsow_                                                  		//
    //       .map_canvas_                                                      		//
		//																																						//
		////////////////////////////////////////////////////////////////////////////////



    function CartoMap (latlng,zoom) {
      this.center_ = latlng;                          // Center of the map at the beginning
      this.zoom_ = zoom;                              // Zoom at the beginning
      this.bounds_ = new google.maps.LatLngBounds();  // A latlngbounds for the map
      this.query_mode = false;												// Query mode

      this.points_ = {};                              // Points belong to the map
      this.fakeMarker_ = null;
      this.fakePolygon_ = null;
      this.fakePolyline_ = null;
                        
      this.status_ = "select";                        // Status of the map (select, add, )
      this.columns_ = null;
      this.cache_buster = 0;
                                                    
      this.show();                                    // First step is show the map canvas
      // this.showLoader();                            // Show loader
      this.createMap();                               // Create the map
    }
    
    
    
    ////////////////////////////////////////
    //  INIT MAP												  //
    ////////////////////////////////////////
    CartoMap.prototype.createMap = function () {
      
      // Generate a google map
      var myOptions = {
        zoom: this.zoom_,
        center: this.center_,
        disableDefaultUI: true,
        mapTypeId: google.maps.MapTypeId.ROADMAP
      }
      
      this.map_ = new google.maps.Map(document.getElementById("map"),myOptions);
      
			// Change view mode and watch if query mode is activated
			this.query_mode = ($('body').attr('query_mode') === 'true');
			$('body').attr('view_mode','map');
      
      this.addMapOverlays();
      this.addMapListeners();
      this.addToolListeners();
      this.startWax();
    }
    
    

		////////////////////////////////////////
    //  ADD ALL NECESSARY OVERLAYS				//
    ////////////////////////////////////////
    CartoMap.prototype.addMapOverlays = function () {
      var me = this;
      
      head.js('/javascripts/admin/maps/Overlays/mapCanvasStub.js',
              '/javascripts/admin/maps/Overlays/CartoTooltip.js',
              '/javascripts/admin/maps/Overlays/CartoInfowindow.js',
              '/javascripts/admin/maps/Overlays/CartoDeleteWindow.js',
              '/javascripts/admin/maps/polygonEdit.js',
              '/javascripts/admin/maps/polylineEdit.js',
        function(){
          me.selection_area_  = new google.maps.Polygon({strokeWeight:1});                          // Selection polygon area
    			me.info_window_     = new CartoInfowindow(new google.maps.LatLng(-260,-260),me.map_);     // InfoWindow for markers
    			me.tooltip_         = new CartoTooltip(new google.maps.LatLng(-260,-260),me.map_);				// Over tooltip for markers and selection area
          me.delete_window_   = new CartoDeleteWindow(new google.maps.LatLng(-260,-260), me.map_);  // Delete window to confirm remove one/several markers
					me.map_canvas_ 			= new mapCanvasStub(me.map_);
				}
      );
    }
    
    

		////////////////////////////////////////
    //  MAP AND TOOLS LISTENERS						//
    ////////////////////////////////////////
		/* Event listeners of the map */
    CartoMap.prototype.addMapListeners = function() {
      var me = this;
      
      google.maps.event.addListener(this.map_, 'zoom_changed', function() {
				$('span.slider').slider('value',me.map_.getZoom());
			});
			
			google.maps.event.addListener(this.map_, 'click', function(ev) {
        if (me.status_=="add") {
          me.addMarker(ev.latLng, {lat_:ev.latLng.lat(), lon_:ev.latLng.lng()}, true);
        }
			});
    }
      
		/* Event listeners of the map tools */
    CartoMap.prototype.addToolListeners = function() {
      var me = this;
      
      // Map tools
      $('div.general_options ul li.map a').hover(function(){
        // Change text
        var text = $(this).text().replace('_',' ');
        $('div.general_options div.tooltip p').text(text);
        // Check position
        var right = -($(this).offset().left-$(window).width());
        var offset = $('div.general_options div.tooltip').width()/2;
        // near right edge
        if (right-13-offset<0) {
          right = 16 + offset;
          $('div.general_options div.tooltip span.arrow').css({left:'83%'});
        } else {
          $('div.general_options div.tooltip span.arrow').css({left:'50%'});
        }
        $('div.general_options div.tooltip').css({right:right-13-offset+'px'});        
        // Show
        $('div.general_options div.tooltip').show();
      },function(){
        $('div.general_options div.tooltip').hide();
      });
      // Change map status
      $('div.general_options ul li.map a').click(function(ev){
        stopPropagation(ev);
        var status = $(this).attr('class');
        me.setMapStatus(status);
      });
      
      
      //Zooms
      $('a.zoom_in').click(function(ev){
        stopPropagation(ev);
        var new_zoom = me.map_.getZoom()+1;
        if (new_zoom<=20) {
          me.map_.setZoom(new_zoom);
          $('span.slider').slider('value',new_zoom);
        }
      });
      $('a.zoom_out').click(function(ev){
        stopPropagation(ev);
        var new_zoom = me.map_.getZoom()-1;
        if (new_zoom>=0) {
          me.map_.setZoom(new_zoom);
          $('span.slider').slider('value',new_zoom);
        }
      });
      
      // Zoom slider
      $('span.slider').slider({
        orientation: 'vertical',
        min:0,
        max:20,
        value:1,
        stop: function(event,ui){
          me.map_.setZoom(ui.value);
        }
      });
      
      
      // SQL Map console
			$('body').bind('query_refresh',function(ev){
				var view_map = ($('body').attr('view_mode') == 'map');
			  if (view_map && me.query_mode) {
					stopPropagation(ev);
					me.query_mode = false;
					me.refresh();
        }
			});
			// Try query
      $('div.sql_window a.try_query').livequery('click',function(ev){
        var map_status = ($('body').attr('view_mode') == "map");
        if (map_status) {
          stopPropagation(ev);
	        $('body').attr('query_mode','true');
					me.query_mode = true;
					setAppStatus();
          me.refresh();
          
          
          // Get results from api
          $.ajax({
				    method: "GET",
				    url: global_api_url+'queries?sql='+escape('SELECT count(*) FROM ('+escape(editor.getValue().replace('/\n/g'," "))+') as count'),
				 		headers: {"cartodbclient":"true"},
				    success: function(data) {
				      $('span.query h3').html(data.rows[0].count + ' row' + ((data.rows[0].count>1)?'s':'') + ' matching your query <a class="clear_table" href="#clear">CLEAR VIEW</a>');
				    },
				    error: function(e) {
				    }
				  });
        }
      });
    }


    
		////////////////////////////////////////
    //  WAX AND TOOLS LISTENERS						//
    ////////////////////////////////////////    
    CartoMap.prototype.startWax = function() {
      // interaction placeholder
      var me = this;
      var currentCartoDbId;
      this.tilejson = this.generateTilejson();

      this.waxOptions = {
        callbacks: {
          out: function(){
            me.over_marker_ = false;
            me.map_.setOptions({ draggableCursor: 'default' });
          }, 
          over: function(feature, div, opt3, evt){
            if (me.status_ == "select" && !me.query_mode) {
              me.over_marker_ = true;
              me.map_.setOptions({ draggableCursor: 'pointer' });
              me.tooltip_.open(evt.latLng,feature);
            }
          },
          click: function(feature, div, opt3, evt){
            
            // click, load information - show geometry - hide layers
            // Get results from api
            $.ajax({
  				    method: "GET",
  				    url: global_api_url+'queries?sql='+escape('SELECT ST_GeometryType(the_geom) FROM '+table_name+' WHERE cartodb_id = ' + feature),
  				 		headers: {"cartodbclient":"true"},
  				    success: function(data) {
  				      var type = (data.rows[0].st_geometrytype).toLowerCase();
  				      if (type == "st_point") {
  				        me.info_window_.openWax(feature);
  				      } else if (type=="st_multipolygon" || type=="st_polygon") {
  				        
  				      } else {
  				        
  				      }
  				      
  				    },
  				    error: function(e) {
  				    }
  				  });
            
            me.hideOverlays();
          }
        },
        clickAction: 'full'
      };


      this.wax_tile = new wax.g.connector(this.tilejson);
      this.map_.overlayMapTypes.insertAt(0,this.wax_tile);
      this.interaction = wax.g.interaction(this.map_, this.tilejson, this.waxOptions);
    }
    
    CartoMap.prototype.hideWax = function() {
      
    }
    
    CartoMap.prototype.showWax = function() {
      
    }
    
    /* Refresh wax tiles */
    CartoMap.prototype.refreshWax = function() {
      // Add again wax layer
      if (this.map_) {
        this.cache_buster++;
        this.map_.overlayMapTypes.clear();
        this.wax_tile = new wax.g.connector(this.tilejson);
        this.map_.overlayMapTypes.insertAt(0,this.wax_tile);
        //wax.g.interaction().clearTileGrid();
        console.log("this should clear things out");
        this.tilejson.grids = this.tilejson.grids + '?cache_buster=' + this.cache_buster;
        wax.g.interaction(this.map_, this.tilejson, this.waxOptions);
      }
    }

    /* Remove WAX layer */
    CartoMap.prototype.removeWax = function() {
      if (this.map_) {
        this.map_.overlayMapTypes.clear();
      }
    }


    ////////////////////////////////////////
    //  SET MAP && MARKER STATUS			    //
    ////////////////////////////////////////
		/* Set map status */
		CartoMap.prototype.setMapStatus = function(status) {
      this.status_ = status;
			this.setMarkerStatus(status);
			
			$('div.general_options li.map').each(function(i,ele){
				$(ele).removeClass('selected');
			});
			$('div.general_options').find('li.map').filter(function(){return $(this).text() == status}).addClass('selected');
			
      if (status=="select_area") {
      	this.enableSelectionTool()
      } else {
      	this.disableSelectionTool()
      }
			
			this.hideOverlays()
    }

		/* Set markers status */		
		CartoMap.prototype.setMarkerStatus = function(status) {
			_.each(this.points_,function(marker,i){
				marker.setDraggable((status=="select")?true:false);
				marker.setClickable((status=="select")?true:false);
			});
		}



    ////////////////////////////////////////
    //  POLYGON OPERATIONS OVER THE MAP		//
    ////////////////////////////////////////
		/* Disable selection area  */
		CartoMap.prototype.disableSelectionTool = function() {
			this.map_.setOptions({draggable:true});
	    $('div#map').unbind('mousedown');
	    if (this.selection_area_!=null) this.selection_area_.setMap(null);
		}

		/* Enable selection area  */	
		CartoMap.prototype.enableSelectionTool = function() {
			var me = this;
	  	this.map_.setOptions({draggable:false});
	     
	    // Selection tool
	    $('div#map').mousedown(function(ev){
	     	if (me.status_=="select_area") {
	     		google.maps.event.clearListeners(me.selection_area_, 'mouseover');
	        google.maps.event.clearListeners(me.selection_area_, 'mouseout');
          me.tooltip_.hide();
	              
	        var position = {};
	        position.x = ev.pageX-($('div#map').offset().left);
	        position.y = ev.pageY-($('div#map').offset().top);
	        var latlng = me.map_canvas_.transformCoordinates(new google.maps.Point(position.x,position.y));
	        
	        me.selection_area_.setOptions({fillOpacity: 0});
	        me.drawing = true;
	        me.selection_area_.setPath([latlng,latlng,latlng,latlng]);
	        me.selection_area_.setMap(me.map_);
	        
	        $('div#map').mousemove(function(ev){
	          position.x = ev.pageX-($('div#map').offset().left);
	          position.y = ev.pageY-($('div#map').offset().top);
	          var latlng = me.map_canvas_.transformCoordinates(new google.maps.Point(position.x,position.y));
	               
	         	me.selection_area_.setPath([
							me.selection_area_.getPath().getAt(0),
              new google.maps.LatLng(me.selection_area_.getPath().getAt(0).lat(),latlng.lng()),
              latlng,
              new google.maps.LatLng(latlng.lat(),me.selection_area_.getPath().getAt(0).lng()),
              me.selection_area_.getPath().getAt(0)]);
     				});
	        
	         	$('div#map').mouseup(function(ev){
	          	var position = {};
	           	position.x = ev.pageX-($('div#map').offset().left);
	           	position.y = ev.pageY-($('div#map').offset().top);
	           	var latlng = me.map_canvas_.transformCoordinates(new google.maps.Point(position.x,position.y));
	           
	           	$('div#map').unbind('mouseup');
	           	$('div#map').unbind('mousemove');
	
            	drawing = false;
              me.selection_area_.setOptions({fillOpacity: 0.40});
              google.maps.event.clearListeners(me.map_, 'mousemove');
              google.maps.event.clearListeners(me.selection_area_, 'click');
    
							var markers = me.markersInPolygon(me.selection_area_);
	    
	            google.maps.event.addListener(me.selection_area_,'mouseover',function(){
	              me.tooltip_.openPolgyon(latlng,markers);
	             	me.over_marker_ = true;
								me.tooltip_.show();
	            });
            
	            google.maps.event.addListener(me.selection_area_,'mouseout',function(){
	             	me.over_marker_ = false;
	            });
	
							me.tooltip_.show();
	         	});
	       	}
	     });
		}

		/* Calculate markers in selection area  */			
		CartoMap.prototype.markersInPolygon = function(selection_polygon) {
	   	//Check if the polygon contains this point
	    function Contains(polygon, point) { 
	       var j=0; 
	       var oddNodes = false; 
	       var x = point.lng(); 
	       var y = point.lat(); 
	       for (var i=0; i < polygon.getPath().getLength(); i++) { 
	         j++; 
	         if (j == polygon.getPath().getLength()) {j = 0;} 
	         if (((polygon.getPath().getAt(i).lat() < y) && (polygon.getPath().getAt(j).lat() >= y)) || ((polygon.getPath().getAt(j).lat() < y) && (polygon.getPath().getAt(i).lat() >= y))) { 
	           if ( polygon.getPath().getAt(i).lng() + (y - polygon.getPath().getAt(i).lat()) /  (polygon.getPath().getAt(j).lat()-polygon.getPath().getAt(i).lat()) *  (polygon.getPath().getAt(j).lng() - polygon.getPath().getAt(i).lng())<x ) { 
	             oddNodes = !oddNodes; 
	           } 
	         } 
	       } 
	       return oddNodes; 
	     };
       
	    var markers_polygon = [];
	 		_.each(this.points_, function(element){
	      if (Contains(selection_polygon,element.getPosition())) {
	        markers_polygon.push(element);
	      }
	     });
	     return markers_polygon;
		}
    
   

    ////////////////////////////////////////
    //  REQUEST POINTS TO DRAW IN THE MAP	//
    ////////////////////////////////////////
    CartoMap.prototype.getPoints = function() {
      var me = this;
      
      var api_key = "",
					query_url = "";

			if (this.query_mode) {
			  var value = editor.getValue();
			  var sql_ = value.split(/(from|FROM|From)/,3);
			  sql_[1] = ", ST_AsGeoJSON(the_geom) as coordinates_ FROM ";
			  value = sql_.join('');
			  
				query_url = global_api_url+'queries?sql='+ escape(value);
				var now = new Date();
			} else {
				query_url = global_api_url+'queries?sql='+ escape("select *,ST_AsGeoJSON(the_geom) as coordinates_ from " + table_name);
			}
			
			

      $.ajax({
        method: 'GET',
        url: query_url,
        headers: {'cartodbclient':true},
        success: function(result) {
					if (me.query_mode) {
						var arrived = new Date();
						var total = result.total_rows;
	          $('div.sql_window p.errors').fadeOut();
						$('span.query h3').html(total + ' row' + ((total>1)?'s':'') + ' matching your query <a class="clear_table" href="#clear">CLEAR VIEW</a>');
						$('span.query p').text('This query took '+(arrived - now)/1000+' seconds');
					}
					me.drawMarkers(result.rows);
        },
        error: function(e) {
					if (me.query_mode) {
						var json = $.parseJSON(e.responseText);
	          var msg = '';

	          _.each(json.errors,function(text,pos){
	            msg += text + ', ';
	          });
	          msg = msg.substr(0,msg.length-2);
						
						$('span.query h3').html('No results for this query <a class="clear_table" href="#clear">CLEAR VIEW</a>');
						$('span.query p').text('');
						$('div.sql_window p.errors').text(msg).fadeIn();
					}
          me.hideLoader();
        }
      });
    }
    
    

    ////////////////////////////////////////
    //  DRAW MARKERS WITH CANVAS ASYNC		//
    ////////////////////////////////////////
    /* Add fake google marker to the map */
    CartoMap.prototype.addFakeMarker = function(feature) {
      var me = this;
      
      $.ajax({
        method: "GET",
        url:global_api_url + 'tables/'+table_name+'/records/'+feature,
        headers: {"cartodbclient": true},
        success:function(data){
          me.removeWax();

          var coordinates = $.parseJSON(data.the_geom).coordinates;
          var latlng = new google.maps.LatLng(coordinates[1],coordinates[0]);

          var image = new google.maps.MarkerImage(me.generateDot('#FF6600'),
                new google.maps.Size(20, 20),
                new google.maps.Point(0,0),
                new google.maps.Point(10, 10));

          var marker = new google.maps.Marker({
            position: latlng,
            icon: image,
            flat: true,
            clickable: true,
            draggable: true,
            raiseOnDrag: false,
            animation: false,
            map: me.map_,
            data: data
          });


    			google.maps.event.addListener(marker,'mouseover',function(){
    				me.over_marker_ = true;
            if (me.status_ == "select" && !me.marker_dragging_ && !me.info_window_.isVisible(marker.data.cartodb_id) && !me.delete_window_.isVisible(marker.data.cartodb_id)) {
    					var latlng = this.getPosition();
    					me.tooltip_.open(latlng,[this]);
    				} else {
    					me.tooltip_.hide();
    				}
          });

    			google.maps.event.addListener(marker,'mouseout',function(){
    				me.over_marker_ = false;
    				setTimeout(function(){
    					if (!me.over_marker_) me.tooltip_.hide();
    				},100);
          });

          google.maps.event.addListener(marker,'dragstart',function(ev){
    				me.marker_dragging_ = true;
            this.data.init_latlng = ev.latLng;

    				// Hide all floating map windows
    				me.hideOverlays();
          });

          google.maps.event.addListener(marker,'dragend',function(ev){
    				me.marker_dragging_ = false;
            var occ_id = this.data.cartodb_id;
            var params = {};
            params.the_geom = '{"type":"Point","coordinates":['+ev.latLng.lng()+','+ev.latLng.lat()+']}';
            params.cartodb_id = occ_id;
            me.updateTable('/records/'+occ_id,params,ev.latLng,this.data.init_latlng,"change_latlng","PUT");
          });

          google.maps.event.addListener(marker,'click',function(ev){
            ev.stopPropagation();
            if (me.status_=="select") {
              me.info_window_.open(this);
            }
          });


    			// Click on map to recover wax layer and remove marker
    			google.maps.event.addListener(map,'click',function(ev){
    			  me.fakeMarker_.setMap(null);
    			  me.fakeMarker_ = null;

    			  // Refresh tiles
    			  me.refreshWax();
    			});
    			
    			me.fakeMarker_ = marker;
        },
        error:function(e){}
      });
      

    }
    
    /* Add record to database */
    CartoMap.prototype.addMarker = function(latlng) {
      var params = {};
      params.the_geom = '{"type":"Point","coordinates":['+latlng.lng()+','+latlng.lat()+']}';
      this.updateTable('/records',params,latlng,null,"add_point","POST");
    }
    
    /* Generate canvas image to fill marker   */
    CartoMap.prototype.generateDot = function(color) {
      if (this[color]==null) {
        var radius = 8;
        var el = document.createElement('canvas');
        el.width = (radius * 2) + 2;
        el.height = (radius * 2) + 2;

        var ctx = el.getContext('2d');
        ctx.fillStyle = '#FFFFFF';
        ctx.beginPath();
        ctx.arc(radius+1, radius+1, radius, 0, Math.PI * 2, true);
        ctx.closePath();
        ctx.fill();

        ctx.lineWidth = 1;
        ctx.strokeStyle = "#000000";
        ctx.stroke();
        ctx.fill();

        ctx.fillStyle = color;
        ctx.beginPath();
        ctx.arc(radius+1, radius+1, radius-2, 0, Math.PI * 2, true);
        ctx.closePath();
        ctx.fill();
        
        this[color] = el.toDataURL();
      }
      return this[color];
    }
    


    ////////////////////////////////////////
    //  REMOVE POINTS AND REFRESH WAX 		//
    ////////////////////////////////////////
    CartoMap.prototype.removeMarkers = function(cartodb_ids) {
      var params = {};
      params.cartodb_ids = cartodb_ids;
      this.updateTable('/records/'+cartodb_ids,params,null,null,"remove_points","DELETE");
    }

    CartoMap.prototype.removeFakeMarker = function() {
      this.fakeMarker_.setMap(null);
      this.fakeMarker_ = null;
    }

    
    
    ////////////////////////////////////////
    //  GET TABLE COLUMNS TO FILL INFO	  //
    ////////////////////////////////////////
    CartoMap.prototype.getColumns = function() {
			var me = this;
			
      $.ajax({
        dataType: 'json',
        type: 'GET',
        headers: {"cartodbclient": true},
        url: global_api_url+'tables/'+table_name +'/columns',
        success: function(data) {
					var new_array = [];
					_.each(data,function(ele,i){
						if (ele[0] != 'created_at' && ele[0] != 'updated_at' && ele[0] != 'the_geom' && ele[0] != 'cartodb_id') {
							new_array.push(ele[0]);
						}
					});
					
					me.columns_ = new_array;
					// Remove before that
					me.info_window_.columns_ = new_array;
        },
        error: function(e, textStatus) {
          me.info_window_.columns_ = [];
        }
      });
    }


    
    ////////////////////////////////////////
    //  REFRESH / CLEAR / CLEAN OVERLAYS  //
    ////////////////////////////////////////
    /* Reset map */
    CartoMap.prototype.refresh = function(sql) {
			this.query_mode = ($('body').attr('query_mode') === 'true');
			$('body').attr('view_mode','map');
		
      // Refresh wax layer
      this.tilejson = this.generateTilejson();
      this.refreshWax();
      
      // Remove the fake marker
			if (this.fakeMarker_!=null)
			  this.fakeMarker_.setMap(null);
    }
    
		/* Hide all overlays (no markers) */ 
		CartoMap.prototype.hideOverlays = function() {
		  // Tooltips
			this.delete_window_.hide();
			this.info_window_.hide();
			this.tooltip_.hide();
		}

    /* Generate another tilejson */
    CartoMap.prototype.generateTilejson = function() {
      var that = this;
      // SQL?
      var query;
      if (this.query_mode) {
        query = '&sql='+editor.getValue();
      } else {
        query = '';
      }
            
      return {
        tilejson: '1.0.0',
        scheme: 'xyz',
        tiles: [TILEHTTP + '://' + user_name + '.' + TILESERVER + '/tiles/' + table_name + '/{z}/{x}/{y}.png8?cache_buster={cache}'+query],
        grids: [TILEHTTP + '://' + user_name + '.' + TILESERVER + '/tiles/' + table_name + '/{z}/{x}/{y}.grid.json'],
        formatter: function(options, data) { 
          currentCartoDbId = data.cartodb_id;
          return data.cartodb_id; 
        },
        cache_buster: function(){
          return that.cache_buster;
        }
      }
    }



    ////////////////////////////////////////
{'fraction_non_alphanumeric': 0.18986832847522364, 'fraction_numerical': 0.004905015579455221, 'mean_word_length': 1.6179349542153458, 'pattern_counts': {'":': 16, '<': 27, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 24, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4927024', 'n_tokens_mistral': 16474, 'n_tokens_neox': 14914, 'n_words': 2397}