cartodb/lib/assets/javascripts/builder/editor/style/style-content-view.js
2020-06-15 10:58:47 +08:00

305 lines
9.3 KiB
JavaScript
Executable File

var _ = require('underscore');
var Backbone = require('backbone');
var CoreView = require('backbone/core-view');
var StyleFormView = require('./style-form/style-form-view');
var StylesFactory = require('./styles-factory');
var CarouselFormView = require('builder/components/carousel-form-view');
var CarouselCollection = require('builder/components/custom-carousel/custom-carousel-collection');
var Notifier = require('builder/components/notifier/notifier');
var MetricsTracker = require('builder/components/metrics/metrics-tracker');
var MetricsTypes = require('builder/components/metrics/metrics-types');
var StyleConstants = require('builder/components/form-components/_constants/_style');
var styleSQLErrorTemplate = require('./style-content-sql-error.tpl');
var actionErrorTemplate = require('builder/editor/layers/sql-error-action.tpl');
var layerTabMessageTemplate = require('builder/editor/layers/layer-tab-message.tpl');
var checkAndBuildOpts = require('builder/helpers/required-opts');
var EXTRA_STYLE_PROPERTIES = [
'fillSize',
'fillColor',
'strokeSize',
'strokeColor'
];
var EXTRA_LABELS_STYLE_PROPERTIES = [
'fillSize',
'fillColor',
'haloSize',
'haloColor'
];
var REQUIRED_OPTS = [
'configModel',
'layerDefinitionsCollection',
'userModel',
'layerDefinitionModel',
'userActions',
'styleModel',
'overlayModel',
'queryGeometryModel',
'querySchemaModel',
'freezeTorgeAggregation',
'editorModel',
'modals',
'layerContentModel'
];
module.exports = CoreView.extend({
module: 'editor/style/style-content-view',
className: 'Editor-styleContent',
initialize: function (opts) {
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
this._initViewState();
this._buildAggregationCarouselCollection();
this._initBinds();
},
render: function () {
this.clearSubViews();
this.$el.empty();
if (this._isErrored()) {
this._renderError();
} else if (this._viewState.get('isDataFiltered')) {
this._renderFilteredData();
} else {
this._initViews();
}
this._toggleOverlay();
return this;
},
_initViewState: function () {
this._viewState = new Backbone.Model({
isDataFiltered: false
});
this._setViewValues();
},
_buildAggregationCarouselCollection: function () {
if (this._carouselCollection) {
this.stopListening(this._carouselCollection, 'change:selected');
}
this._carouselCollection = new CarouselCollection(
_.map(StylesFactory.getStyleTypes(this._styleModel.get('type'), this._queryGeometryModel.get('simple_geom')), function (type) {
return {
selected: this._styleModel.get('type') === type.value,
val: type.value,
label: type.label,
template: function () {
return (type.iconTemplate && type.iconTemplate()) || type.value;
},
tooltip: type.tooltip
};
}, this)
);
this.listenTo(this._carouselCollection, 'change:selected', this._onSelectAggregation);
return this._carouselCollection;
},
_initBinds: function () {
this.listenTo(this._layerDefinitionModel, 'change:autoStyle', this._onAutoStyleChanged);
this.listenTo(this._layerDefinitionModel, 'change', this._onStyleChanged);
this.listenTo(this._styleModel, 'change:fill', this._onStyleChanged);
this.listenTo(this._styleModel, 'change:labels', this._onStyleLabelChanged);
this.listenTo(this._styleModel, 'undo redo', this.render);
this.listenTo(this._layerContentModel, 'change:state', this._setViewValues);
this.listenTo(this._layerContentModel, 'change:state', this._checkEditorModel);
this.listenTo(this._overlayModel, 'change:visible', this._toggleOverlay);
this.listenTo(this._viewState, 'change:isDataFiltered', this.render);
},
_initViews: function () {
if (this._queryGeometryModel.get('simple_geom') === 'point') {
this._renderCarousel();
}
this._renderForm();
},
_toggleOverlay: function () {
var isDisabled = this._overlayModel.get('visible');
this.$el.toggleClass('is-disabled', isDisabled);
},
_isErrored: function () {
return this._layerContentModel.isErrored();
},
_onAutoStyleChanged: function (layerDefModel) {
var isAutoStyleApplied = layerDefModel.get('autoStyle');
if (!isAutoStyleApplied) {
this.render();
} else {
this._styleModel.unbind('change', this._onStyleChanged, this);
this._styleModel.once('change', function () {
this.render();
this._styleModel.bind('change', this._onStyleChanged, this);
}, this);
}
},
_checkEditorModel: function () {
this._queryGeometryModel.hasValueAsync()
.then(function (hasGeometry) {
this._editorModel.set({ disabled: this._isErrored() || !hasGeometry });
}.bind(this));
},
_renderError: function () {
this.$el.append(
styleSQLErrorTemplate({
body: _t('editor.error-query.body', {
action: actionErrorTemplate({
label: _t('editor.error-query.label')
})
})
})
);
},
_renderFilteredData: function () {
this.$el.append(
layerTabMessageTemplate({
message: _t('editor.layers.warnings.no-data.message'),
action: _t('editor.layers.warnings.no-data.action-message')
})
);
},
_renderCarousel: function () {
var view = new CarouselFormView({
collection: this._buildAggregationCarouselCollection(),
template: require('./style-form-types.tpl')
});
this.addView(view);
this.$el.append(view.render().el);
},
_onSelectAggregation: function (model) {
var alreadyTorqueLayer = this._layerDefinitionsCollection.isThereAnyTorqueLayer();
var isTorqueLayer = this._layerDefinitionModel.get('type') === 'torque';
var styleType;
var isTorqueType;
var currentGeometryType;
if (model.get('selected')) {
styleType = model.getValue();
currentGeometryType = this._queryGeometryModel.get('simple_geom');
isTorqueType = (styleType === StyleConstants.Type.HEATMAP || styleType === StyleConstants.Type.ANIMATION);
if (alreadyTorqueLayer && !isTorqueLayer && isTorqueType) {
this._freezeTorgeAggregation(styleType, currentGeometryType);
} else {
// If an animated or a heatmap style is selected, we should move the layer to the
// top, but if it is in the most high position, we shouldn't do anything
if (isTorqueType && !this._layerDefinitionsCollection.isDataLayerOnTop(this._layerDefinitionModel)) {
this._moveTorqueLayerToTop(function () {
this._setDefaultProperties(styleType, currentGeometryType);
}.bind(this));
} else {
this._setDefaultProperties(styleType, currentGeometryType, false /* silently */);
}
}
}
},
_setDefaultProperties: function (styleType, currentGeometryType, silently) {
var previousType = this._styleModel.get('type');
this._styleModel.setDefaultPropertiesByType(styleType, currentGeometryType, silently);
MetricsTracker.track(MetricsTypes.AGGREGATED_GEOMETRIES, {
previous_agg_type: previousType,
agg_type: styleType
});
},
_moveTorqueLayerToTop: function (callback) {
var notification = Notifier.addNotification({
status: 'loading',
info: _t('editor.layers.moveTorqueLayer.loading'),
closable: true
});
this._layerDefinitionsCollection.once('layerMoved', function () {
if (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()
});
},
_renderForm: function () {
if (this._querySchemaModel.get('query')) {
var view = new StyleFormView({
layerDefinitionsCollection: this._layerDefinitionsCollection,
layerDefinitionModel: this._layerDefinitionModel,
styleModel: this._styleModel,
configModel: this._configModel,
userModel: this._userModel,
queryGeometryModel: this._queryGeometryModel,
querySchemaModel: this._querySchemaModel,
modals: this._modals
});
this.$el.append(view.render().el);
this.addView(view);
}
},
_onStyleChanged: function () {
this._styleModel.set('autogenerated', false);
this._unsetExtraStyleProperties();
this._layerDefinitionModel.save();
MetricsTracker.track(MetricsTypes.MODIFIED_STYLE_FORM, {
layer_id: this._layerDefinitionModel.get('id'),
cartocss: this._layerDefinitionModel.get('cartocss'),
style_properties: this._layerDefinitionModel.get('style_properties')
});
},
_unsetExtraStyleProperties: function () {
EXTRA_STYLE_PROPERTIES.forEach(function (property) {
this._styleModel.unset(property, { silent: true });
}.bind(this));
},
_onStyleLabelChanged: function () {
var labels = this._styleModel.get('labels');
EXTRA_LABELS_STYLE_PROPERTIES.forEach(function (property) {
delete labels[property];
});
this._styleModel.set('labels', labels, { silent: true });
},
_setViewValues: function () {
this._layerDefinitionModel.isDataFiltered()
.then(function (isDataFiltered) {
this._viewState.set('isDataFiltered', isDataFiltered);
}.bind(this));
}
});