305 lines
9.3 KiB
JavaScript
305 lines
9.3 KiB
JavaScript
|
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));
|
||
|
}
|
||
|
});
|