cartodb/lib/assets/javascripts/builder/deep-insights-integration/widgets-integration.js
2020-06-15 10:58:47 +08:00

414 lines
14 KiB
JavaScript

var _ = require('underscore');
var checkAndBuildOpts = require('builder/helpers/required-opts');
var WidgetsService = require('builder/editor/widgets/widgets-service');
var WidgetDefinitionModel = require('builder/data/widget-definition-model');
var WidgetsNotifications = require('builder/widgets-notifications');
var getStylesWithoutAutostyles = require('builder/helpers/styles-without-autostyle');
var WIDGET_STYLE_PARAMS = [
'widget_style_definition',
'auto_style_definition',
'auto_style_allowed'
];
var REQUIRED_OPTS = [
'diDashboardHelpers',
'layerDefinitionsCollection',
'widgetDefinitionsCollection',
'analysisDefinitionNodesCollection'
];
/**
* Only manage **WIDGET** actions between Deep-Insights (CARTO.js) and Builder
*
*/
module.exports = {
track: function (options) {
checkAndBuildOpts(options, REQUIRED_OPTS, this);
this._getStylesWithoutAutostyles = getStylesWithoutAutostyles();
this._widgetDefinitionsCollection.on('add', this._onWidgetDefinitionAdded, this);
this._widgetDefinitionsCollection.on('sync', this._onWidgetDefinitionSynced, this);
this._widgetDefinitionsCollection.on('change', this._onWidgetDefinitionChanged, this);
this._widgetDefinitionsCollection.on('destroy', this._onWidgetDefinitionDestroyed, this);
this._widgetDefinitionsCollection.on('add remove reset', this._invalidateSize, this);
this._widgetDefinitionsCollection.on('setSelected', this._setSelectedWidget, this);
this._widgetDefinitionsCollection.each(this._onWidgetDefinitionAdded, this);
WidgetsNotifications.track(this._widgetDefinitionsCollection);
return this;
},
_invalidateSize: function () {
var vis = this._diDashboardHelpers.getMap();
vis.invalidateSize();
},
_onWidgetDefinitionAdded: function (model) {
var widgetModel = this._diDashboardHelpers.getWidget(model.id);
if (widgetModel) {
var layerDefModel = this._layerDefinitionsCollection.findWhere({ id: model.get('layer_id') });
widgetModel.set({
show_source: true,
show_stats: true,
show_options: true,
table_name: layerDefModel.get('table_name')
});
this._bindWidgetChanges(widgetModel);
}
},
_onWidgetDefinitionSynced: function (model) {
var widgetModel = this._diDashboardHelpers.getWidget(model.id);
if (!widgetModel) {
this._createWidgetModel(model);
}
},
_onWidgetAutoStyleColorChanged: function (model) {
var isAutoStyleApplied = model.isAutoStyle();
var autoStyleInfo = model.getAutoStyle();
var layerId = model.layerModel.id;
var layerDefModel = this._layerDefinitionsCollection.findWhere({ id: layerId });
var nodeDefModel = layerDefModel && layerDefModel.getAnalysisDefinitionNodeModel();
var styleModel = layerDefModel && layerDefModel.styleModel;
var geometryType = nodeDefModel && nodeDefModel.get('simple_geom');
if (layerDefModel) {
layerDefModel.set({
autoStyle: isAutoStyleApplied ? model.id : false,
cartocss: autoStyleInfo.cartocss
});
}
if (isAutoStyleApplied && styleModel && geometryType) {
styleModel.setPropertiesFromAutoStyle({
definition: autoStyleInfo.definition,
geometryType: geometryType,
widgetId: model.id
});
}
},
_onWidgetAutoStyleChanged: function (model) {
var isAutoStyleApplied = model.isAutoStyle();
var autoStyleInfo = model.getAutoStyle();
var layerId = model.layerModel.id;
var layerDefModel = this._layerDefinitionsCollection.findWhere({ id: layerId });
var nodeDefModel = layerDefModel && layerDefModel.getAnalysisDefinitionNodeModel();
var styleModel = layerDefModel && layerDefModel.styleModel;
var onLayerChange = _.debounce(function () {
var dontResetStyles = true; // In order to make it more visible
model.cancelAutoStyle(dontResetStyles);
}, 10);
var stylesWithoutAutostyles = this._getStylesWithoutAutostyles(layerDefModel);
if (layerDefModel && nodeDefModel) {
if (isAutoStyleApplied) {
layerDefModel.set({
autoStyle: model.id
});
}
} else {
return;
}
if (isAutoStyleApplied) {
var geometryType = nodeDefModel.get('simple_geom');
styleModel.setPropertiesFromAutoStyle({
definition: autoStyleInfo.definition,
geometryType: geometryType,
widgetId: model.id
});
layerDefModel.set(_.extend(
{
cartocss: autoStyleInfo.cartocss,
cartocss_custom: false
},
stylesWithoutAutostyles
));
layerDefModel.once('change:autoStyle change:cartocss', onLayerChange, this);
} else {
layerDefModel.unbind('change:autoStyle change:cartocss', onLayerChange, this);
var autoStyleId = layerDefModel.get('autoStyle');
if (autoStyleId && autoStyleId === model.id) {
styleModel.resetPropertiesFromAutoStyle();
layerDefModel.set({
autoStyle: false,
cartocss_custom: layerDefModel.get('previousCartoCSSCustom'),
cartocss: layerDefModel.get('previousCartoCSS')
});
// Because we are messing with the autoStyle property on saving,
// whenever we disable autoStyle, we save the layer to force
// the sync on the cartocss
layerDefModel.save();
}
}
},
_onWidgetCustomAutoStyleColorChanged: function (model) {
var isAutoStyleApplied = model.isAutoStyle();
var autoStyleInfo = model.getAutoStyle();
var layerId = model.layerModel.id;
var layerDefModel = this._layerDefinitionsCollection.findWhere({ id: layerId });
var nodeDefModel = layerDefModel && layerDefModel.getAnalysisDefinitionNodeModel();
var styleModel = layerDefModel && layerDefModel.styleModel;
if (isAutoStyleApplied) {
var geometryType = nodeDefModel.get('simple_geom');
styleModel.setPropertiesFromAutoStyle({
definition: autoStyleInfo.definition,
geometryType: geometryType,
widgetId: model.id
});
layerDefModel.set({
cartocss: autoStyleInfo.cartocss,
cartocss_custom: false
}, {silent: true});
// In order to make legends aware
styleModel.trigger('style:update');
}
},
_onWidgetDefinitionChanged: function (model) {
var widgetModel = this._diDashboardHelpers.getWidget(model.id);
// Only try to update if there's a corresponding widget model
// E.g. the change of type will remove the model and provoke change events, which are not of interest (here),
// since the widget model should be re-created for the new type anyway.
if (widgetModel) {
if (model.hasChanged('type')) {
widgetModel.remove();
this._createWidgetModel(model);
} else {
var attrs = this._formatWidgetAttrs(model.changedAttributes(), model);
widgetModel.update(attrs);
}
}
var colorChanged = !model.get('widget_color_changed') &&
model.changedAttributes() &&
model.changedAttributes().widget_style_definition &&
model.changedAttributes().widget_style_definition.color;
if (colorChanged) {
model.set({ widget_color_changed: true });
}
},
_onWidgetDefinitionDestroyed: function (model) {
var widgetModel = this._diDashboardHelpers.getWidget(model.id);
if (widgetModel) {
if (widgetModel.isAutoStyle()) {
widgetModel.cancelAutoStyle();
}
this._unbindWidgetChanges(widgetModel);
widgetModel.remove();
}
},
_setSelectedWidget: function (selectedWidgetId) {
var collection = this._diDashboardHelpers.getWidgets();
collection.forEach(function (widget) {
widget.trigger('setDisabled', widget, selectedWidgetId);
});
},
_onEditWidget: function (model) {
var widgetDefinitionModel = this._widgetDefinitionsCollection.get(model.id);
if (widgetDefinitionModel) {
WidgetsService.editWidget(widgetDefinitionModel);
}
},
_onRemoveWidget: function (model) {
var widgetDefinitionModel = this._widgetDefinitionsCollection.get(model.id);
if (widgetDefinitionModel) {
WidgetsService.removeWidget(widgetDefinitionModel);
}
},
_bindWidgetChanges: function (model) {
model.bind('editWidget', this._onEditWidget, this);
model.bind('removeWidget', this._onRemoveWidget, this);
model.bind('customAutoStyle', this._onWidgetCustomAutoStyleColorChanged, this);
model.bind('change:autoStyle', this._onWidgetAutoStyleChanged, this);
model.bind('change:color', this._onWidgetAutoStyleColorChanged, this);
},
_unbindWidgetChanges: function (model) {
model.unbind('editWidget', this._onEditWidget, this);
model.unbind('removeWidget', this._onRemoveWidget, this);
model.unbind('customAutoStyle', this._onWidgetCustomAutoStyleColorChanged, this);
model.unbind('change:autoStyle', this._onWidgetAutoStyleChanged, this);
model.unbind('change:color', this._onWidgetAutoStyleColorChanged, this);
},
_createWidgetModel: function (model) {
// e.g. 'time-series' => createTimeSeriesWidget
var infix = model.get('type').replace(/(^\w|-\w)/g, function (match) {
return match.toUpperCase().replace('-', '');
});
var methodName = 'create' + infix + 'Widget';
var layerId = model.get('layer_id');
var layerModel = this._diDashboardHelpers.getLayer(layerId);
var layerDefModel = this._layerDefinitionsCollection.findWhere({ id: layerId });
var attrs = this._formatWidgetAttrs(model.attributes, model);
var widgetModel = this._diDashboardHelpers.getDashboard()[methodName](attrs, layerModel);
if (widgetModel) {
widgetModel.set({
show_source: true,
show_stats: true,
show_options: true,
table_name: layerDefModel.get('table_name')
});
this._bindWidgetChanges(widgetModel);
}
},
/**
* Massage some data points to the expected format of deep-insights API
*/
_formatWidgetAttrs: function (changedAttrs, widgetDefinitionModel) {
var formattedAttrs = changedAttrs;
// Source formatting
if (_.isString(formattedAttrs.source)) {
formattedAttrs = _.omit(formattedAttrs, 'source');
formattedAttrs.source = this._diDashboardHelpers.getAnalysisByNodeId(changedAttrs.source);
}
// Widget style or auto style changes
var thereIsWidgetStyleChange = _.find(formattedAttrs, function (value, key) {
return _.contains(WIDGET_STYLE_PARAMS, key);
});
if (!_.isUndefined(thereIsWidgetStyleChange)) {
formattedAttrs = _.omit(formattedAttrs, WIDGET_STYLE_PARAMS);
formattedAttrs.style = widgetDefinitionModel.toJSON().style;
}
return formattedAttrs;
},
manageTimeSeriesForTorque: function (model) {
function recreateWidget (currentTimeseries, newLayer, animated) {
var persistName = currentTimeseries && currentTimeseries.get('title');
// This prevents a bug if user has a range selected and switches column
newLayer.unset('customDuration');
this._createTimeseries(newLayer, animated, persistName);
}
// not a cartodb layer
if (!model.styleModel) return;
var animatedChanged = model.styleModel.changedAttributes().animated;
var previousAnimated = model.styleModel.previous('animated');
var attributeChanged;
if (animatedChanged && previousAnimated && animatedChanged.attribute !== previousAnimated.attribute) attributeChanged = animatedChanged.attribute;
var typeChanged = model.styleModel.changedAttributes().type;
var animatedAttribute = model.styleModel.get('animated') && model.styleModel.get('animated').attribute;
var previousType = model.styleModel.previous('type');
if (!typeChanged && !attributeChanged) return;
var type = model.styleModel.get('type');
var widgetModel = this._diDashboardHelpers.getWidgets().filter(function (model) {
return model.get('type') === 'time-series';
})[0];
var currentTimeseries = this._getTimeseriesDefinition();
var newLayer = this._diDashboardHelpers.getLayer(model.id);
if (type !== 'animation' && previousType === 'animation' && this._lastType !== type) {
if (widgetModel) {
this._removeTimeseries();
}
this._lastType = type;
this._lastTSAnimateChange = '';
}
if (type === 'animation' && (this._lastTSAnimateChange !== attributeChanged || this._lastType !== 'animation')) {
if (widgetModel) {
this._removeTimeseries();
}
if (newLayer.get('type') === 'torque' || model.get('type') === 'torque') {
recreateWidget.call(this, currentTimeseries, newLayer, _.extend({ animated: true }, animatedChanged, { attribute: animatedAttribute }));
}
this._lastType = type;
this._lastTSAnimateChange = attributeChanged;
}
},
_removeTimeseries: function () {
this._widgetDefinitionsCollection.models.forEach(function (def) {
if (def.get('type') === 'time-series') {
def.set({avoidNotification: true}, {silent: true});
def.destroy();
}
});
},
_getTimeseriesDefinition: function () {
return this._widgetDefinitionsCollection.findWhere({type: 'time-series'});
},
_createTimeseries: function (newLayer, animatedChanged, persist) {
function getColumnType (sourceId, columnName) {
var node = this._analysisDefinitionNodesCollection.get(sourceId);
return node && node.querySchemaModel.getColumnType(columnName);
}
this._removeTimeseries();
var attribute = animatedChanged && animatedChanged.attribute || '';
var animated = animatedChanged && animatedChanged.animated;
var sourceId = newLayer.get('source');
var source = this._diDashboardHelpers.getAnalysisByNodeId(sourceId);
if (attribute) {
var baseAttrs = {
type: 'time-series',
layer_id: newLayer.get('id'),
source: source,
options: {
column: attribute,
title: persist || 'time_date__t',
animated: animated
},
style: {
widget_style: WidgetDefinitionModel.getDefaultWidgetStyle('time-series')
}
};
var columnType = getColumnType.call(this, sourceId, attribute);
if (columnType !== 'date') {
baseAttrs.options.bins = 256;
}
this._widgetDefinitionsCollection.create(baseAttrs, { wait: true });
}
}
};