478 lines
14 KiB
JavaScript
478 lines
14 KiB
JavaScript
|
cdb.admin.TableEditorView = cdb.core.View.extend({
|
||
|
|
||
|
el: document.body,
|
||
|
|
||
|
events: {
|
||
|
'keypress': 'keyPress',
|
||
|
'keyup': 'keyUp'
|
||
|
},
|
||
|
|
||
|
initialize: function(options) {
|
||
|
this.table = null;
|
||
|
this.selectedMenu = null; // enable this
|
||
|
this.workViewActive = 'table';
|
||
|
// for oppening a menu in the startup
|
||
|
// Get user layers as well
|
||
|
this.options.user_data.get_layers = true;
|
||
|
|
||
|
this.user = new cdb.admin.User(this.options.user_data);
|
||
|
cdb.config.set('user', this.user);
|
||
|
|
||
|
this._initModels();
|
||
|
this._initViews();
|
||
|
|
||
|
cdb.admin.hotkeys.enable();
|
||
|
this.keyBind();
|
||
|
this._initBinds();
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Bind the keystrokes associated with menu actions
|
||
|
* alt + <- : show right menu
|
||
|
* alt + -> : hide right menu
|
||
|
* alt + c : toggle carto
|
||
|
* alt + s : toggle sql
|
||
|
* @method keyBind
|
||
|
*/
|
||
|
keyBind: function() {
|
||
|
var self = this;
|
||
|
cdb.god.bind('hotkey:d', function(e) {
|
||
|
self.menu.isOpen
|
||
|
? self.menu.hide()
|
||
|
: self.menu.show('sql_mod');
|
||
|
});
|
||
|
cdb.god.bind('hotkey:s', function(e) {
|
||
|
self.menu.show('sql_mod');
|
||
|
})
|
||
|
cdb.god.bind('hotkey:c', function(e) {
|
||
|
self.menu.show('style_mod');
|
||
|
})
|
||
|
},
|
||
|
|
||
|
_initModels: function() {
|
||
|
var self = this;
|
||
|
this.vis = new cdb.admin.Visualization(this.options.vis_data);
|
||
|
// when the user changes slides the visualization is changed
|
||
|
// but we want to keep the master one for tasks that require to use it
|
||
|
this.master_vis = new cdb.admin.Visualization(_.omit(this.options.vis_data, 'children'));
|
||
|
|
||
|
this.vis.setMaster(this.master_vis);
|
||
|
|
||
|
// if slides are available enable first one to fetch the map
|
||
|
// if not use the master map
|
||
|
if (this.vis.slides.length === 0) {
|
||
|
this.vis.map.set(this.vis.map.parse(this.options.map_data));
|
||
|
} else {
|
||
|
this.vis.activeSlide(0);
|
||
|
}
|
||
|
|
||
|
this.map = this.vis.map;
|
||
|
this.authTokens = this.options.vis_data.auth_tokens || [];
|
||
|
this.vis.enableOverlays();
|
||
|
|
||
|
var layers = this.options.basemaps;
|
||
|
|
||
|
this.baseLayers = this.user.layers;
|
||
|
this.baseLayers.each(function(m) {
|
||
|
m.set('category', 'Yours');
|
||
|
})
|
||
|
|
||
|
_(layers).each(function(catLayers, cat) {
|
||
|
_(catLayers).map(function(m) {
|
||
|
var baseTypes = {
|
||
|
'googlemaps': cdb.admin.GMapsBaseLayer
|
||
|
}
|
||
|
var BaseType = baseTypes[m.className] || cdb.admin.TileLayer;
|
||
|
|
||
|
// Default basemaps are defined in app_config.yml and
|
||
|
// lib/assets/javascripts/cartodb/table/default_layers.js
|
||
|
var tmpLayer = {
|
||
|
name: m.name,
|
||
|
className: m.className,
|
||
|
base_type: m.baseType || 'default',
|
||
|
urlTemplate: m.urlTemplate,
|
||
|
minZoom: m.minZoom,
|
||
|
maxZoom: m.maxZoom,
|
||
|
attribution: m.attribution,
|
||
|
subdomains: m.subdomains,
|
||
|
baseName: m.baseName,
|
||
|
style: m.style ? JSON.parse(m.style): null,
|
||
|
labels: m.labels,
|
||
|
read_only: true,
|
||
|
category: cat
|
||
|
};
|
||
|
|
||
|
if (m.tileSize) {
|
||
|
tmpLayer.tileSize = m.tileSize;
|
||
|
}
|
||
|
|
||
|
if (m.zoomOffset) {
|
||
|
tmpLayer.zoomOffset = m.zoomOffset;
|
||
|
}
|
||
|
|
||
|
// Default basemaps are defined in app_config.yml and
|
||
|
// lib/assets/javascripts/cartodb/table/default_layers.js
|
||
|
self.baseLayers.add(new BaseType(tmpLayer));
|
||
|
});
|
||
|
});
|
||
|
|
||
|
// Background polling model
|
||
|
// - It takes care of the background imports and geocodings
|
||
|
this.backgroundPollingModel = new cdb.editor.BackgroundPollingModel({
|
||
|
geocodingsPolling: true,
|
||
|
importsPolling: false
|
||
|
}, {
|
||
|
user: this.user,
|
||
|
vis: this.vis
|
||
|
});
|
||
|
},
|
||
|
|
||
|
_resetModel: function(_id) {
|
||
|
this.vis = new cdb.admin.Visualization({ id: _id });
|
||
|
this.vis.fetch();
|
||
|
},
|
||
|
|
||
|
_initViews: function() {
|
||
|
this.globalError = new cdb.admin.GlobalError({
|
||
|
el: $('.globalerror')
|
||
|
});
|
||
|
this.globalError.listenGlobal();
|
||
|
this.addView(this.globalError);
|
||
|
|
||
|
// *** Locked visualization (table or visualization type)?
|
||
|
if (this.vis.get('locked')) {
|
||
|
var viewModel = new cdb.editor.ChangeLockViewModel({
|
||
|
items: [this.vis],
|
||
|
contentType: this.vis.isVisualization() ? 'maps' : 'datasets'
|
||
|
});
|
||
|
var view = new cdb.editor.ChangeLockView({
|
||
|
model: viewModel,
|
||
|
ownerName: this.vis.permission.owner.get('username'),
|
||
|
isOwner: this.vis.permission.isOwner(this.user),
|
||
|
template: cdb.templates.getTemplate('common/dialogs/change_lock/templates/unlock_to_editor'),
|
||
|
clean_on_hide: true,
|
||
|
enter_to_confirm: true
|
||
|
});
|
||
|
var self = this;
|
||
|
view.cancel = function() {
|
||
|
window.location = self.user.viewUrl().dashboard()[ self.vis.isVisualization() ? 'maps' : 'datasets' ]().urlToPath('locked');
|
||
|
};
|
||
|
|
||
|
view.appendToBody();
|
||
|
}
|
||
|
|
||
|
// *** Warning opening Builder maps in the old editor
|
||
|
if (this.vis.isVisualization() && this.vis.get('uses_builder_features')) {
|
||
|
var view = new cdb.editor.BuilderFeaturesWarningDialog({
|
||
|
clean_on_hide: true,
|
||
|
enter_to_confirm: true
|
||
|
});
|
||
|
var self = this;
|
||
|
view.cancel = function() {
|
||
|
window.location = self.user.viewUrl().dashboard()[ self.vis.isVisualization() ? 'maps' : 'datasets' ]();
|
||
|
};
|
||
|
|
||
|
view.appendToBody();
|
||
|
}
|
||
|
|
||
|
// *** tabs
|
||
|
this.tabs = new cdb.admin.Tabs({
|
||
|
el: this.$('nav'),
|
||
|
slash: true
|
||
|
});
|
||
|
this.addView(this.tabs);
|
||
|
|
||
|
// *** work pane (table and map)
|
||
|
this.workView = new cdb.ui.common.TabPane({
|
||
|
el: this.$('.panes')
|
||
|
});
|
||
|
|
||
|
this.addView(this.workView);
|
||
|
|
||
|
// *** right menu
|
||
|
// We need to provide the master/parent vis to allow creating a new visualization
|
||
|
// and the regular vis to work with the layers
|
||
|
this.menu = new cdb.admin.LayersPanel({
|
||
|
vis: this.vis,
|
||
|
master_vis: this.master_vis,
|
||
|
user: this.user,
|
||
|
globalError: this.globalError
|
||
|
});
|
||
|
|
||
|
this.$el.append(this.menu.render().el);
|
||
|
this.menu.hide();
|
||
|
this.addView(this.menu);
|
||
|
|
||
|
this.menu.bind('switch', function(layerView) {
|
||
|
this.setTable(layerView.table, layerView.sqlView);
|
||
|
if(!this.tableTab) {
|
||
|
this._initTableMap();
|
||
|
this.table.trigger('change', this.table);
|
||
|
}
|
||
|
this.tableTab.setActiveLayer(layerView);
|
||
|
this.mapTab.setActiveLayer(layerView);
|
||
|
this.header.setActiveLayer(layerView);
|
||
|
}, this);
|
||
|
|
||
|
// Set watching notifier if needed
|
||
|
if (!this.vis.isVisualization()) {
|
||
|
this._setWatchingNotifier();
|
||
|
}
|
||
|
|
||
|
// global click
|
||
|
enableClickOut(this.$el);
|
||
|
|
||
|
// On resize window...
|
||
|
$(window).bind("resize", this._onResize);
|
||
|
|
||
|
// If import layer fail, show dialog
|
||
|
this.backgroundPollingModel.bind('importLayerFail', function(errorMsg) {
|
||
|
cdb.editor.ViewFactory.createDialogByTemplate('common/templates/fail', { msg: errorMsg })
|
||
|
.render().appendToBody();
|
||
|
});
|
||
|
|
||
|
this.backgroundPollingModel.bind('geocodingCompleted', function(mdl) {
|
||
|
// Refresh data in order to have cartodb_georef_status column updated
|
||
|
// but only if current layer data is the geocoded one
|
||
|
if (this.table && this.table.get('id') === mdl.get('table_name') && this.table.data) {
|
||
|
this.table.data().refresh()
|
||
|
}
|
||
|
// Refresh map
|
||
|
if (this.mapTab && this.mapTab.updateDataLayerView) {
|
||
|
this.mapTab.updateDataLayerView()
|
||
|
}
|
||
|
// Reload user data in order to have updated info about geocoding quota etc.
|
||
|
this.user.fetch();
|
||
|
}, this);
|
||
|
|
||
|
this.backgroundPollingModel.bind('geocodingFailed', function() {
|
||
|
// Refresh data in order to have
|
||
|
// cartodb_georef_status column updated
|
||
|
if (this.table && this.table.data) {
|
||
|
this.table.data().refresh()
|
||
|
}
|
||
|
}, this);
|
||
|
|
||
|
this.backgroundPollingModel.bind('importCompleted', function() {
|
||
|
// Reload user data in order to have updated info about limits etc.
|
||
|
this.user.fetch();
|
||
|
}, this);
|
||
|
|
||
|
// Background polling view!
|
||
|
var bgPollingView = new cdb.editor.BackgroundPollingView({
|
||
|
model: this.backgroundPollingModel,
|
||
|
createVis: false,
|
||
|
vis: this.vis,
|
||
|
user: this.user
|
||
|
});
|
||
|
|
||
|
this.$el.append(bgPollingView.render().el);
|
||
|
this.addView(bgPollingView);
|
||
|
|
||
|
},
|
||
|
|
||
|
_initTableMap: function() {
|
||
|
var self = this;
|
||
|
|
||
|
|
||
|
// Init geocoder
|
||
|
// TODO: remove when new_modals is enabled for everybody
|
||
|
this.geocoder = new cdb.admin.Geocoding();
|
||
|
|
||
|
// New visualization header
|
||
|
this.header = new cdb.admin.Header({
|
||
|
el: this.$('header'),
|
||
|
globalError: this.globalError,
|
||
|
model: this.master_vis,
|
||
|
visualization: this.vis,
|
||
|
user: this.user,
|
||
|
config: this.options.config,
|
||
|
geocoder: this.geocoder,
|
||
|
backgroundPollingModel: this.backgroundPollingModel
|
||
|
});
|
||
|
this.addView(this.header);
|
||
|
|
||
|
// Table tab
|
||
|
this.tableTab = new cdb.admin.TableTab({
|
||
|
model: this.table,
|
||
|
user: this.user,
|
||
|
vis: this.vis,
|
||
|
sqlView: this.sqlView,
|
||
|
geocoder: this.geocoder,
|
||
|
backgroundPollingModel: this.backgroundPollingModel,
|
||
|
globalError: this.globalError,
|
||
|
menu: this.menu
|
||
|
});
|
||
|
|
||
|
// Map tab
|
||
|
this.mapTab = new cdb.admin.MapTab({
|
||
|
model: this.map,
|
||
|
authTokens: this.authTokens,
|
||
|
baseLayers: this.baseLayers,
|
||
|
vis: this.vis,
|
||
|
master_vis: this.master_vis,
|
||
|
geocoder: this.geocoder,
|
||
|
backgroundPollingModel: this.backgroundPollingModel,
|
||
|
table: this.table,
|
||
|
user: this.user,
|
||
|
menu: this.menu
|
||
|
});
|
||
|
|
||
|
// Mamufas view
|
||
|
this.mamufasView = new cdb.editor.MamufasImportView({
|
||
|
el: this.$el,
|
||
|
user: this.user
|
||
|
}).render();
|
||
|
|
||
|
if (this.vis.isVisualization()) {
|
||
|
this.mamufasView.enable();
|
||
|
}
|
||
|
|
||
|
this._addVideoPlayer();
|
||
|
|
||
|
this.map.bind('notice', this.globalError.showError, this.globalError);
|
||
|
|
||
|
this.workView.bind('tabEnabled:map', this.mapTab.enableMap, this.mapTab);
|
||
|
this.workView.bind('tabEnabled', this.tabs.activate);
|
||
|
this.mapTab.bind('missingClick', self.menu.hide, self.menu);
|
||
|
|
||
|
this.workView.addTab('table', this.tableTab.render(), { active: false });
|
||
|
this.workView.addTab('map', this.mapTab.render(), { active: false });
|
||
|
this.workView.active(this.workViewActive);
|
||
|
},
|
||
|
|
||
|
_initBinds: function() {
|
||
|
cdb.god.bind('geocodingChosen', this._onGeocodingChosen, this);
|
||
|
cdb.god.bind('dialogOpened', function() {
|
||
|
if (this.vis.isVisualization() && this.mamufasView) {
|
||
|
this.mamufasView.disable();
|
||
|
}
|
||
|
this.backgroundPollingModel && this.backgroundPollingModel.stopPollings();
|
||
|
}, this);
|
||
|
cdb.god.bind('dialogClosed', function() {
|
||
|
if (this.vis.isVisualization() && this.mamufasView) {
|
||
|
this.mamufasView.enable();
|
||
|
}
|
||
|
this.backgroundPollingModel && this.backgroundPollingModel.startPollings();
|
||
|
}, this);
|
||
|
},
|
||
|
|
||
|
_onGeocodingChosen: function(data) {
|
||
|
this._sendGeocodingMetrics(data.type);
|
||
|
|
||
|
var geocodeModel;
|
||
|
if (data.type === 'lonlat') {
|
||
|
geocodeModel = new cdb.editor.LonLatGeocodingModel({
|
||
|
table: this.table,
|
||
|
longitude_column: data.longitude,
|
||
|
latitude_column: data.latitude,
|
||
|
force_all_rows: !!data.force_all_rows
|
||
|
});
|
||
|
} else {
|
||
|
geocodeModel = new cdb.editor.GeocodingModel(_.omit(data, 'type'))
|
||
|
}
|
||
|
|
||
|
this.backgroundPollingModel.addGeocodingItem(geocodeModel);
|
||
|
},
|
||
|
|
||
|
_sendGeocodingMetrics: function(type) {
|
||
|
// Event tracking "Geocoding"
|
||
|
cdb.god.trigger('metrics', 'geocoding', {
|
||
|
email: this.options.user_data.email
|
||
|
});
|
||
|
},
|
||
|
|
||
|
setTable: function(table, sqlView) {
|
||
|
var self = this;
|
||
|
if(this.table) {
|
||
|
this.table.unbind('notice', null, this.globalError);
|
||
|
this.table.unbind('change:permission', null, this);
|
||
|
}
|
||
|
|
||
|
function setPermissions() {
|
||
|
// check permissions to set read only
|
||
|
table.setReadOnly(!table.permission.hasWriteAccess(self.user));
|
||
|
}
|
||
|
table.bind('change:permission', setPermissions, this);
|
||
|
setPermissions();
|
||
|
|
||
|
this.table = table;
|
||
|
this.sqlView = sqlView;
|
||
|
this.table.bind('notice', this.globalError.showError, this.globalError);
|
||
|
this.table.bind('change:isSync', this._setSyncInfo, this);
|
||
|
this._setSyncInfo();
|
||
|
},
|
||
|
|
||
|
_addVideoPlayer: function() {
|
||
|
|
||
|
this.player = new cdb.admin.VideoPlayer();
|
||
|
|
||
|
if (this.player.hasVideoData()) {
|
||
|
this.$el.append(this.player.render().$el);
|
||
|
}
|
||
|
|
||
|
},
|
||
|
|
||
|
// Set necessary info if the table is synced
|
||
|
_setSyncInfo: function() {
|
||
|
if (this.table && this.table.isSync() && !this.vis.isVisualization()) {
|
||
|
this.workView.$el.addClass('synced');
|
||
|
} else {
|
||
|
this.workView.$el.removeClass('synced');
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_setWatchingNotifier: function() {
|
||
|
// Create model
|
||
|
var watchvis_notifier = new cdb.admin.WatchingNotifierModel({}, {
|
||
|
vis: this.vis,
|
||
|
interval: cdb.config.get('watcher_ttl')
|
||
|
});
|
||
|
|
||
|
// And then the view
|
||
|
var watchvis_notifier_view = new cdb.admin.WatchingNotifierView({
|
||
|
model: watchvis_notifier,
|
||
|
user: this.user
|
||
|
});
|
||
|
|
||
|
this.$el.append(watchvis_notifier_view.render().el);
|
||
|
this.addView(watchvis_notifier_view);
|
||
|
},
|
||
|
|
||
|
// Close all dialogs in window resize
|
||
|
_onResize: function(e) {
|
||
|
cdb.god.trigger("closeDialogs");
|
||
|
},
|
||
|
|
||
|
keyUp: function(e) {},
|
||
|
|
||
|
keyPress: function(e) {},
|
||
|
|
||
|
// Show big loader when changes to visualization
|
||
|
// or table
|
||
|
showLoader: function(type) {
|
||
|
this.hideLoader();
|
||
|
this._loader = cdb.editor.ViewFactory.createDialogByTemplate('common/templates/loading', {
|
||
|
title: 'Setting ' + type,
|
||
|
quote: cdb.editor.randomQuote()
|
||
|
});
|
||
|
this._loader.appendToBody();
|
||
|
},
|
||
|
|
||
|
// Hide big loader when visualization
|
||
|
// or table finishes
|
||
|
hideLoader: function() {
|
||
|
if (this._loader) {
|
||
|
this._loader.close();
|
||
|
this._loader = null;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
activeView: function(name) {
|
||
|
this.workView.active(name);
|
||
|
// table or map is active?
|
||
|
this.menu.setActiveWorkView(name);
|
||
|
this.workViewActive = name;
|
||
|
}
|
||
|
});
|