475 lines
16 KiB
JavaScript
475 lines
16 KiB
JavaScript
|
var _ = require('underscore');
|
||
|
var Backbone = require('backbone');
|
||
|
var CoreView = require('backbone/core-view');
|
||
|
var PanelWithOptionsView = require('builder/components/view-options/panel-with-options-view');
|
||
|
var StyleContentView = require('./style-content-view');
|
||
|
var StyleCartoCSSView = require('./style-cartocss-view');
|
||
|
var ScrollView = require('builder/components/scroll/scroll-view');
|
||
|
var TabPaneView = require('builder/components/tab-pane/tab-pane-view');
|
||
|
var TabPaneCollection = require('builder/components/tab-pane/tab-pane-collection');
|
||
|
var Toggler = require('builder/components/toggler/toggler-view');
|
||
|
var UndoButtons = require('builder/components/undo-redo/undo-redo-view');
|
||
|
var ParserCSS = require('builder/helpers/parser-css');
|
||
|
var Infobox = require('builder/components/infobox/infobox-factory');
|
||
|
var InfoboxModel = require('builder/components/infobox/infobox-model');
|
||
|
var InfoboxCollection = require('builder/components/infobox/infobox-collection');
|
||
|
var Notifier = require('builder/components/notifier/notifier');
|
||
|
var CartoCSSNotifications = require('builder/cartocss-notifications');
|
||
|
var MetricsTracker = require('builder/components/metrics/metrics-tracker');
|
||
|
var MetricsTypes = require('builder/components/metrics/metrics-types');
|
||
|
|
||
|
var OnboardingLauncher = require('builder/components/onboardings/generic/generic-onboarding-launcher');
|
||
|
var OnboardingView = require('builder/components/onboardings/layers/style-onboarding/style-onboarding-view');
|
||
|
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||
|
|
||
|
var ONBOARDING_KEY = 'layer-style-onboarding';
|
||
|
|
||
|
var REQUIRED_OPTS = [
|
||
|
'layerDefinitionsCollection',
|
||
|
'layerDefinitionModel',
|
||
|
'userActions',
|
||
|
'queryGeometryModel',
|
||
|
'querySchemaModel',
|
||
|
'queryRowsCollection',
|
||
|
'modals',
|
||
|
'editorModel',
|
||
|
'configModel',
|
||
|
'userModel',
|
||
|
'onboardings',
|
||
|
'onboardingNotification',
|
||
|
'layerContentModel'
|
||
|
];
|
||
|
|
||
|
module.exports = CoreView.extend({
|
||
|
|
||
|
initialize: function (opts) {
|
||
|
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||
|
|
||
|
this._styleModel = this._layerDefinitionModel.styleModel;
|
||
|
this._cartocssModel = this._layerDefinitionModel.cartocssModel;
|
||
|
this._codemirrorModel = new Backbone.Model({
|
||
|
content: this._layerDefinitionModel.get('cartocss')
|
||
|
});
|
||
|
|
||
|
// Set edition attribute in case custom cartocss is applied
|
||
|
this._editorModel.set({
|
||
|
edition: !!this._layerDefinitionModel.get('cartocss_custom'),
|
||
|
disabled: false
|
||
|
});
|
||
|
|
||
|
this._infoboxModel = new InfoboxModel({
|
||
|
state: this._isLayerHidden() ? 'layer-hidden' : ''
|
||
|
});
|
||
|
|
||
|
this._overlayModel = new Backbone.Model({
|
||
|
visible: this._isLayerHidden()
|
||
|
});
|
||
|
|
||
|
this._applyButtonStatusModel = new Backbone.Model({
|
||
|
loading: false
|
||
|
});
|
||
|
|
||
|
this._togglerModel = new Backbone.Model({
|
||
|
labels: [_t('editor.style.style-toggle.values'), _t('editor.style.style-toggle.cartocss')],
|
||
|
active: this._editorModel.isEditing(),
|
||
|
disabled: this._editorModel.isDisabled(),
|
||
|
isDisableable: true,
|
||
|
tooltip: _t('editor.style.style-toggle.tooltip')
|
||
|
});
|
||
|
|
||
|
this._appendMapsAPIError();
|
||
|
|
||
|
CartoCSSNotifications.track(this);
|
||
|
|
||
|
this._onboardingLauncher = new OnboardingLauncher({
|
||
|
view: OnboardingView,
|
||
|
onboardingNotification: this._onboardingNotification,
|
||
|
notificationKey: ONBOARDING_KEY,
|
||
|
onboardings: this._onboardings
|
||
|
}, {
|
||
|
editorModel: this._editorModel,
|
||
|
selector: 'LayerOnboarding'
|
||
|
});
|
||
|
|
||
|
this._configPanes();
|
||
|
this._initBinds();
|
||
|
},
|
||
|
|
||
|
render: function () {
|
||
|
this._launchOnboarding();
|
||
|
this.clearSubViews();
|
||
|
this.$el.empty();
|
||
|
|
||
|
this._initViews();
|
||
|
this._infoboxState();
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
_launchOnboarding: function () {
|
||
|
if (this._onboardingNotification.getKey(ONBOARDING_KEY)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var georeferencePromise = this._layerDefinitionModel.canBeGeoreferenced();
|
||
|
var hasGeomPromise = this._queryGeometryModel.hasValueAsync();
|
||
|
var launchOnboarding = function (canBeGeoreferenced, hasGeom) {
|
||
|
if (!this._editorModel.isEditing() && !canBeGeoreferenced && hasGeom) {
|
||
|
this._onboardingLauncher.launch({
|
||
|
geom: this._queryGeometryModel.get('simple_geom'),
|
||
|
type: this._styleModel.get('type')
|
||
|
});
|
||
|
}
|
||
|
}.bind(this);
|
||
|
|
||
|
Promise.all([georeferencePromise, hasGeomPromise])
|
||
|
.then(function (values) {
|
||
|
launchOnboarding(values[0], values[1]);
|
||
|
});
|
||
|
},
|
||
|
|
||
|
_initBinds: function () {
|
||
|
this.listenTo(this._layerDefinitionModel, 'change:error', this._appendMapsAPIError);
|
||
|
this.listenTo(this._layerDefinitionModel, 'change:cartocss', this._onCartocssChanged);
|
||
|
this.listenTo(this._layerDefinitionModel, 'change:visible', this._infoboxState);
|
||
|
this.listenTo(this._layerDefinitionModel, 'change:autoStyle', this._infoboxState);
|
||
|
|
||
|
this.listenTo(this._editorModel, 'change:edition', this._onChangeEdition);
|
||
|
this.listenTo(this._editorModel, 'change:disabled', this._onChangeDisabled);
|
||
|
this.listenTo(this._togglerModel, 'change:active', this._onTogglerChanged);
|
||
|
|
||
|
this.listenTo(this._querySchemaModel, 'change:query_errors', this._updateEditor);
|
||
|
|
||
|
this.listenTo(this._styleModel, 'change', this._onCartocssChanged);
|
||
|
this.listenTo(this._cartocssModel, 'undo redo', this._onUndoRedo);
|
||
|
},
|
||
|
|
||
|
_initViews: function () {
|
||
|
var self = this;
|
||
|
|
||
|
var infoboxSstates = [
|
||
|
{
|
||
|
state: 'confirm',
|
||
|
createContentView: function () {
|
||
|
return Infobox.createWithAction({
|
||
|
type: 'alert',
|
||
|
title: _t('editor.style.messages.cartocss-applied.title'),
|
||
|
body: _t('editor.style.messages.cartocss-applied.body'),
|
||
|
action: {
|
||
|
label: _t('editor.style.messages.cartocss-applied.clear')
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
onAction: self._clearCustomStyles.bind(self),
|
||
|
onClose: self._cancelClearStyles.bind(self)
|
||
|
}, {
|
||
|
state: 'layer-hidden',
|
||
|
createContentView: function () {
|
||
|
return Infobox.createWithAction({
|
||
|
type: self._layerDefinitionModel.get('cartocss_custom') ? 'code' : 'alert',
|
||
|
title: _t('editor.messages.layer-hidden.title'),
|
||
|
body: _t('editor.messages.layer-hidden.body'),
|
||
|
action: {
|
||
|
label: _t('editor.messages.layer-hidden.show')
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
onAction: self._showHiddenLayer.bind(self)
|
||
|
}, {
|
||
|
state: 'torque-exists',
|
||
|
createContentView: function () {
|
||
|
return Infobox.createWithAction({
|
||
|
type: 'alert',
|
||
|
title: _t('editor.style.messages.torque-exists.title'),
|
||
|
body: _t('editor.style.messages.torque-exists.body'),
|
||
|
action: {
|
||
|
label: _t('editor.style.messages.torque-exists.continue')
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
onAction: self._applyTorqueAggregation.bind(self),
|
||
|
onClose: self._cancelTorqueAggregation.bind(self)
|
||
|
}
|
||
|
];
|
||
|
|
||
|
var infoboxCollection = new InfoboxCollection(infoboxSstates);
|
||
|
|
||
|
var panelWithOptionsView = new PanelWithOptionsView({
|
||
|
className: 'Editor-content',
|
||
|
editorModel: self._editorModel,
|
||
|
infoboxModel: self._infoboxModel,
|
||
|
infoboxCollection: infoboxCollection,
|
||
|
createContentView: function () {
|
||
|
return new TabPaneView({
|
||
|
collection: self._collectionPane
|
||
|
});
|
||
|
},
|
||
|
createControlView: function () {
|
||
|
return new Toggler({
|
||
|
model: self._togglerModel
|
||
|
});
|
||
|
},
|
||
|
createActionView: function () {
|
||
|
return new TabPaneView({
|
||
|
collection: self._collectionPane,
|
||
|
createContentKey: 'createActionView'
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
|
||
|
this.$el.append(panelWithOptionsView.render().el);
|
||
|
this.addView(panelWithOptionsView);
|
||
|
},
|
||
|
|
||
|
_onCartocssChanged: function () {
|
||
|
this._codemirrorModel.set('content', this._layerDefinitionModel.get('cartocss'));
|
||
|
},
|
||
|
|
||
|
_onUndoRedo: function () {
|
||
|
this._codemirrorModel.set('content', this._cartocssModel.get('content'));
|
||
|
},
|
||
|
|
||
|
_appendMapsAPIError: function () {
|
||
|
var error = this._layerDefinitionModel.get('error');
|
||
|
if (error) {
|
||
|
if (error.subtype === 'turbo-carto') {
|
||
|
var newErrors = _.clone(this._codemirrorModel.get('errors')) || [];
|
||
|
newErrors.push({ line: error.line, message: error.message });
|
||
|
this._codemirrorModel.set('errors', newErrors);
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_updateEditor: function (model) {
|
||
|
var errors = this._querySchemaModel.get('query_errors');
|
||
|
var hasErrors = errors && errors.length > 0;
|
||
|
this._editorModel.set('disabled', hasErrors);
|
||
|
},
|
||
|
|
||
|
_saveCartoCSS: function (cb) {
|
||
|
var content = this._codemirrorModel.get('content');
|
||
|
var parser = new ParserCSS(content);
|
||
|
var errors = parser.errors();
|
||
|
|
||
|
if (!content) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
this._applyButtonStatusModel.set('loading', true);
|
||
|
this._codemirrorModel.set('errors', parser.parseError(errors));
|
||
|
|
||
|
if (errors.length === 0) {
|
||
|
this._cartocssModel.set('content', content);
|
||
|
|
||
|
// Disable auto-style before saving, in order to not reset styles
|
||
|
this._layerDefinitionModel.set('autoStyle', false);
|
||
|
|
||
|
this._layerDefinitionModel.save({
|
||
|
cartocss_custom: true,
|
||
|
cartocss: content
|
||
|
}, {
|
||
|
complete: cb
|
||
|
});
|
||
|
|
||
|
MetricsTracker.track(MetricsTypes.APPLIED_CARTOCSS, {
|
||
|
layer_id: this._layerDefinitionModel.get('id'),
|
||
|
cartocss: this._layerDefinitionModel.get('cartocss')
|
||
|
});
|
||
|
|
||
|
MetricsTracker.track(MetricsTypes.USED_ADVANCED_MODE, {
|
||
|
mode_type: 'cartocss'
|
||
|
});
|
||
|
} else {
|
||
|
this._editorModel.get('edition') === false && CartoCSSNotifications.showErrorNotification(parser.parseError(errors));
|
||
|
this._applyButtonStatusModel.set('loading', false);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_onSaveComplete: function () {
|
||
|
CartoCSSNotifications.showSuccessNotification();
|
||
|
this._applyButtonStatusModel.set('loading', false);
|
||
|
},
|
||
|
|
||
|
_cancelClearStyles: function () {
|
||
|
this._infoboxModel.set({ state: '' });
|
||
|
this._overlayModel.set({ visible: false });
|
||
|
|
||
|
this._editorModel.set({
|
||
|
edition: true
|
||
|
});
|
||
|
},
|
||
|
|
||
|
_clearCustomStyles: function () {
|
||
|
this._layerDefinitionModel.set('cartocss_custom', false);
|
||
|
this._styleModel.applyLastState();
|
||
|
this._infoboxModel.set({ state: '' });
|
||
|
this._overlayModel.set({ visible: false });
|
||
|
this.render();
|
||
|
},
|
||
|
|
||
|
_cancelTorqueAggregation: function () {
|
||
|
this._styleModel.applyLastState();
|
||
|
this._infoboxModel.set({ state: '' });
|
||
|
this._overlayModel.set({ visible: false });
|
||
|
this.render();
|
||
|
},
|
||
|
|
||
|
_applyTorqueAggregation: function () {
|
||
|
if (this._layerDefinitionsCollection.isThereAnyTorqueLayer()) {
|
||
|
var torqueLayer = this._layerDefinitionsCollection.findWhere({ type: 'torque' });
|
||
|
torqueLayer.styleModel.setDefaultPropertiesByType('simple', 'point');
|
||
|
}
|
||
|
|
||
|
this._infoboxModel.set({ state: 'unfreeze' });
|
||
|
},
|
||
|
|
||
|
_onChangeEdition: function () {
|
||
|
this._infoboxState();
|
||
|
|
||
|
var edition = this._editorModel.get('edition');
|
||
|
var index = edition ? 1 : 0;
|
||
|
this._collectionPane.at(index).set({ selected: true });
|
||
|
this._togglerModel.set({ active: edition });
|
||
|
},
|
||
|
|
||
|
_onChangeDisabled: function () {
|
||
|
var disabled = this._editorModel.get('disabled');
|
||
|
this._togglerModel.set({ disabled: disabled });
|
||
|
},
|
||
|
|
||
|
_onTogglerChanged: function () {
|
||
|
var checked = this._togglerModel.get('active');
|
||
|
this._editorModel.set({ edition: checked });
|
||
|
},
|
||
|
|
||
|
_freezeTorgeAggregation: function (styleType, currentGeometryType) {
|
||
|
this._infoboxModel.set({ state: 'torque-exists' });
|
||
|
this._overlayModel.set({ visible: true });
|
||
|
|
||
|
// Apply the previously selected style
|
||
|
this._infoboxModel.once('change:state', function (mdl, state) {
|
||
|
if (state === 'unfreeze') {
|
||
|
this._moveTorqueLayerToTop(function () {
|
||
|
this._overlayModel.set({ visible: false });
|
||
|
this._styleModel.setDefaultPropertiesByType(styleType, currentGeometryType);
|
||
|
}.bind(this));
|
||
|
this._infoboxModel.set({ state: '' });
|
||
|
}
|
||
|
}, this);
|
||
|
},
|
||
|
|
||
|
_moveTorqueLayerToTop: function (callback) {
|
||
|
var notification = Notifier.addNotification({
|
||
|
status: 'loading',
|
||
|
info: _t('editor.layers.moveTorqueLayer.loading'),
|
||
|
closable: true
|
||
|
});
|
||
|
|
||
|
this._layerDefinitionsCollection.once('layerMoved', function () {
|
||
|
callback && callback();
|
||
|
notification.set({
|
||
|
status: 'success',
|
||
|
info: _t('editor.layers.moveTorqueLayer.success'),
|
||
|
delay: Notifier.DEFAULT_DELAY
|
||
|
});
|
||
|
}, this);
|
||
|
|
||
|
this._userActions.moveLayer({
|
||
|
from: this._layerDefinitionModel.get('order'),
|
||
|
to: this._layerDefinitionsCollection.getTopDataLayerIndex()
|
||
|
});
|
||
|
},
|
||
|
|
||
|
_configPanes: function () {
|
||
|
var self = this;
|
||
|
var tabPaneTabs = [{
|
||
|
selected: !this._layerDefinitionModel.get('cartocss_custom'),
|
||
|
createContentView: function () {
|
||
|
return new ScrollView({
|
||
|
createContentView: function () {
|
||
|
return new StyleContentView({
|
||
|
className: 'Editor-content',
|
||
|
userActions: self._userActions,
|
||
|
layerDefinitionsCollection: self._layerDefinitionsCollection,
|
||
|
layerDefinitionModel: self._layerDefinitionModel,
|
||
|
styleModel: self._styleModel,
|
||
|
modals: self._modals,
|
||
|
configModel: self._configModel,
|
||
|
userModel: self._userModel,
|
||
|
queryGeometryModel: self._queryGeometryModel,
|
||
|
querySchemaModel: self._querySchemaModel,
|
||
|
editorModel: self._editorModel,
|
||
|
overlayModel: self._overlayModel,
|
||
|
freezeTorgeAggregation: self._freezeTorgeAggregation.bind(self),
|
||
|
layerContentModel: self._layerContentModel
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
createActionView: function () {
|
||
|
return new UndoButtons({
|
||
|
trackModel: self._styleModel,
|
||
|
editorModel: self._editorModel,
|
||
|
applyButton: false
|
||
|
});
|
||
|
}
|
||
|
}, {
|
||
|
selected: this._layerDefinitionModel.get('cartocss_custom'),
|
||
|
createContentView: function () {
|
||
|
return new StyleCartoCSSView({
|
||
|
layerDefinitionModel: self._layerDefinitionModel,
|
||
|
querySchemaModel: self._querySchemaModel,
|
||
|
styleModel: self._styleModel,
|
||
|
editorModel: self._editorModel,
|
||
|
codemirrorModel: self._codemirrorModel,
|
||
|
onApplyEvent: self._saveCartoCSS.bind(self, self._onSaveComplete.bind(self)),
|
||
|
overlayModel: self._overlayModel
|
||
|
});
|
||
|
},
|
||
|
createActionView: function () {
|
||
|
return new UndoButtons({
|
||
|
trackModel: self._cartocssModel,
|
||
|
editorModel: self._editorModel,
|
||
|
applyStatusModel: self._applyButtonStatusModel,
|
||
|
applyButton: true,
|
||
|
onApplyClick: self._saveCartoCSS.bind(self, self._onSaveComplete.bind(self)),
|
||
|
overlayModel: self._overlayModel
|
||
|
});
|
||
|
}
|
||
|
}];
|
||
|
|
||
|
this._collectionPane = new TabPaneCollection(tabPaneTabs);
|
||
|
},
|
||
|
|
||
|
_isLayerHidden: function () {
|
||
|
return this._layerDefinitionModel.get('visible') === false;
|
||
|
},
|
||
|
|
||
|
_infoboxState: function () {
|
||
|
var edition = this._editorModel.get('edition');
|
||
|
var cartocss_custom = this._layerDefinitionModel.get('cartocss_custom');
|
||
|
var isAutoStyleApplied = this._layerDefinitionModel.get('autoStyle');
|
||
|
|
||
|
if (!edition && cartocss_custom && !isAutoStyleApplied) {
|
||
|
this._infoboxModel.set({ state: 'confirm' });
|
||
|
this._overlayModel.set({ visible: true });
|
||
|
} else if (this._isLayerHidden()) {
|
||
|
this._infoboxModel.set({ state: 'layer-hidden' });
|
||
|
this._overlayModel.set({ visible: true });
|
||
|
this._togglerModel.set({ disabled: true });
|
||
|
} else {
|
||
|
this._infoboxModel.set({ state: '' });
|
||
|
this._overlayModel.set({ visible: false });
|
||
|
this._togglerModel.set({ disabled: false });
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_showHiddenLayer: function () {
|
||
|
var savingOptions = {
|
||
|
shouldPreserveAutoStyle: true
|
||
|
};
|
||
|
this._layerDefinitionModel.toggleVisible();
|
||
|
this._userActions.saveLayer(this._layerDefinitionModel, savingOptions);
|
||
|
}
|
||
|
});
|