1670 lines
45 KiB
JavaScript
1670 lines
45 KiB
JavaScript
/**
|
|
* map tab shown in cartodb admin
|
|
*/
|
|
|
|
/**
|
|
* inside the UI all the cartodb layers should be shown merged.
|
|
* the problem is that the editor needs the layers separated to work
|
|
* with them so this class transform from multiple cartodb layers
|
|
* and create only a view to represent all merged in a single layer group
|
|
*/
|
|
function GrouperLayerMapView(mapViewClass) {
|
|
|
|
return {
|
|
|
|
initialize: function() {
|
|
this.groupLayer = null;
|
|
this.activeLayerModel = null;
|
|
mapViewClass.prototype.initialize.call(this);
|
|
},
|
|
|
|
_removeLayers: function() {
|
|
var self = this;
|
|
_.each(this.map.layers.getLayersByType('CartoDB'), function(layer) {
|
|
layer.unbind(null, null, self);
|
|
});
|
|
cdb.geo.MapView.prototype._removeLayers.call(this);
|
|
|
|
if(this.groupLayer) {
|
|
this.groupLayer.model.unbind();
|
|
}
|
|
this.groupLayer = null;
|
|
},
|
|
|
|
_removeLayer: function(layer) {
|
|
// if the layer is in layergroup
|
|
if(layer.cid in this.layers) {
|
|
if(this.layers[layer.cid] === this.groupLayer) {
|
|
this._updateLayerDefinition(layer);
|
|
layer.unbind(null, null, this);
|
|
delete this.layers[layer.cid];
|
|
this.trigger('removeLayerView', this);
|
|
} else {
|
|
this.trigger('removeLayerView', this);
|
|
cdb.geo.MapView.prototype._removeLayer.call(this, layer);
|
|
}
|
|
} else {
|
|
cdb.log.info("removing non existing layer");
|
|
}
|
|
},
|
|
|
|
setActiveLayer: function(layer) {
|
|
this.activeLayerModel = layer;
|
|
this._setInteraction();
|
|
},
|
|
|
|
disableInteraction: function() {
|
|
if (this.groupLayer) {
|
|
this.groupLayer._clearInteraction();
|
|
}
|
|
},
|
|
|
|
enableInteraction: function() {
|
|
this._setInteraction();
|
|
},
|
|
|
|
// set interaction only for the active layer
|
|
_setInteraction: function() {
|
|
if(!this.groupLayer) return;
|
|
if(this.activeLayerModel) {
|
|
this.groupLayer._clearInteraction();
|
|
var idx = this.map.layers.getLayerDefIndex(this.activeLayerModel);
|
|
// when layer is not found idx == -1 so the interaction is
|
|
// disabled for all the layers
|
|
for(var i = 0; i < this.groupLayer.getLayerCount(); ++i) {
|
|
this.groupLayer.setInteraction(i, i == idx);
|
|
}
|
|
}
|
|
},
|
|
|
|
_updateLayerDefinition: function(layer) {
|
|
if(!layer) throw "layer must be a valid layer (not null)";
|
|
if(this.groupLayer) {
|
|
if(this.map.layers.getLayersByType('CartoDB').length === 0) {
|
|
this.groupLayer.remove();
|
|
this.groupLayer = null;
|
|
} else {
|
|
var def = this.map.layers.getLayerDef();
|
|
this.groupLayer.setLayerDefinition(def);
|
|
this._setInteraction();
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* when merged layers raises an error this function send the error to the
|
|
* layer that actually caused it
|
|
*/
|
|
_routeErrors: function(errors) {
|
|
var styleRegExp = /style(\d+)/;
|
|
var postgresExp = /layer(\d+):/i;
|
|
var generalPostgresExp = /PSQL error/i;
|
|
var syntaxErrorExp = /syntax error/i;
|
|
var webMercatorErrorExp = /"the_geom_webmercator" does not exist/i;
|
|
var tilerError = /Error:/i;
|
|
var layers = this.map.layers.where({ visible: true, type: 'CartoDB' });
|
|
for(var i in errors) {
|
|
var err = errors[i];
|
|
// filter empty errors
|
|
if(err && err.length) {
|
|
var match = styleRegExp.exec(err);
|
|
if(match) {
|
|
var layerIndex = parseInt(match[1], 10);
|
|
layers[layerIndex].trigger('parseError', [err]);
|
|
} else {
|
|
var match = postgresExp.exec(err);
|
|
if(match) {
|
|
var layerIndex = parseInt(match[1], 10);
|
|
if (webMercatorErrorExp.exec(err)) {
|
|
err = _t("you should select the_geom_webmercator column");
|
|
layers[layerIndex].trigger('sqlNoMercator', [err]);
|
|
} else {
|
|
layers[layerIndex].trigger('sqlParseError', [err]);
|
|
}
|
|
} else if(generalPostgresExp.exec(err) || syntaxErrorExp.exec(err) || tilerError.exec(err)) {
|
|
var error = 'sqlError';
|
|
if (webMercatorErrorExp.exec(err)) {
|
|
error = 'sqlNoMercator';
|
|
err = _t("you should select the_geom_webmercator column");
|
|
}
|
|
_.each(layers, function(lyr) { lyr.trigger(error, err); });
|
|
} else {
|
|
_.each(layers, function(lyr) { lyr.trigger('error', err); });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
_routeSignal: function(signal) {
|
|
var self = this;
|
|
return function() {
|
|
var layers = self.map.layers.where({ visible: true, type: 'CartoDB' });
|
|
var args = [signal].concat(arguments);
|
|
_.each(layers, function(lyr) { lyr.trigger.apply(lyr, args); });
|
|
}
|
|
},
|
|
|
|
_addLayer: function(layer, layers, opts) {
|
|
opts = opts || {};
|
|
// create group layer to acumulate cartodb layers
|
|
if (layer.get('type') === 'CartoDB') {
|
|
var self = this;
|
|
if(!this.groupLayer) {
|
|
// create model
|
|
var m = new cdb.geo.CartoDBGroupLayer(
|
|
_.extend(layer.toLayerGroup(), {
|
|
user_name: this.options.user.get("username"),
|
|
maps_api_template: cdb.config.get('maps_api_template'),
|
|
no_cdn: false,
|
|
force_cors: true // use CORS to control error management in a better way
|
|
})
|
|
);
|
|
|
|
var layer_view = mapViewClass.prototype._addLayer.call(this, m, layers, _.extend({}, opts, { silent: true }));
|
|
delete this.layers[m.cid];
|
|
this.layers[layer.cid] = layer_view;
|
|
this.groupLayer = layer_view;
|
|
m.bind('error', this._routeErrors, this);
|
|
m.bind('tileOk', this._routeSignal('tileOk'), this);
|
|
this.trigger('newLayerView', layer_view, layer, this);
|
|
} else {
|
|
this.layers[layer.cid] = this.groupLayer;
|
|
this._updateLayerDefinition(layer);
|
|
this.trigger('newLayerView', this.groupLayer, layer, this);
|
|
}
|
|
|
|
layer.bind('change:tile_style change:query change:query_wrapper change:interactivity change:visible', this._updateLayerDefinition, this);
|
|
this._addLayerToMap(this.groupLayer, opts);
|
|
delete this.layers[this.groupLayer.model.cid];
|
|
} else {
|
|
mapViewClass.prototype._addLayer.call(this, layer, layers, opts);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
cdb.admin.LeafletMapView = cdb.geo.LeafletMapView.extend(GrouperLayerMapView(cdb.geo.LeafletMapView));
|
|
|
|
if (typeof(google) !== 'undefined') {
|
|
cdb.admin.GoogleMapsMapView = cdb.geo.GoogleMapsMapView.extend(GrouperLayerMapView(cdb.geo.GoogleMapsMapView));
|
|
}
|
|
|
|
cdb.admin.MapTab = cdb.core.View.extend({
|
|
|
|
events: {
|
|
'click .toggle_slides.button': '_toggleSlides',
|
|
'click .add_overlay.button': 'killEvent',
|
|
'click .canvas_setup.button': 'killEvent',
|
|
'click .export_image.button': '_exportImage',
|
|
'click .sqlview .clearview': '_clearView',
|
|
'click .sqlview .export_query':'_tableFromQuery',
|
|
'keydown':'_onKeyDown'
|
|
},
|
|
|
|
_TEXTS: {
|
|
no_interaction_warn: _t("Map interaction is disabled, select cartodb_id to enable it")
|
|
},
|
|
|
|
className: 'map',
|
|
animation_time: 300,
|
|
|
|
initialize: function() {
|
|
|
|
this.template = this.getTemplate('table/views/maptab');
|
|
|
|
this.map = this.model;
|
|
this.user = this.options.user;
|
|
this.vis = this.options.vis;
|
|
this.authTokens = this.options.authTokens || [];
|
|
this.master_vis = this.options.master_vis;
|
|
|
|
this.canvas = new cdb.core.Model({ mode: "desktop" });
|
|
|
|
this.map_enabled = false;
|
|
this.georeferenced = false;
|
|
this.featureHovered = null;
|
|
this.activeLayerView = null;
|
|
this.layerDataView = null;
|
|
this.layerModel = null;
|
|
this.legends = [];
|
|
this.overlays = null;
|
|
|
|
this.add_related_model(this.map);
|
|
this.add_related_model(this.canvas);
|
|
this.add_related_model(this.map.layers);
|
|
|
|
this._addBindings();
|
|
|
|
},
|
|
|
|
_addBindings: function() {
|
|
|
|
// Actions triggered in the right panel
|
|
cdb.god.bind("panel_action", function(action) {
|
|
this._moveInfo(action);
|
|
}, this);
|
|
|
|
this.add_related_model(cdb.god);
|
|
|
|
this.map.bind('change:provider', this.switchMapType, this);
|
|
this.map.bind('change:legends', this._toggleLegends, this);
|
|
this.map.layers.bind('change:visible', this._addLegends, this);
|
|
this.map.layers.bind('change:visible', this._addTimeline, this);
|
|
this.map.layers.bind('change:tile_style', this._addTimeline, this);
|
|
this.map.layers.bind('remove reset', this._addLegends, this);
|
|
this.map.layers.bind('remove reset', this._addTimeline, this);
|
|
|
|
_.bindAll(this, 'showNoGeoRefWarning', "_exportImage");
|
|
|
|
},
|
|
|
|
isMapEnabled: function() {
|
|
return this.map_enabled;
|
|
},
|
|
|
|
deactivated: function() {
|
|
if(this.map_enabled) {
|
|
this.clearMap();
|
|
}
|
|
},
|
|
|
|
clearMap: function() {
|
|
|
|
clearTimeout(this.autoSaveBoundsTimer);
|
|
|
|
this.mapView.clean();
|
|
|
|
if (this.exportImageView) {
|
|
this.exportImageView.clean();
|
|
this.exportImageView = null;
|
|
}
|
|
|
|
if (this.overlaysDropdown) this.overlaysDropdown.clean();
|
|
if (this.mapOptionsDropdown) this.mapOptionsDropdown.clean();
|
|
if (this.basemapDropdown) this.basemapDropdown.clean();
|
|
if (this.configureCanvasDropdown) this.configureCanvasDropdown.clean();
|
|
|
|
if (this.zoom) {
|
|
this.zoom.clean();
|
|
}
|
|
|
|
if (this.infowindow) {
|
|
this.infowindow.clean();
|
|
}
|
|
|
|
if (this.overlays) {
|
|
this.overlays._cleanOverlays();
|
|
}
|
|
|
|
this._cleanLegends();
|
|
|
|
if (this.stackedLegend) {
|
|
this.stackedLegend.clean();
|
|
}
|
|
|
|
if (this.timeline) {
|
|
this.timeline.clean();
|
|
this.timeline = null;
|
|
}
|
|
|
|
if (this.geometryEditor) this.geometryEditor.clean();
|
|
|
|
if (this.table) {
|
|
this.table.unbind(null, null, this);
|
|
}
|
|
|
|
delete this.mapView;
|
|
delete this.overlaysDropdown;
|
|
delete this.basemapDropdown;
|
|
delete this.mapOptionsDropdown;
|
|
delete this.configureCanvasDropdown;
|
|
|
|
delete this.zoom;
|
|
delete this.infowindow;
|
|
delete this.layer_selector;
|
|
delete this.header;
|
|
delete this.share;
|
|
delete this.legends;
|
|
delete this.overlays;
|
|
delete this.legend;
|
|
delete this.stackedLegend;
|
|
delete this.geometryEditor;
|
|
|
|
this.map_enabled = false;
|
|
|
|
// place the map DOM object again
|
|
this.render();
|
|
},
|
|
|
|
|
|
/**
|
|
* Hide the infowindow when a query is applied or cleared
|
|
*/
|
|
_hideInfowindow: function() {
|
|
if(this.infowindow) {
|
|
this.infowindow.model.set('visibility', false);
|
|
}
|
|
},
|
|
|
|
|
|
/**
|
|
* this function is used when the map library is changed. Each map library
|
|
* works in different way and need to recreate all the components again
|
|
*/
|
|
switchMapType: function() {
|
|
|
|
if (this.map_enabled) {
|
|
this.clearMap();
|
|
this.enableMap();
|
|
}
|
|
|
|
},
|
|
|
|
_showGMapsDeprecationDialog: function() {
|
|
var dialog = cdb.editor.ViewFactory.createDialogByTemplate('common/dialogs/confirm_gmaps_basemap_to_leaflet_conversion');
|
|
|
|
var self = this;
|
|
dialog.ok = function() {
|
|
self.map.set('provider', 'leaflet', { silent: true });
|
|
self.setupMap();
|
|
this.close && this.close();
|
|
};
|
|
|
|
dialog.cancel = function() {
|
|
if (self.user.isInsideOrg()) {
|
|
window.location = "/u/" + self.user.get("username") + "/dashboard";
|
|
} else {
|
|
window.location = "/dashboard";
|
|
}
|
|
};
|
|
|
|
dialog.appendToBody();
|
|
},
|
|
|
|
/**
|
|
* map can't be loaded from the beggining, it needs the DOM to be loaded
|
|
* so we wait until is actually shown to create the mapview and show it
|
|
*/
|
|
enableMap: function() {
|
|
|
|
this.render();
|
|
|
|
var baseLayer = this.map.getBaseLayer();
|
|
|
|
// check if this user has google maps enabled. In case not and the provider is google maps
|
|
// show a message
|
|
if ( typeof cdb.admin.GoogleMapsMapView === 'undefined') {
|
|
if (baseLayer && this.map.isProviderGmaps()) {
|
|
this._showGMapsDeprecationDialog();
|
|
return;
|
|
}
|
|
}
|
|
|
|
this.setupMap();
|
|
|
|
},
|
|
|
|
setupMap: function() {
|
|
|
|
this.$('.tipsy').remove();
|
|
|
|
var self = this;
|
|
|
|
if (!this.map_enabled) {
|
|
|
|
this._addMapView();
|
|
|
|
this.clickTimeout = null;
|
|
|
|
this._bindMissingClickEvents();
|
|
|
|
this.map_enabled = true;
|
|
|
|
$(".map")
|
|
.append('<div class="map-options" />')
|
|
.append("<div class='mobile_bkg' />");
|
|
|
|
this._addBasemapDropdown();
|
|
this._addInfowindow();
|
|
this._addTooltip();
|
|
this._addLegends();
|
|
this._addOverlays();
|
|
|
|
this._showPecan();
|
|
|
|
this._showScratchDialog();
|
|
|
|
if (this.user.featureEnabled('slides')) {
|
|
this._addSlides();
|
|
};
|
|
|
|
var torqueLayer;
|
|
|
|
var type = this.vis.get("type");
|
|
|
|
if (type !== "table") {
|
|
|
|
this._addOverlaysDropdown();
|
|
this._addConfigureCanvasDropdown();
|
|
this._addMapOptionsDropdown();
|
|
|
|
this.canvas.on("change:mode", this._onChangeCanvasMode, this);
|
|
|
|
}
|
|
|
|
this.master_vis.on("change:type", function() {
|
|
if (this.master_vis.previous('type') === 'table') {
|
|
// reaload the map to show overlays and other visualization related stuff
|
|
this.switchMapType();
|
|
}
|
|
}, this);
|
|
|
|
// HACK
|
|
// wait a little bit to give time to the mapview
|
|
// to estabilize
|
|
this.autoSaveBoundsTimer = setTimeout(function() {
|
|
//self.mapView.setAutoSaveBounds();
|
|
self.mapView.on('dragend zoomend', function() {
|
|
self.mapView._saveLocation();
|
|
});
|
|
}, 1000);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_addMapView: function() {
|
|
|
|
var div = this.$('.cartodb-map');
|
|
|
|
var mapViewClass = cdb.admin.LeafletMapView;
|
|
if (this.map.get('provider') === 'googlemaps') {
|
|
var mapViewClass = cdb.admin.GoogleMapsMapView;
|
|
}
|
|
|
|
this.mapView = new mapViewClass({
|
|
el: div,
|
|
map: this.map,
|
|
user: this.user
|
|
});
|
|
|
|
this.mapView.bind('removeLayerView', function(layerView) {
|
|
if (this.layer_selector) this.layer_selector.render();
|
|
}, this);
|
|
|
|
this.mapView.bind('newLayerView', function(layerView, model) {
|
|
if(this.activeLayerView && this.activeLayerView.model.id === model.id) {
|
|
this._bindDataLayer(this.activeLayerView, model);
|
|
|
|
if (this.layer_selector) {
|
|
this.layer_selector.render();
|
|
}
|
|
}
|
|
this._addTimeline();
|
|
}, this);
|
|
|
|
if (this.activeLayerView) {
|
|
this._bindDataLayer(this.activeLayerView, this.activeLayerView.model);
|
|
}
|
|
|
|
},
|
|
|
|
_addConfigureCanvasDropdown: function() {
|
|
if (!this.configureCanvasDropdown) {
|
|
this.configureCanvasDropdown = new cdb.admin.ConfigureCanvasDropdown({
|
|
target: $('.canvas_setup'),
|
|
position: "position",
|
|
canvas: this.canvas,
|
|
template_base: "table/views/canvas_setup_dropdown",
|
|
tick: "left",
|
|
horizontal_position: "left",
|
|
horizontal_offset: "40px"
|
|
});
|
|
|
|
this.addView(this.configureCanvasDropdown);
|
|
|
|
this.configureCanvasDropdown.bind("onDropdownShown", function(){
|
|
this.exportImageView && this.exportImageView.hide();
|
|
}, this);
|
|
|
|
cdb.god.bind("closeDialogs", this.configureCanvasDropdown.hide, this.configureCanvasDropdown);
|
|
$(".canvas_setup").append(this.configureCanvasDropdown.render().el);
|
|
}
|
|
},
|
|
|
|
_addOverlaysDropdown: function() {
|
|
|
|
if (!this.overlaysDropdown) {
|
|
|
|
this.overlaysDropdown = new cdb.admin.OverlaysDropdown({
|
|
vis: this.master_vis,
|
|
canvas: this.canvas,
|
|
mapView: this.mapView,
|
|
target: $('.add_overlay'),
|
|
position: "position",
|
|
collection: this.vis.overlays,
|
|
template_base: "table/views/widget_dropdown",
|
|
tick: "left",
|
|
horizontal_position: "left",
|
|
horizontal_offset: "40px"
|
|
});
|
|
|
|
this.addView(this.overlaysDropdown);
|
|
|
|
this.overlaysDropdown.bind("onOverlayDropdownOpen", function(){
|
|
this.slidesPanel && this.slidesPanel.hide();
|
|
this.exportImageView && this.exportImageView.hide();
|
|
}, this);
|
|
|
|
|
|
cdb.god.bind("closeDialogs", this.overlaysDropdown.hide, this.overlaysDropdown);
|
|
cdb.god.bind("closeOverlayDropdown", this.overlaysDropdown.hide, this.overlaysDropdown);
|
|
|
|
$(".add_overlay").append(this.overlaysDropdown.render().el);
|
|
}
|
|
|
|
},
|
|
|
|
_addBasemapDropdown: function() {
|
|
|
|
if (!this.basemapDropdown) {
|
|
|
|
if (this.vis.get("type") !== "table") {
|
|
// TODO: use templates and _t for texts
|
|
var $options = $('<a href="#" class="option-button dropdown basemap_dropdown"><div class="thumb"></div>Change basemap</a>');
|
|
|
|
$(".map-options").append($options);
|
|
|
|
}
|
|
|
|
this.basemapDropdown = new cdb.admin.DropdownBasemap({
|
|
target: $('.basemap_dropdown'),
|
|
position: "position",
|
|
template_base: "table/views/basemap/basemap_dropdown",
|
|
model: this.map,
|
|
mapview: this.mapView,
|
|
user: this.user,
|
|
baseLayers: this.options.baseLayers,
|
|
tick: "left",
|
|
vertical_offset: 40,
|
|
horizontal_position: "left",
|
|
vertical_position: this.vis.get("type") === 'table' ? "down" : "up",
|
|
horizontal_offset: this.vis.get("type") === 'table' ? 42 : 0
|
|
});
|
|
|
|
this.addView(this.basemapDropdown);
|
|
|
|
this.basemapDropdown.bind("onDropdownShown", function() {
|
|
cdb.god.trigger("closeDialogs");
|
|
});
|
|
|
|
cdb.god.bind("closeDialogs", this.basemapDropdown.hide, this.basemapDropdown);
|
|
|
|
$(".basemap_dropdown").append(this.basemapDropdown.render().el);
|
|
|
|
}
|
|
|
|
// Set active base layer if it already exists
|
|
if (this.map.getBaseLayer()) {
|
|
this.basemapDropdown.setActiveBaselayer();
|
|
}
|
|
|
|
},
|
|
|
|
bindGeoRefCheck: function() {
|
|
if(!this.table.data().fetched) {
|
|
this.table.bind('dataLoaded', function() {
|
|
this.checkGeoRef();
|
|
if (!this.scratchDialog) {
|
|
this._showScratchDialog();
|
|
}
|
|
if (!this.pecanView) {
|
|
this._showPecan();
|
|
}
|
|
}, this);
|
|
} else {
|
|
this.checkGeoRef();
|
|
}
|
|
},
|
|
|
|
activated: function() {
|
|
this.checkGeoRef();
|
|
$(window).scrollTop(0);
|
|
},
|
|
|
|
checkGeoRef: function() {
|
|
if (this.options && this.table) {
|
|
this.georeferenced = this.table.isGeoreferenced();
|
|
if (this.noGeoRefDialog) {
|
|
this.noGeoRefDialog.hide();
|
|
}
|
|
if (!this.georeferenced) {
|
|
if (this.table.data().length > 0) {
|
|
this[ this.table.isSync() ? '_showNoGeoWarning' : 'showNoGeoRefWarning' ]();
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
// Shows a warning dialog when your current dialog doesn't have any
|
|
// geometry on it and it is synchronized
|
|
_showNoGeoWarning: function() {
|
|
var noGeoWarningDialog = 'noGeoWarningDialog_' + this.table.id + '_' + this.table.get('map_id');
|
|
if (this.noGeoWarningDialog || localStorage[noGeoWarningDialog]) {
|
|
return;
|
|
}
|
|
|
|
this.noGeoWarningDialog = cdb.editor.ViewFactory.createDialogByTemplate(
|
|
'table/views/no_geo_warning_template', {
|
|
clean_on_hide: true
|
|
}
|
|
);
|
|
|
|
this.noGeoWarningDialog.bind("hide", function() {
|
|
localStorage[noGeoWarningDialog] = true;
|
|
});
|
|
|
|
this.noGeoWarningDialog.appendToBody();
|
|
},
|
|
|
|
_showPecan: function() {
|
|
|
|
var hasPecan = this.user.featureEnabled('pecan_cookies');
|
|
|
|
var hasData = this.options.table && this.options.table.data() && this.options.table.data().length > 0 ? true : false;
|
|
|
|
if (hasPecan && hasData) {
|
|
|
|
var skipPencanDialog = 'pecan_' + this.options.user.get("username") + "_" + this.options.table.id;
|
|
|
|
if (!localStorage[skipPencanDialog]) {
|
|
|
|
this.pecanView = new cdb.editor.PecanView({
|
|
table: this.options.table,
|
|
backgroundPollingModel: this.options.backgroundPollingModel
|
|
});
|
|
}
|
|
}
|
|
},
|
|
|
|
_showScratchDialog: function() {
|
|
if (this.options.table && this.options.table.data().fetched && this.options.table.data().length === 0) {
|
|
|
|
var skipScratchDialog = 'scratchDialog_' + this.options.table.id + '_' + this.options.table.get('map_id');
|
|
|
|
if (!localStorage[skipScratchDialog]) {
|
|
|
|
this.scratchDialog = new cdb.editor.ScratchView({
|
|
table: this.options.table
|
|
});
|
|
|
|
this.scratchDialog.appendToBody();
|
|
|
|
this.scratchDialog.bind("newGeometry", function(type) {
|
|
this._addGeometry(type);
|
|
}, this);
|
|
|
|
this.scratchDialog.bind("skip", function() {
|
|
localStorage[skipScratchDialog] = true;
|
|
});
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* this function binds click and dblclick events
|
|
* in order to not raise click when user does a dblclick
|
|
*
|
|
* it raises a missingClick when the user clicks on the map
|
|
* but not over a feature or ui component
|
|
*/
|
|
_bindMissingClickEvents: function() {
|
|
var self = this;
|
|
this.mapView.bind('click', function(e) {
|
|
if(self.clickTimeout === null) {
|
|
self.clickTimeout = setTimeout(function() {
|
|
self.clickTimeout = null;
|
|
if(!self.featureHovered) {
|
|
self.trigger('missingClick');
|
|
}
|
|
}, 150);
|
|
}
|
|
//google maps does not send an event
|
|
if(!self.featureHovered && e.preventDefault) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
}
|
|
});
|
|
|
|
this.mapView.bind('dblclick', function() {
|
|
if(self.clickTimeout !== null) {
|
|
clearTimeout(self.clickTimeout);
|
|
self.clickTimeout = null;
|
|
}
|
|
});
|
|
},
|
|
|
|
setActiveLayer: function(layerView) {
|
|
this.activeLayerView = layerView;
|
|
// check if the map is rendered and the layer is in the map
|
|
if(this.mapView && this.mapView.getLayerByCid(layerView.model.cid)) {
|
|
var layerModel = layerView.model;
|
|
this._bindDataLayer(layerView, layerModel);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* when the layer view is created this method is called
|
|
* to attach all the click events
|
|
*/
|
|
_bindDataLayer: function(layerView, layer) {
|
|
var self = this;
|
|
var layerType = layer.get('type');
|
|
|
|
if (layerType === 'CartoDB' || layerType === 'torque') { // unbind previos stuff
|
|
|
|
// Set data layer bindings
|
|
if (self.layerDataView) {
|
|
self.layerDataView.unbind(null, null, this);
|
|
}
|
|
|
|
if (self.layerModel) {
|
|
self.layerModel.unbind(null, null, this);
|
|
}
|
|
|
|
if (self.options.geocoder) {
|
|
self.options.geocoder.unbind(null, null, this);
|
|
}
|
|
|
|
self.infowindowModel = layer.infowindow;
|
|
self.tooltipModel = layer.tooltip;
|
|
self.legendModel = layer.legend;
|
|
|
|
self._bindTable(layer.table);
|
|
self._bindSQLView(layer.sqlView);
|
|
self.layerDataView = self.mapView.getLayerByCid(layer.cid);
|
|
|
|
self.mapView.setActiveLayer(layer);
|
|
self._addLegends();
|
|
self._addTimeline();
|
|
|
|
if (self.layerDataView) {
|
|
self.layerDataView.bind('featureClick', self.featureClick, self);
|
|
self.layerDataView.bind('featureOut', self.featureOut, self);
|
|
self.layerDataView.bind('featureOver', self.featureOver, self);
|
|
self.layerDataView.bind('loading', self.loadingTiles, self);
|
|
self.layerDataView.bind('load', self.loadTiles, self);
|
|
self.layerDataView.bind('error', self.loadTiles, self);
|
|
self.tooltip
|
|
.setLayer(self.layerDataView)
|
|
.enable();
|
|
|
|
}
|
|
|
|
// Set layer model binding
|
|
if (layerView && layer) {
|
|
layer.unbind('startEdition',this._addGeometry, this);
|
|
layer.bind('startEdition', this._addGeometry, this);
|
|
}
|
|
|
|
if(layer) {
|
|
self.layerModel = layer;
|
|
//TODO: unbind this at some point
|
|
layer.bind('change:interactivity', this._updateSQLHeader, this);
|
|
this._updateSQLHeader();
|
|
}
|
|
|
|
if (self.options.geocoder) {
|
|
self.options.geocoder.bind('geocodingComplete geocodingError geocodingCanceled', this.updateDataLayerView, this);
|
|
self.add_related_model(self.options.geocoder);
|
|
}
|
|
|
|
}
|
|
},
|
|
|
|
_cleanLegends: function() {
|
|
|
|
if (this.legends) {
|
|
_.each(this.legends, function(legend) {
|
|
legend.clean();
|
|
});
|
|
|
|
}
|
|
|
|
this.legends = [];
|
|
|
|
},
|
|
|
|
|
|
_getCartoDBLayers: function() {
|
|
|
|
return this.map.layers.models.filter(function(layerModel) {
|
|
return layerModel.get("type") === 'CartoDB'
|
|
});
|
|
|
|
},
|
|
|
|
_onKeyDown: function(e) {
|
|
if (this.overlays && e.which == 86 && (e.ctrlKey || e.metaKey)) {
|
|
this.overlays.paste();
|
|
}
|
|
},
|
|
|
|
_onChangeCanvasMode: function() {
|
|
|
|
var self = this;
|
|
|
|
cdb.god.trigger("closeDialogs");
|
|
|
|
var mode = this.canvas.get("mode");
|
|
|
|
if (mode === "desktop") {
|
|
|
|
this._showDesktopCanvas(mode);
|
|
|
|
if (this.overlays.loader && this.overlays.fullscreen) {
|
|
setTimeout(function() {
|
|
self.overlays && self.overlays._positionOverlaysVertically(true);
|
|
}, 500);
|
|
}
|
|
|
|
} else if (mode === "mobile") {
|
|
|
|
this._showMobileCanvas(mode);
|
|
|
|
setTimeout(function() {
|
|
self.overlays && self.overlays._positionOverlaysVertically(true);
|
|
}, 300);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_showMobileCanvas: function(mode) {
|
|
|
|
var self = this;
|
|
|
|
var width = 288;
|
|
var height = 476;
|
|
|
|
this.overlays._hideOverlays("desktop");
|
|
|
|
var $map = $("div.map div.cartodb-map");
|
|
|
|
this.$el.addClass(mode);
|
|
|
|
// Animations step - 1
|
|
var onBackgroundShown = function() {
|
|
|
|
$map.animate(
|
|
{ width: width, marginLeft: -Math.round(width/2) - 1, left: "50%" },
|
|
{ easing: "easeOutQuad", duration: 200, complete: onCanvasLandscapeStretched }
|
|
);
|
|
|
|
};
|
|
|
|
// Animations step - 2
|
|
var onCanvasPortraitStretched = function() {
|
|
|
|
self.$el.find(".mobile_bkg").animate(
|
|
{ opacity: 1 },
|
|
{ duration: 250 }
|
|
);
|
|
|
|
self.overlays._showOverlays(mode);
|
|
|
|
// Let's set center view for mobile mode
|
|
var center = self.map.get('center');
|
|
self.mapView.invalidateSize();
|
|
$map.fadeOut(250);
|
|
|
|
setTimeout(function() {
|
|
self.mapView.map.setCenter(center);
|
|
$map.fadeIn(250);
|
|
},300);
|
|
|
|
};
|
|
|
|
// Animations step - 3
|
|
var onCanvasLandscapeStretched = function() {
|
|
|
|
$map.animate(
|
|
{ height: height, marginTop: -Math.round(height/2) + 23, top: "50%" },
|
|
{ easing: "easeOutQuad", duration: 200, complete: onCanvasPortraitStretched }
|
|
);
|
|
|
|
};
|
|
|
|
onBackgroundShown();
|
|
|
|
this._enableAnimatedMap();
|
|
this._enableMobileLayout();
|
|
|
|
},
|
|
|
|
_enableMobileLayout: function() {
|
|
|
|
if (!this.mobile) {
|
|
|
|
var torqueLayer;
|
|
|
|
this.mobile = new cdb.admin.overlays.Mobile({
|
|
mapView: this.mapView,
|
|
overlays: this.overlays,
|
|
map: this.map
|
|
});
|
|
|
|
this.mapView.$el.append(this.mobile.render().$el);
|
|
|
|
} else {
|
|
this.mobile.show();
|
|
}
|
|
|
|
},
|
|
|
|
_disableMobileLayout: function() {
|
|
|
|
if (this.mobile) this.mobile.hide();
|
|
|
|
},
|
|
|
|
_showDesktopCanvas: function(mode) {
|
|
|
|
var self = this;
|
|
|
|
this.overlays._hideOverlays("mobile");
|
|
|
|
this.$el.removeClass("mobile");
|
|
|
|
this.$el.find(".mobile_bkg").animate({ opacity: 0}, 250);
|
|
|
|
var
|
|
$map = $("div.map div.cartodb-map"),
|
|
top = $map.css("top"),
|
|
left = $map.css("left"),
|
|
mTop = $map.css("marginTop"),
|
|
mLeft = $map.css("marginLeft"),
|
|
curWidth = $map.width(),
|
|
curHeight = $map.height(),
|
|
autoWidth = $map.css({width: 'auto', marginLeft: 0, left: "15px"}).width(); //temporarily change to auto and get the width.
|
|
autoHeight = $map.css({height: 'auto', marginTop: 0, top: "82px" }).height(); //temporarily change to auto and get the width.
|
|
|
|
$map.height(curHeight);
|
|
$map.width(curWidth);
|
|
|
|
$map.css({ top: top, left: left, marginLeft: mLeft, marginTop: mTop, height: curHeight, width: curWidth });
|
|
|
|
var onSecondAnimationFinished = function() {
|
|
|
|
$map.css('width', 'auto');
|
|
self.overlays._showOverlays(mode);
|
|
|
|
// Let's set center view for desktop mode
|
|
var center = self.map.get('center');
|
|
self.mapView.invalidateSize();
|
|
|
|
setTimeout(function() {
|
|
self.mapView.map.setCenter(center);
|
|
},300);
|
|
|
|
};
|
|
|
|
var onFirstAnimationFinished = function() {
|
|
|
|
$map.css('height', 'auto');
|
|
$map.animate(
|
|
{ width: autoWidth, left: "15px", marginLeft: "0"},
|
|
{ easing: "easeOutQuad", duration: 200, complete: onSecondAnimationFinished }
|
|
);
|
|
|
|
};
|
|
|
|
var stretchMapLandscape = function() {
|
|
$map.animate(
|
|
{ height: autoHeight, top: "82", marginTop: "0"},
|
|
{ easing: "easeOutQuad", duration: 200, complete: onFirstAnimationFinished }
|
|
);
|
|
};
|
|
|
|
stretchMapLandscape();
|
|
|
|
this._disableAnimatedMap();
|
|
this._disableMobileLayout();
|
|
|
|
},
|
|
|
|
_enableAnimatedMap: function() {
|
|
|
|
var self = this;
|
|
|
|
setTimeout(function() {
|
|
self.$el.addClass("animated");
|
|
}, 800)
|
|
|
|
},
|
|
|
|
_disableAnimatedMap: function() {
|
|
this.$el.removeClass("animated");
|
|
},
|
|
|
|
_addMapOptionsDropdown: function() {
|
|
|
|
if (!this.mapOptionsDropdown) {
|
|
|
|
var $options = $("<a href='#show-options' class='option-button show-table-options'>Options</a>");
|
|
|
|
this.$options = $options;
|
|
|
|
$(".map-options").append($options);
|
|
|
|
this.mapOptionsDropdown = new cdb.admin.MapOptionsDropdown({
|
|
target: $('.show-table-options'),
|
|
template_base: "table/views/map_options_dropdown",
|
|
table: table,
|
|
model: this.map,
|
|
mapview: this.mapView,
|
|
collection: this.vis.overlays,
|
|
user: this.user,
|
|
vis: this.vis,
|
|
canvas: this.canvas,
|
|
position: "position",
|
|
tick: "left",
|
|
vertical_position: "up",
|
|
horizontal_position: "left",
|
|
horizontal_offset: "-3px"
|
|
});
|
|
|
|
this._bindMapOptions();
|
|
|
|
this.addView(this.mapOptionsDropdown);
|
|
|
|
$(".show-table-options").append(this.mapOptionsDropdown.render().el);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_bindMapOptions: function() {
|
|
|
|
this.mapOptionsDropdown.bind("onDropdownShown", function() {
|
|
cdb.god.trigger("closeDialogs");
|
|
this.$options.addClass("open");
|
|
}, this);
|
|
|
|
this.mapOptionsDropdown.bind("onDropdownHidden", function() {
|
|
this.$options.removeClass("open");
|
|
}, this);
|
|
|
|
this.mapOptionsDropdown.bind("createOverlay", function(overlay_type, property) {
|
|
this.vis.overlays.createOverlayByType(overlay_type, property);
|
|
}, this);
|
|
|
|
cdb.god.bind("closeDialogs", this.mapOptionsDropdown.hide, this.mapOptionsDropdown);
|
|
|
|
},
|
|
|
|
_addOverlays: function() {
|
|
this.overlays = new cdb.admin.MapOverlays({
|
|
headerMessageIsVisible: this._shouldAddSQLViewHeader(),
|
|
vis: this.vis,
|
|
canvas: this.canvas,
|
|
mapView: this.mapView,
|
|
master_vis: this.master_vis,
|
|
mapToolbar: this.$el.find(".map_toolbar")
|
|
});
|
|
|
|
},
|
|
|
|
_exportImage: function(e) {
|
|
|
|
this.killEvent(e);
|
|
|
|
if (this.exportImageView) {
|
|
return;
|
|
}
|
|
|
|
this.exportImageView = new cdb.admin.ExportImageView({
|
|
authTokens: this.authTokens,
|
|
height: this.mapView.$el.height(),
|
|
map: this.map,
|
|
mapView: this.mapView,
|
|
overlays: this.overlays,
|
|
user: this.options.user,
|
|
vis: this.vis,
|
|
vizjson: this.vis.vizjsonURL(),
|
|
width: this.mapView.$el.width()
|
|
});
|
|
|
|
this.exportImageView.bind("was_removed", function() {
|
|
this.exportImageView = null;
|
|
}, this);
|
|
|
|
this.mapView.$el.append(this.exportImageView.render().$el);
|
|
|
|
cdb.god.bind("panel_action", function(action) {
|
|
if (action !== "hide" && this.exportImageView) {
|
|
this.exportImageView.hide();
|
|
}
|
|
}, this);
|
|
},
|
|
|
|
_addSlides: function() {
|
|
|
|
if (!this.vis.isVisualization()) return;
|
|
|
|
this.slidesPanel = new cdb.admin.SlidesPanel({
|
|
user: this.user,
|
|
slides: this.vis.slides,
|
|
toggle: this.$el.find(".toggle_slides")
|
|
});
|
|
|
|
this.slidesPanel.bind("onChangeVisible", function() {
|
|
this.exportImageView && this.exportImageView.hide();
|
|
}, this);
|
|
|
|
this.$el.append(this.slidesPanel.render().el);
|
|
|
|
this.addView(this.slidesPanel);
|
|
|
|
},
|
|
|
|
_addLegends: function() {
|
|
|
|
var self = this;
|
|
|
|
this._cleanLegends();
|
|
|
|
if (!this.map.get("legends")) {
|
|
return;
|
|
}
|
|
|
|
var models = this.map.layers.models;
|
|
|
|
for (var i = models.length - 1; i >= 0; --i) {
|
|
var layer = models[i];
|
|
self._addLegend(layer);
|
|
}
|
|
|
|
},
|
|
|
|
_addLegend: function(layer) {
|
|
|
|
var type = layer.get('type');
|
|
|
|
if (type === 'CartoDB' || type === 'torque') {
|
|
|
|
if (this.table && this.mapView) {
|
|
|
|
if (this.legend) this.legend.clean();
|
|
|
|
if (layer.get("visible")) {
|
|
|
|
var legend = new cdb.geo.ui.Legend({
|
|
model: layer.legend,
|
|
mapView: this.mapView,
|
|
table: this.table
|
|
});
|
|
|
|
if (this.legends) {
|
|
this.legends.push(legend);
|
|
this._renderStackedLengeds();
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
},
|
|
|
|
_toggleLegends: function() {
|
|
if (this.map.get("legends")) {
|
|
this._addLegends();
|
|
} else {
|
|
this._cleanLegends();
|
|
}
|
|
},
|
|
|
|
_addTimeline: function() {
|
|
if (!this.mapView) return;
|
|
// check if there is some torque layer
|
|
if(!this.map.layers.any(function(lyr) { return lyr.get('type') === 'torque' && lyr.get('visible'); })) {
|
|
this.timeline && this.timeline.clean();
|
|
this.timeline = null;
|
|
} else {
|
|
var layer = this.map.layers.getLayersByType('torque')[0];
|
|
var steps = layer.wizard_properties.get('torque-frame-count');
|
|
|
|
if (this.timeline) {
|
|
// check if the model is different
|
|
if (this.timeline.torqueLayer.model.cid !== layer.cid) {
|
|
this.timeline.clean();
|
|
this.timeline = null;
|
|
}
|
|
}
|
|
|
|
layerView = this.mapView.getLayerByCid(layer.cid);
|
|
|
|
if (layerView && typeof layerView.getStep !== "undefined" && steps > 1) {
|
|
if (!this.timeline) {
|
|
this.timeline = new cdb.geo.ui.TimeSlider({
|
|
layer: layerView,
|
|
width: "auto"
|
|
});
|
|
|
|
this.mapView.$el.append(this.timeline.render().$el);
|
|
this.addView(this.timeline);
|
|
} else {
|
|
this.timeline.setLayer(layerView);
|
|
}
|
|
}
|
|
else if (this.timeline) {
|
|
this.timeline.clean();
|
|
this.timeline = null;
|
|
}
|
|
}
|
|
},
|
|
|
|
_renderStackedLengeds: function() {
|
|
|
|
if (this.stackedLegend) this.stackedLegend.clean();
|
|
if (this.legend) this.legend.clean();
|
|
|
|
this.stackedLegend = new cdb.geo.ui.StackedLegend({
|
|
legends: this.legends
|
|
});
|
|
|
|
this.mapView.$el.append(this.stackedLegend.render().$el);
|
|
this.addView(this.stackedLegend);
|
|
|
|
},
|
|
|
|
_renderLegend: function() {
|
|
|
|
if (this.legend) this.legend.clean();
|
|
|
|
this.legend = this.legends[0];
|
|
|
|
this.mapView.$el.append(this.legend.render().$el);
|
|
|
|
if (!this.legend.model.get("type")) this.legend.hide();
|
|
else this.legend.show();
|
|
|
|
this.addView(this.legend);
|
|
|
|
},
|
|
|
|
_addTooltip: function() {
|
|
if(this.tooltip) this.tooltip.clean();
|
|
if(this.table && this.mapView) {
|
|
this.tooltip = new cdb.admin.Tooltip({
|
|
model: this.tooltipModel,
|
|
table: this.table,
|
|
mapView: this.mapView,
|
|
omit_columns: ['cartodb_id'] // don't show cartodb_id while hover
|
|
});
|
|
this.mapView.$el.append(this.tooltip.render().el);
|
|
this.tooltip.bind('editData', this._editData, this);
|
|
this.tooltip.bind('removeGeom', this._removeGeom, this);
|
|
this.tooltip.bind('editGeom', this._editGeom, this);
|
|
if (this.layerDataView) {
|
|
this.tooltip
|
|
.setLayer(this.layerDataView)
|
|
.enable();
|
|
}
|
|
}
|
|
},
|
|
|
|
_addInfowindow: function() {
|
|
if(this.infowindow) this.infowindow.clean();
|
|
if(this.table && this.mapView) {
|
|
this.infowindow = new cdb.admin.MapInfowindow({
|
|
model: this.infowindowModel,
|
|
mapView: this.mapView,
|
|
table: this.table
|
|
});
|
|
this.mapView.$el.append(this.infowindow.el);
|
|
|
|
// Editing geometry
|
|
if(this.geometryEditor) {
|
|
this.geometryEditor.discard();
|
|
this.geometryEditor.clean();
|
|
}
|
|
|
|
this.geometryEditor = new cdb.admin.GeometryEditor({
|
|
user: this.user,
|
|
model: this.table
|
|
});
|
|
|
|
this.geometryEditor.mapView = this.mapView;
|
|
this.mapView.$el.append(this.geometryEditor.render().el);
|
|
this.geometryEditor.hide();
|
|
|
|
this.geometryEditor.bind('editStart', this.hideDataLayer, this);
|
|
this.geometryEditor.bind('editDiscard', this.showDataLayer, this);
|
|
this.geometryEditor.bind('editFinish', this.showDataLayer, this);
|
|
this.geometryEditor.bind('editFinish', this.updateDataLayerView, this);
|
|
this.geometryEditor.bind('geomCreated', function(row) {
|
|
this.table.data().add(row);
|
|
}, this);
|
|
|
|
var self = this;
|
|
|
|
this.infowindow.bind('editData', this._editData, this);
|
|
this.infowindow.bind('removeGeom', this._removeGeom, this);
|
|
this.infowindow.bind('editGeom', this._editGeom, this);
|
|
|
|
this.infowindow.bind('openInfowindowPanel', function() {
|
|
this.activeLayerView.showModule('infowindow', 'fields');
|
|
}, this);
|
|
|
|
this.infowindow.bind('close', function() {
|
|
if (this.tooltip) {
|
|
this.tooltip.setFilter(null);
|
|
}
|
|
}, this);
|
|
|
|
this.table.bind('remove:row', this.updateDataLayerView, this);
|
|
|
|
this.table.bind('change:dataSource', function() {
|
|
if (this.geometryEditor) this.geometryEditor.discard();
|
|
}, this);
|
|
|
|
this.map.bind('change:provider', function() {
|
|
if (this.geometryEditor) this.geometryEditor.discard();
|
|
}, this);
|
|
}
|
|
},
|
|
|
|
_editGeom: function(row) {
|
|
// when provider is leaflet move the world to [-180, 180]
|
|
// because vector features are only rendered on that slice
|
|
if (this.map.get('provider') === 'leaflet') {
|
|
this.map.clamp();
|
|
}
|
|
this.geometryEditor.editGeom(row);
|
|
},
|
|
|
|
/**
|
|
* Shows edit data modal window
|
|
*/
|
|
_editData: function(row) {
|
|
if (!this.table.isReadOnly()) {
|
|
var self = this;
|
|
row.fetch({ cache: false, no_geom: true, success: function() {
|
|
var dlg = new cdb.editor.FeatureDataView({
|
|
row: row,
|
|
provider: self.map.get('provider'),
|
|
baseLayer: self.map.getBaseLayer().clone(),
|
|
dataLayer: self.layerModel.clone(),
|
|
currentZoom: self.map.getZoom(),
|
|
enter_to_confirm: false,
|
|
table: self.table,
|
|
user: self.user,
|
|
clean_on_hide: true,
|
|
onDone: self.updateDataLayerView.bind(self) // Refreshing layer when changes have been done
|
|
});
|
|
|
|
dlg.appendToBody();
|
|
}});
|
|
|
|
return false;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* triggers an removeGeom event when the geometry
|
|
* is removed from the server
|
|
*/
|
|
_removeGeom: function(row) {
|
|
if (!this.table.isReadOnly()) {
|
|
var view = new cdb.editor.DeleteRowView({
|
|
name: 'feature',
|
|
table: this.table,
|
|
row: row,
|
|
clean_on_hide: true,
|
|
enter_to_confirm: true,
|
|
wait: true // to not remove from parent collection until server-side confirmed deletion
|
|
});
|
|
view.appendToBody();
|
|
|
|
return false;
|
|
}
|
|
},
|
|
|
|
_addGeometry: function(type) {
|
|
this.geometryEditor.createGeom(this.table.data().newRow(), type);
|
|
},
|
|
|
|
_bindTable: function(table) {
|
|
if (this.table) {
|
|
this.table.unbind(null, null, this);
|
|
}
|
|
|
|
this.table = table;
|
|
|
|
this.table.bind('change:dataSource', this._hideInfowindow, this);
|
|
this.table.bind('change:dataSource', this._updateSQLHeader, this);
|
|
this.table.bind('change:schema', this._updateSQLHeader, this);
|
|
|
|
this.table.bind('data:saved', this.updateDataLayerView, this);
|
|
|
|
this._addInfowindow();
|
|
|
|
this._addLegends();
|
|
this._addTooltip();
|
|
|
|
this.bindGeoRefCheck();
|
|
},
|
|
|
|
_bindSQLView: function(sqlView) {
|
|
if(this.sqlView) {
|
|
this.sqlView.unbind(null, null, this);
|
|
}
|
|
this.sqlView = sqlView;
|
|
this.sqlView.bind('reset error', this._updateSQLHeader, this);
|
|
this.sqlView.bind('loading', this._renderLoading, this);
|
|
this._updateSQLHeader();
|
|
},
|
|
|
|
_renderLoading: function(opts) {
|
|
this._removeSQLViewHeader();
|
|
|
|
//TODO: remove this hack
|
|
if ($('.table_panel').length > 0) {
|
|
panel_opened = $('.table_panel').css("right").replace("px","") == 0
|
|
}
|
|
|
|
var html = this.getTemplate('table/views/sql_view_notice_loading')({
|
|
panel_opened: panel_opened
|
|
});
|
|
|
|
if (this.overlays) {
|
|
this.overlays.setHeaderMessageIsVisible(true);
|
|
}
|
|
|
|
this.$('.cartodb-map').after(html);
|
|
},
|
|
|
|
_updateSQLHeader: function() {
|
|
if (this._shouldAddSQLViewHeader()) {
|
|
this._addSQLViewHeader();
|
|
} else {
|
|
this._removeSQLViewHeader();
|
|
}
|
|
},
|
|
|
|
_shouldAddSQLViewHeader: function() {
|
|
return this.table && this.table.isInSQLView();
|
|
},
|
|
|
|
loadingTiles: function() {
|
|
if (this.overlays.loader) this.overlays.loader.show();
|
|
},
|
|
|
|
loadTiles: function() {
|
|
if (this.overlays.loader) this.overlays.loader.hide();
|
|
},
|
|
|
|
featureOver: function(e, latlon, pxPos, data) {
|
|
if(this.infowindowModel.get('disabled')) return;
|
|
this.mapView.setCursor('pointer');
|
|
this.featureHovered = data;
|
|
},
|
|
|
|
featureOut: function() {
|
|
if(this.infowindowModel.get('disabled')) return;
|
|
this.mapView.setCursor('auto');
|
|
this.featureHovered = null;
|
|
},
|
|
|
|
featureClick: function(e, latlon, pxPos, data) {
|
|
if(this.infowindowModel.get('disabled')) return;
|
|
if(!this.geometryEditor.isEditing()) {
|
|
if(data.cartodb_id) {
|
|
this.infowindow
|
|
.setLatLng(latlon)
|
|
.setFeatureInfo(data.cartodb_id)
|
|
.showInfowindow();
|
|
|
|
this.tooltip.setFilter(function(feature) {
|
|
return feature.cartodb_id !== data.cartodb_id;
|
|
}).hide();
|
|
} else {
|
|
cdb.log.error("can't show infowindow, no cartodb_id on data");
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Move all necessary blocks when panel is openned (normal, narrowed,...) or closed
|
|
*/
|
|
_moveInfo: function(type) {
|
|
if (type === "show") {
|
|
this.$el
|
|
.removeClass('narrow')
|
|
.addClass('displaced');
|
|
} else if (type === "hide") {
|
|
this.$el.removeClass('narrow displaced');
|
|
} else if (type === "narrow") {
|
|
this.$el.addClass('narrow displaced');
|
|
}
|
|
},
|
|
|
|
render: function() {
|
|
|
|
this.$el.html('');
|
|
|
|
this.$el
|
|
.removeClass("mobile")
|
|
.removeClass("derived")
|
|
.removeClass("table");
|
|
|
|
this.$el.addClass(this.vis.isVisualization() ? 'derived': 'table');
|
|
var provider = this.map.get("provider");
|
|
|
|
this.$el.append(this.template({
|
|
slides_enabled: this.user.featureEnabled('slides'),
|
|
type: this.vis.get('type'),
|
|
exportEnabled: !this.map.isProviderGmaps()
|
|
}));
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
showDataLayer: function() {
|
|
this.mapView.enableInteraction();
|
|
this.layerDataView.setOpacity && this.layerDataView.setOpacity(1.0);
|
|
},
|
|
|
|
hideDataLayer: function() {
|
|
this.mapView.disableInteraction();
|
|
this.layerDataView.setOpacity && this.layerDataView.setOpacity(0.5);
|
|
},
|
|
|
|
/**
|
|
* reload tiles
|
|
*/
|
|
updateDataLayerView: function() {
|
|
if(this.layerDataView) {
|
|
this.layerDataView.invalidate();
|
|
}
|
|
},
|
|
/**
|
|
* Paints a dialog with a warning when the user hasn't any georeferenced row
|
|
* @method showNoGeorefWarning
|
|
*/
|
|
showNoGeoRefWarning: function() {
|
|
var warningStorageName = 'georefNoContentWarningShowed_' + this.table.id + '_' + this.table.get('map_id');
|
|
|
|
// if the dialog already has been shown, we don't show it again
|
|
if(!this.noGeoRefDialog && !this.table.isInSQLView() && (!localStorage[warningStorageName])) {
|
|
localStorage[warningStorageName] = true;
|
|
|
|
this.noGeoRefDialog = new cdb.editor.GeoreferenceView({
|
|
table: this.table,
|
|
user: this.user
|
|
});
|
|
this.noGeoRefDialog.appendToBody();
|
|
}
|
|
|
|
},
|
|
|
|
//adds the green indicator when a query is applied
|
|
_addSQLViewHeader: function() {
|
|
this.$('.sqlview').remove();
|
|
var total = this.table.data().size();
|
|
var warnMsg = null;
|
|
// if the layer does not suppor interactivity do not show the message
|
|
if (this.layerModel && !this.layerModel.get('interactivity') && this.layerModel.wizard_properties.supportsInteractivity()) {
|
|
warnMsg = this._TEXTS.no_interaction_warn;
|
|
}
|
|
if (this.layerModel && !this.layerModel.table.containsColumn('the_geom_webmercator')) {
|
|
warnMsg = _t('the_geom_webmercator column should be selected');
|
|
}
|
|
var html = this.getTemplate('table/views/sql_view_notice')({
|
|
empty: !total,
|
|
isVisualization: this.vis.isVisualization(),
|
|
warnMsg: warnMsg
|
|
});
|
|
|
|
this.$('.cartodb-map').after(html);
|
|
|
|
if (this.overlays) {
|
|
this.overlays.setHeaderMessageIsVisible(true);
|
|
}
|
|
},
|
|
|
|
_removeSQLViewHeader: function() {
|
|
this.$('.sqlview').remove();
|
|
|
|
if (this.overlays) {
|
|
this.overlays.setHeaderMessageIsVisible(false);
|
|
}
|
|
},
|
|
|
|
_toggleSlides: function(e) {
|
|
this.killEvent(e);
|
|
this.slidesPanel && this.slidesPanel.toggle();
|
|
},
|
|
|
|
_clearView: function(e) {
|
|
this.killEvent(e);
|
|
this.activeLayerView.model.clearSQLView();
|
|
return false;
|
|
},
|
|
|
|
_tableFromQuery: function(e) {
|
|
this.killEvent(e);
|
|
|
|
var duplicate_dialog = new cdb.editor.DuplicateDatasetView({
|
|
model: this.table,
|
|
user: this.user,
|
|
clean_on_hide: true
|
|
});
|
|
duplicate_dialog.appendToBody();
|
|
}
|
|
|
|
});
|