<commit_before>var _ = require('underscore');
var linkLayerInfowindow = require('./deep-insights-integration/link-layer-infowindow');
var linkLayerTooltip = require('./deep-insights-integration/link-layer-tooltip');

/**
 * Integration between various data collections/models with cartodb.js and deep-insights.
 */
var F = function (opts) {
  if (!opts.deepInsightsDashboard) throw new Error('deepInsightsDashboard is required');
  if (!opts.analysisDefinitionNodesCollection) throw new Error('analysisDefinitionNodesCollection is required');
  if (!opts.analysisDefinitionsCollection) throw new Error('analysisDefinitionsCollection is required');
  if (!opts.layerDefinitionsCollection) throw new Error('layerDefinitionsCollection is required');
  if (!opts.widgetDefinitionsCollection) throw new Error('widgetDefinitionsCollection is required');

  this._diDashboard = opts.deepInsightsDashboard;
  this._analysisDefinitionNodesCollection = opts.analysisDefinitionNodesCollection;
  this._analysisDefinitionsCollection = opts.analysisDefinitionsCollection;
  this._layerDefinitionsCollection = opts.layerDefinitionsCollection;

  this._analysisDefinitionNodesCollection.on('add', this._analyseDefinitionNode, this);
  this._analysisDefinitionNodesCollection.on('change', this._analyseDefinitionNode, this);
  this._analysisDefinitionNodesCollection.on('remove', this._onAnalysisDefinitionNodeRemoved, this);
  this._analysisDefinitionsCollection.on('add change:node_id sync', this._analyseDefinition, this);

  opts.layerDefinitionsCollection.on('add', this._onLayerDefinitionAdded, this);
  opts.layerDefinitionsCollection.on('sync', this._onLayerDefinitionSynced, this);
  opts.layerDefinitionsCollection.on('change', this._onLayerDefinitionChanged, this);
  opts.layerDefinitionsCollection.on('remove', this._onLayerDefinitionRemoved, this);
  opts.layerDefinitionsCollection.on('layerMoved', this._onLayerDefinitionMoved, this);
  opts.layerDefinitionsCollection.on('change:cartocss', this._onLayerChangeCheckWidgetAutoStyling, this);

  opts.layerDefinitionsCollection.each(function (layerDefModel) {
    linkLayerInfowindow(layerDefModel, this.visMap());
    linkLayerTooltip(layerDefModel, this.visMap());
  }, this);

  opts.widgetDefinitionsCollection.on('add', this._onWidgetDefinitionAdded, this);
  opts.widgetDefinitionsCollection.on('sync', this._onWidgetDefinitionSynced, this);
  opts.widgetDefinitionsCollection.on('change', this._onWidgetDefinitionChanged, this);
  opts.widgetDefinitionsCollection.on('destroy', this._onWidgetDefinitionDestroyed, this);
  opts.widgetDefinitionsCollection.on('add remove reset', this._invalidateSize, this);

  this.widgetDefinitionsCollection = opts.widgetDefinitionsCollection;

  this._analysisDefinitionsCollection.each(this._analyseDefinition, this);
};

F.prototype._onAnalysisDefinitionNodeRemoved = function (m) {
  var node = this._analysis().findNodeById(m.id);
  if (node) {
    node.remove();
  }
};

F.prototype._analyseDefinition = function (m) {
  var id = m.get('node_id');
  var nodeDefModel = this._analysisDefinitionNodesCollection.get(id);
  this._analyseDefinitionNode(nodeDefModel);
};

F.prototype._analyseDefinitionNode = function (nodeDefModel) {
  var attrs = nodeDefModel.toJSON({skipOptions: true});
  this._analysis().analyse(attrs);

  // Unfortunately have to try to setup sync until this point, since a node doesn't exist until after analyse call
  this._analysisDefinitionNodesCollection.each(this._tryToSetupDefinitionNodeSync, this);
};

F.prototype._tryToSetupDefinitionNodeSync = function (m) {
  if (m.__syncSetup) return; // only setup once

  var node = this._analysis().findNodeById(m.id);
  if (!node) return; // might not exist when method is called, so do nothing to allow retries

  m.__syncSetup = true;

  // Don't need to sync source nodes
  if (node.get('type') !== 'source') {
    var updateAnalysisQuerySchema = function () {
      m.querySchemaModel.set({
        query: node.get('query'),
        may_have_rows: node.get('status') === 'ready'
      });
    };
    updateAnalysisQuerySchema();
    m.listenTo(node, 'change', updateAnalysisQuerySchema);
    m.listenToOnce(node, 'destroy', m.stopListening);
  }
};

