<commit_before>var $ = require('jquery');
var _ = require('underscore');
var nodeIds = require('../value-objects/analysis-node-ids');
var MetricsTracker = require('../components/metrics/metrics-tracker');
var SimpleStyleDefaults = require('../editor/style/style-defaults/simple-style-defaults');
var camshaftReference = require('./camshaft-reference');
var layerTypesAndKinds = require('./layer-types-and-kinds');
var layerColors = require('./layer-colors');
var Notifier = require('../components/notifier/notifier');
var ResetStylePerNode = require('../helpers/reset-style-per-node');

/**
 * Coordinate side-effects done on explicit interactions.
 * @param {Object} params
 * @param {Object} params.userModel
 * @param {Object} params.analysisDefinitionsCollection
 * @param {Object} params.analysisDefinitionNodesCollection
 * @param {Object} params.widgetDefinitionsCollection
 * @return {Object} that contains all user-actions that the user may do related to a map
 */
module.exports = function (params) {
  if (!params.userModel) throw new Error('userModel is required');
  if (!params.analysisDefinitionsCollection) throw new Error('analysisDefinitionsCollection is required');
  if (!params.analysisDefinitionNodesCollection) throw new Error('analysisDefinitionNodesCollection is required');
  if (!params.layerDefinitionsCollection) throw new Error('layerDefinitionsCollection is required');
  if (!params.widgetDefinitionsCollection) throw new Error('widgetDefinitionsCollection is required');

  var userModel = params.userModel;
  var analysisDefinitionsCollection = params.analysisDefinitionsCollection;
  var analysisDefinitionNodesCollection = params.analysisDefinitionNodesCollection;
  var layerDefinitionsCollection = params.layerDefinitionsCollection;
  var widgetDefinitionsCollection = params.widgetDefinitionsCollection;

  var createDefaultCartoDBAttrs = function (oldLayerStyle) {
    return {
      kind: 'carto',
      options: {
        interactivity: '',
        tile_style: (oldLayerStyle && oldLayerStyle.options.tile_style) || camshaftReference.getDefaultCartoCSSForType(),
        cartocss: (oldLayerStyle && oldLayerStyle.options.tile_style) || camshaftReference.getDefaultCartoCSSForType(),
        style_version: '2.1.1',
        visible: true,
        style_properties: (oldLayerStyle && oldLayerStyle.options.style_properties),
        sql_wrap: (oldLayerStyle && oldLayerStyle.options.sql_wrap)
      },
      tooltip: oldLayerStyle ? oldLayerStyle.tooltip : {},
      infowindow: oldLayerStyle ? oldLayerStyle.infowindow : {}
    };
  };

  var deleteOrphanedAnalyses = function () {
    analysisDefinitionNodesCollection
      .toArray()
      .forEach(function (nodeDefModel) {
        if (!layerDefinitionsCollection.anyContainsNode(nodeDefModel)) {
          nodeDefModel.destroy();
        }
      });

    analysisDefinitionsCollection
      .toArray()
      .forEach(function (analysisDefModel) {
        if (!analysisDefModel.getNodeDefinitionModel()) {
          analysisDefModel.destroy();
        }
      });
  };

  var restoreWidgetsFromLayer = function (affectedWidgets, layerDefModel, callback, callbackOptions) {
    if (!affectedWidgets || !_.isArray(affectedWidgets)) throw new Error('affectedWidgets is required');
    if (!layerDefModel) throw new Error('layerDefModel is required');
    if (!callback) throw new Error('callback is required');

    var notification;
    var restoringError;
    var onWidgetFinished = _.after(affectedWidgets.length, function () {
      if (!restoringError) {
        notification.set({
          status: 'success',
          info: _t('notifications.widgets.restored'),
          closable: true
        });
      } else {
        Notifier.removeNotification(notification);
      }
      callback(layerDefModel, callbackOptions);
    });

    if (affectedWidgets.length > 0) {
      notification = Notifier.addNotification({
        status: 'loading',
        info: _t('notifications.widgets.restoring'),
        closable: false
      });

      // Create widgets with new source and layer_id
      _.each(affectedWidgets, function (attrs) {
        attrs.layer_id = attrs.layer_id || layerDefModel.id;

        widgetDefinitionsCollection.create(
          attrs, {
            wait: true,
            success: onWidgetFinished,
            error: function (e, mdl) {
              notification = Notifier.addNotification({
                status: 'error',
                info: _t('notifications.widgets.error.title') +
                  _t('notifications.widgets.error.body', {
                    body: '',
                    error: mdl && mdl.get('title')
                  }),
                closable: true
              });
              restoringError = true;
              onWidgetFinished();
            }
          }
        );
      });
    } else {
      callback(layerDefModel, callbackOptions);
    }
  };

  return {
    saveAnalysis: function (analysisFormModel) {
      if (!analysisFormModel.isValid()) return;

      var nodeDefModel = analysisDefinitionNodesCollection.get(analysisFormModel.id);

      if (nodeDefModel) {
        analysisFormModel.updateNodeDefinition(nodeDefModel);
        var layerDefModel = layerDefinitionsCollection.findOwnerOfAnalysisNode(nodeDefModel);
        analysisDefinitionsCollection.saveAnalysisForLayer(layerDefModel);

        MetricsTracker.track('Modified analysis', {
          analysis: {
            id: analysisFormModel.attributes['id'],
            natural_id: analysisFormModel.attributes['id'],
            type: analysisFormModel.attributes['type']
          }
        });
      } else {
        nodeDefModel = analysisFormModel.createNodeDefinition(this); // delegate creation to the form, but expect it to call userActions.createAnalysisNode when done with its internal stuff
      }
      nodeDefModel.USER_SAVED = true;
    },

    /**
     * Creates a new analysis node on a particular layer.
     * It's assumed to be created on top of an existing node.
     * @param {Object} nodeAttrs
     * @param {Object} layerDefModel - instance of layer-definition-model
     * @return {Object} instance of analysis-definition-node-model
     */
    createAnalysisNode: function (nodeAttrs, layerDefModel) {
      var nodeDefModel = analysisDefinitionNodesCollection.add(nodeAttrs, {parse: false});

      layerDefModel.save({
        cartocss: camshaftReference.getDefaultCartoCSSForType(),
        source: nodeDefModel.id
      });
      analysisDefinitionsCollection.saveAnalysisForLayer(layerDefModel);

      MetricsTracker.track('Created analysis', {
        analysis: {
          id: nodeDefModel.attributes['id'],
          natural_id: nodeDefModel.attributes['id'],
          type: nodeDefModel.attributes['type']
        }
      });

      ResetStylePerNode(nodeDefModel, layerDefModel);

      return nodeDefModel;
    },

    saveAnalysisSourceQuery: function (query, nodeDefModel, layerDefModel) {
      if (!nodeDefModel) throw new Error('nodeDefModel is required');
      if (nodeDefModel.get('type') !== 'source') throw new Error('nodeDefModel must be a source node');

      nodeDefModel.set('query', query || '');

      analysisDefinitionsCollection.saveAnalysisForLayer(layerDefModel);
      ResetStylePerNode(nodeDefModel, layerDefModel);
      layerDefModel.save(); // to make sure layer's source ref is persisted
    },

    saveWidgetOption: function (widgetOptionModel) {
      if (!widgetOptionModel) throw new Error('widgetOptionModel is required');

      if (widgetOptionModel.analysisDefinitionNodeModel()) { // Might not always have a node-definition, e.g. time-series none-option
        var layerDefModel = widgetOptionModel.layerDefinitionModel();
        analysisDefinitionsCollection.saveAnalysisForLayer(layerDefModel);
        layerDefModel.save(); // to make sure layer's source ref is persisted
      }

      return widgetOptionModel.save(widgetDefinitionsCollection); // delegate back to form model to decide how to save
    },

    saveWidget: function (widgetDefModel) {
      if (!widgetDefModel) throw new Error('widgetDefModel is required');

      var widgetLayerId = widgetDefModel.get('layer_id');
      layerDefinitionsCollection.some(function (layerDefModel) {
        if (layerDefModel.id === widgetLayerId) {
          analysisDefinitionsCollection.saveAnalysisForLayer(layerDefModel);
          layerDefModel.save(); // to make sure layer's source ref is persisted
          return true; // aborts the "some"-iterator
        }
      });

      widgetDefModel.save();
    },

    deleteAnalysisNode: function (nodeId) {
      var nodeDefModel = analysisDefinitionNodesCollection.get(nodeId);
      if (!nodeDefModel) return false; // abort if there is no node-definition; nothing to delete/change
      if (!nodeDefModel.canBeDeletedByUser()) return false;

      var layerDefModel = layerDefinitionsCollection.findOwnerOfAnalysisNode(nodeDefModel);
      var primarySourceNode = nodeDefModel.getPrimarySource();

      var containsNode = function (m) {
        return m !== layerDefModel && m !== nodeDefModel && m.containsNode(nodeDefModel);
      };
      _
        .flatten([
          widgetDefinitionsCollection.filter(containsNode),
          layerDefinitionsCollection.filter(containsNode),
          analysisDefinitionsCollection.filter(containsNode),
          analysisDefinitionNodesCollection.filter(containsNode),
          nodeDefModel
        ], true)
        .forEach(function (m) {
          m.destroy();
        });

      // Try to restore old styles if we have them, reset them if not
      var oldNodeStyle = primarySourceNode.getStyleHistoryForLayer(layerDefModel.id);
      if (oldNodeStyle) {
        layerDefModel.set({
          source: primarySourceNode.id,
          cartocss: oldNodeStyle.options.tile_style,
          infowindow: oldNodeStyle.infowindow,
          tooltip: oldNodeStyle.tooltip,
          sql_wrap: oldNodeStyle.options.sql_wrap
        }, {ignoreSchemaChange: true});
        layerDefModel.styleModel.set(layerDefModel.styleModel.parse(oldNodeStyle.options.style_properties));
      } else {
        layerDefModel.set({source: primarySourceNode.id});
        ResetStylePerNode(primarySourceNode, layerDefModel);
      }
      layerDefModel.save();
      analysisDefinitionsCollection.saveAnalysisForLayer(layerDefModel);

      MetricsTracker.track('Deleted analysis', {
        analysis: {
          id: nodeDefModel.attributes['id'],
          natural_id: nodeDefModel.attributes['id'],<commit_msg>Correct order of resetting so legends are reset<commit_after>var $ = require('jquery');
var _ = require('underscore');
var nodeIds = require('../value-objects/analysis-node-ids');
var MetricsTracker = require('../components/metrics/metrics-tracker');
var SimpleStyleDefaults = require('../editor/style/style-defaults/simple-style-defaults');
var camshaftReference = require('./camshaft-reference');
var layerTypesAndKinds = require('./layer-types-and-kinds');
var layerColors = require('./layer-colors');
var Notifier = require('../components/notifier/notifier');
var ResetStylePerNode = require('../helpers/reset-style-per-node');

/**
 * Coordinate side-effects done on explicit interactions.
 * @param {Object} params
 * @param {Object} params.userModel
 * @param {Object} params.analysisDefinitionsCollection
 * @param {Object} params.analysisDefinitionNodesCollection
 * @param {Object} params.widgetDefinitionsCollection
 * @return {Object} that contains all user-actions that the user may do related to a map
 */
module.exports = function (params) {
  if (!params.userModel) throw new Error('userModel is required');
  if (!params.analysisDefinitionsCollection) throw new Error('analysisDefinitionsCollection is required');
  if (!params.analysisDefinitionNodesCollection) throw new Error('analysisDefinitionNodesCollection is required');
  if (!params.layerDefinitionsCollection) throw new Error('layerDefinitionsCollection is required');
  if (!params.widgetDefinitionsCollection) throw new Error('widgetDefinitionsCollection is required');

  var userModel = params.userModel;
  var analysisDefinitionsCollection = params.analysisDefinitionsCollection;
  var analysisDefinitionNodesCollection = params.analysisDefinitionNodesCollection;
  var layerDefinitionsCollection = params.layerDefinitionsCollection;
  var widgetDefinitionsCollection = params.widgetDefinitionsCollection;

  var createDefaultCartoDBAttrs = function (oldLayerStyle) {
    return {
      kind: 'carto',
      options: {
        interactivity: '',
        tile_style: (oldLayerStyle && oldLayerStyle.options.tile_style) || camshaftReference.getDefaultCartoCSSForType(),
        cartocss: (oldLayerStyle && oldLayerStyle.options.tile_style) || camshaftReference.getDefaultCartoCSSForType(),
        style_version: '2.1.1',
        visible: true,
        style_properties: (oldLayerStyle && oldLayerStyle.options.style_properties),
        sql_wrap: (oldLayerStyle && oldLayerStyle.options.sql_wrap)
      },
      tooltip: oldLayerStyle ? oldLayerStyle.tooltip : {},
      infowindow: oldLayerStyle ? oldLayerStyle.infowindow : {}
    };
  };

  var deleteOrphanedAnalyses = function () {
    analysisDefinitionNodesCollection
      .toArray()
      .forEach(function (nodeDefModel) {
        if (!layerDefinitionsCollection.anyContainsNode(nodeDefModel)) {
          nodeDefModel.destroy();
        }
      });

    analysisDefinitionsCollection
      .toArray()
      .forEach(function (analysisDefModel) {
        if (!analysisDefModel.getNodeDefinitionModel()) {
          analysisDefModel.destroy();
        }
      });
  };

  var restoreWidgetsFromLayer = function (affectedWidgets, layerDefModel, callback, callbackOptions) {
    if (!affectedWidgets || !_.isArray(affectedWidgets)) throw new Error('affectedWidgets is required');
    if (!layerDefModel) throw new Error('layerDefModel is required');
    if (!callback) throw new Error('callback is required');

    var notification;
    var restoringError;
    var onWidgetFinished = _.after(affectedWidgets.length, function () {
      if (!restoringError) {
        notification.set({
          status: 'success',
          info: _t('notifications.widgets.restored'),
          closable: true
        });
      } else {
        Notifier.removeNotification(notification);
      }
      callback(layerDefModel, callbackOptions);
    });

    if (affectedWidgets.length > 0) {
      notification = Notifier.addNotification({
        status: 'loading',
        info: _t('notifications.widgets.restoring'),
        closable: false
      });

      // Create widgets with new source and layer_id
      _.each(affectedWidgets, function (attrs) {
        attrs.layer_id = attrs.layer_id || layerDefModel.id;

        widgetDefinitionsCollection.create(
          attrs, {
            wait: true,
            success: onWidgetFinished,
            error: function (e, mdl) {
              notification = Notifier.addNotification({
                status: 'error',
                info: _t('notifications.widgets.error.title') +
                  _t('notifications.widgets.error.body', {
                    body: '',
                    error: mdl && mdl.get('title')
                  }),
                closable: true
              });
              restoringError = true;
              onWidgetFinished();
            }
          }
        );
      });
    } else {
      callback(layerDefModel, callbackOptions);
    }
  };

  return {
    saveAnalysis: function (analysisFormModel) {
      if (!analysisFormModel.isValid()) return;

      var nodeDefModel = analysisDefinitionNodesCollection.get(analysisFormModel.id);

      if (nodeDefModel) {
        analysisFormModel.updateNodeDefinition(nodeDefModel);
        var layerDefModel = layerDefinitionsCollection.findOwnerOfAnalysisNode(nodeDefModel);
        analysisDefinitionsCollection.saveAnalysisForLayer(layerDefModel);

        MetricsTracker.track('Modified analysis', {
          analysis: {
            id: analysisFormModel.attributes['id'],
            natural_id: analysisFormModel.attributes['id'],
            type: analysisFormModel.attributes['type']
          }
        });
      } else {
        nodeDefModel = analysisFormModel.createNodeDefinition(this); // delegate creation to the form, but expect it to call userActions.createAnalysisNode when done with its internal stuff
      }
      nodeDefModel.USER_SAVED = true;
    },

    /**
     * Creates a new analysis node on a particular layer.
     * It's assumed to be created on top of an existing node.
     * @param {Object} nodeAttrs
     * @param {Object} layerDefModel - instance of layer-definition-model
     * @return {Object} instance of analysis-definition-node-model
     */
    createAnalysisNode: function (nodeAttrs, layerDefModel) {
      var nodeDefModel = analysisDefinitionNodesCollection.add(nodeAttrs, {parse: false});

      layerDefModel.save({
        cartocss: camshaftReference.getDefaultCartoCSSForType(),
        source: nodeDefModel.id
      });
      analysisDefinitionsCollection.saveAnalysisForLayer(layerDefModel);

      MetricsTracker.track('Created analysis', {
        analysis: {
          id: nodeDefModel.attributes['id'],
          natural_id: nodeDefModel.attributes['id'],
          type: nodeDefModel.attributes['type']
        }
      });

      ResetStylePerNode(nodeDefModel, layerDefModel);

      return nodeDefModel;
    },

    saveAnalysisSourceQuery: function (query, nodeDefModel, layerDefModel) {
      if (!nodeDefModel) throw new Error('nodeDefModel is required');
      if (nodeDefModel.get('type') !== 'source') throw new Error('nodeDefModel must be a source node');

      nodeDefModel.set('query', query || '');

      analysisDefinitionsCollection.saveAnalysisForLayer(layerDefModel);
      ResetStylePerNode(nodeDefModel, layerDefModel);
      layerDefModel.save(); // to make sure layer's source ref is persisted
    },

    saveWidgetOption: function (widgetOptionModel) {
      if (!widgetOptionModel) throw new Error('widgetOptionModel is required');

      if (widgetOptionModel.analysisDefinitionNodeModel()) { // Might not always have a node-definition, e.g. time-series none-option
        var layerDefModel = widgetOptionModel.layerDefinitionModel();
        analysisDefinitionsCollection.saveAnalysisForLayer(layerDefModel);
        layerDefModel.save(); // to make sure layer's source ref is persisted
      }

      return widgetOptionModel.save(widgetDefinitionsCollection); // delegate back to form model to decide how to save
    },

    saveWidget: function (widgetDefModel) {
      if (!widgetDefModel) throw new Error('widgetDefModel is required');

      var widgetLayerId = widgetDefModel.get('layer_id');
      layerDefinitionsCollection.some(function (layerDefModel) {
        if (layerDefModel.id === widgetLayerId) {
          analysisDefinitionsCollection.saveAnalysisForLayer(layerDefModel);
          layerDefModel.save(); // to make sure layer's source ref is persisted
          return true; // aborts the "some"-iterator
        }
      });

      widgetDefModel.save();
    },

    deleteAnalysisNode: function (nodeId) {
      var nodeDefModel = analysisDefinitionNodesCollection.get(nodeId);
      if (!nodeDefModel) return false; // abort if there is no node-definition; nothing to delete/change
      if (!nodeDefModel.canBeDeletedByUser()) return false;

      var layerDefModel = layerDefinitionsCollection.findOwnerOfAnalysisNode(nodeDefModel);
      var primarySourceNode = nodeDefModel.getPrimarySource();

      var containsNode = function (m) {
        return m !== layerDefModel && m !== nodeDefModel && m.containsNode(nodeDefModel);
      };
      _
        .flatten([
          widgetDefinitionsCollection.filter(containsNode),
          layerDefinitionsCollection.filter(containsNode),
          analysisDefinitionsCollection.filter(containsNode),
          analysisDefinitionNodesCollection.filter(containsNode),
          nodeDefModel
        ], true)
        .forEach(function (m) {
          m.destroy();
        });

      // Try to restore old styles if we have them, reset them if not
      var oldNodeStyle = primarySourceNode.getStyleHistoryForLayer(layerDefModel.id);
      if (oldNodeStyle) {
        layerDefModel.set({source: primarySourceNode.id}, {ignoreSchemaChange: true});
        layerDefModel.styleModel.set(layerDefModel.styleModel.parse(oldNodeStyle.options.style_properties));
        layerDefModel.set({
          cartocss: oldNodeStyle.options.tile_style,
          infowindow: oldNodeStyle.infowindow,
          tooltip: oldNodeStyle.tooltip,
          sql_wrap: oldNodeStyle.options.sql_wrap
        });
      } else {
        layerDefModel.set({source: primarySourceNode.id});
        ResetStylePerNode(primarySourceNode, layerDefModel);
      }
      layerDefModel.save();
      analysisDefinitionsCollection.saveAnalysisForLayer(layerDefModel);

      MetricsTracker.track('Deleted analysis', {
        analysis: {
          id: nodeDefModel.attributes['id'],
          natural_id: nodeDefModel.attributes['id'],
{'fraction_non_alphanumeric': 0.10225286922023331, 'fraction_numerical': 0.0003778397015066358, 'mean_word_length': 3.215409118056938, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, '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': '6340566', 'n_tokens_mistral': 5491, 'n_tokens_neox': 5300, 'n_words': 1190}