F.prototype._onWidgetDefinitionAdded = function (m) {
  var widgetModel = this._diDashboard.getWidget(m.id);
  if (widgetModel) {
    widgetModel.set('show_stats', true);
  }
};

F.prototype._onWidgetDefinitionSynced = function (m) {
  var widgetModel = this._diDashboard.getWidget(m.id);
  if (!widgetModel) {
    this._createWidgetModel(m);
  }
};

F.prototype._onLayerChangeCheckWidgetAutoStyling = function (model, style) {
  var self = this;
  var currentLayerId = model.get('id');
  var layer = this._getLayer(model);
  layer.set('initialStyle', style);
  var widgetsToInvalidate = this.widgetDefinitionsCollection.where({'layer_id': currentLayerId});
  widgetsToInvalidate.forEach(function (widget) {
    var widgetModel = self._diDashboard.getWidget(widget.id);
    if (widgetModel && _.contains(['histogram', 'category'], widgetModel.get('type'))) {
      widgetModel.cancelAutoStyle();
    }
  });
  this._diDashboard._dashboard.vis._layersCollection.where({'id': currentLayerId})[0];
};

F.prototype._onWidgetDefinitionChanged = function (m) {
  var widgetModel = this._diDashboard.getWidget(m.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 (m.hasChanged('type')) {
      widgetModel.remove();
      this._createWidgetModel(m);
    } else {
      var attrs = this._formatWidgetAttrs(m.changedAttributes());
      widgetModel.update(attrs);
    }
  }
};

F.prototype._onWidgetDefinitionDestroyed = function (m) {
  var widgetModel = this._diDashboard.getWidget(m.id);<commit_msg>doesn't do anything to autostyling if there is no layer<commit_after>var _ = require('underscore');
var linkLayerInfowindow = require('./deep-insights-integration/link-layer-infowindow');
var linkLayerTooltip = require('./deep-insights-integration/link-layer-tooltip');

/**
 * Integration between various data collections/models with cartodb.js and deep-insights.
 */
var F = function (opts) {
  if (!opts.deepInsightsDashboard) throw new Error('deepInsightsDashboard is required');
  if (!opts.analysisDefinitionNodesCollection) throw new Error('analysisDefinitionNodesCollection is required');
  if (!opts.analysisDefinitionsCollection) throw new Error('analysisDefinitionsCollection is required');
  if (!opts.layerDefinitionsCollection) throw new Error('layerDefinitionsCollection is required');
  if (!opts.widgetDefinitionsCollection) throw new Error('widgetDefinitionsCollection is required');

  this._diDashboard = opts.deepInsightsDashboard;
  this._analysisDefinitionNodesCollection = opts.analysisDefinitionNodesCollection;
  this._analysisDefinitionsCollection = opts.analysisDefinitionsCollection;
  this._layerDefinitionsCollection = opts.layerDefinitionsCollection;

  this._analysisDefinitionNodesCollection.on('add', this._analyseDefinitionNode, this);
  this._analysisDefinitionNodesCollection.on('change', this._analyseDefinitionNode, this);
  this._analysisDefinitionNodesCollection.on('remove', this._onAnalysisDefinitionNodeRemoved, this);
  this._analysisDefinitionsCollection.on('add change:node_id sync', this._analyseDefinition, this);

  opts.layerDefinitionsCollection.on('add', this._onLayerDefinitionAdded, this);
  opts.layerDefinitionsCollection.on('sync', this._onLayerDefinitionSynced, this);
  opts.layerDefinitionsCollection.on('change', this._onLayerDefinitionChanged, this);
  opts.layerDefinitionsCollection.on('remove', this._onLayerDefinitionRemoved, this);
  opts.layerDefinitionsCollection.on('layerMoved', this._onLayerDefinitionMoved, this);
  opts.layerDefinitionsCollection.on('change:cartocss', this._onLayerChangeCheckWidgetAutoStyling, this);

  opts.layerDefinitionsCollection.each(function (layerDefModel) {
    linkLayerInfowindow(layerDefModel, this.visMap());
    linkLayerTooltip(layerDefModel, this.visMap());
  }, this);

  opts.widgetDefinitionsCollection.on('add', this._onWidgetDefinitionAdded, this);
  opts.widgetDefinitionsCollection.on('sync', this._onWidgetDefinitionSynced, this);
  opts.widgetDefinitionsCollection.on('change', this._onWidgetDefinitionChanged, this);
  opts.widgetDefinitionsCollection.on('destroy', this._onWidgetDefinitionDestroyed, this);
  opts.widgetDefinitionsCollection.on('add remove reset', this._invalidateSize, this);

  this.widgetDefinitionsCollection = opts.widgetDefinitionsCollection;

  this._analysisDefinitionsCollection.each(this._analyseDefinition, this);
};

F.prototype._onAnalysisDefinitionNodeRemoved = function (m) {
  var node = this._analysis().findNodeById(m.id);
  if (node) {
    node.remove();
  }
};

F.prototype._analyseDefinition = function (m) {
  var id = m.get('node_id');
  var nodeDefModel = this._analysisDefinitionNodesCollection.get(id);
  this._analyseDefinitionNode(nodeDefModel);
};

F.prototype._analyseDefinitionNode = function (nodeDefModel) {
  var attrs = nodeDefModel.toJSON({skipOptions: true});
  this._analysis().analyse(attrs);

  // Unfortunately have to try to setup sync until this point, since a node doesn't exist until after analyse call
  this._analysisDefinitionNodesCollection.each(this._tryToSetupDefinitionNodeSync, this);
};

F.prototype._tryToSetupDefinitionNodeSync = function (m) {
  if (m.__syncSetup) return; // only setup once

  var node = this._analysis().findNodeById(m.id);
  if (!node) return; // might not exist when method is called, so do nothing to allow retries

  m.__syncSetup = true;

  // Don't need to sync source nodes
  if (node.get('type') !== 'source') {
    var updateAnalysisQuerySchema = function () {
      m.querySchemaModel.set({
        query: node.get('query'),
        may_have_rows: node.get('status') === 'ready'
      });
    };
    updateAnalysisQuerySchema();
    m.listenTo(node, 'change', updateAnalysisQuerySchema);
    m.listenToOnce(node, 'destroy', m.stopListening);
  }
};

F.prototype._onWidgetDefinitionAdded = function (m) {
  var widgetModel = this._diDashboard.getWidget(m.id);
  if (widgetModel) {
    widgetModel.set('show_stats', true);
  }
};

F.prototype._onWidgetDefinitionSynced = function (m) {
  var widgetModel = this._diDashboard.getWidget(m.id);
  if (!widgetModel) {
    this._createWidgetModel(m);
  }
};

F.prototype._onLayerChangeCheckWidgetAutoStyling = function (model, style) {
  var self = this;
  var currentLayerId = model.get('id');
  var layer = this._getLayer(model);
  if (!layer) return;
  layer.attributes.initialStyle = style;
  var widgetsToInvalidate = this.widgetDefinitionsCollection.where({'layer_id': currentLayerId});
  widgetsToInvalidate.forEach(function (widget) {
    var widgetModel = self._diDashboard.getWidget(widget.id);
    if (widgetModel && _.contains(['histogram', 'category'], widgetModel.get('type'))) {
      widgetModel.cancelAutoStyle();
    }
  });
  this._diDashboard._dashboard.vis._layersCollection.where({'id': currentLayerId})[0];
};

F.prototype._onWidgetDefinitionChanged = function (m) {
  var widgetModel = this._diDashboard.getWidget(m.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 (m.hasChanged('type')) {
      widgetModel.remove();
      this._createWidgetModel(m);
    } else {
      var attrs = this._formatWidgetAttrs(m.changedAttributes());
      widgetModel.update(attrs);
    }
  }
};

F.prototype._onWidgetDefinitionDestroyed = function (m) {
  var widgetModel = this._diDashboard.getWidget(m.id);
{'fraction_non_alphanumeric': 0.11869932291231297, 'fraction_numerical': 0.00016718214494691966, 'mean_word_length': 5.599007170435742, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6959410', 'n_tokens_mistral': 3394, 'n_tokens_neox': 3311, 'n_words': 695}