CartoDB/cartodb20

View on GitHub
lib/assets/javascripts/builder/editor/style/style-definition-model.js

Summary

Maintainability
D
2 days
Test Coverage
var Backbone = require('backbone');
var _ = require('underscore');
var StylesFactory = require('./styles-factory');
var StyleConstants = require('builder/components/form-components/_constants/_style');
var UndoManager = require('builder/data/undo-manager');

module.exports = Backbone.Model.extend({

  parse: function (r) {
    r = r || {};
    return _.extend(
      {
        type: r.type,
        autogenerated: r && r.autogenerated
      },
      r.properties
    );
  },

  initialize: function (attrs, opts) {
    if (!this.get('type')) {
      this.setDefaultPropertiesByType(StyleConstants.Type.SIMPLE, 'point' /* geometryType */);
    }

    UndoManager.init(this, { track: true });
  },

  resetPropertiesFromAutoStyle: function () {
    if (this._stylesPreAutoStyle) {
      delete this.attributes.autoStyle;
      this.set(this._stylesPreAutoStyle);
      this.removeStylesPreAutoStyle();
    }
  },

  removeStylesPreAutoStyle: function () {
    delete this._stylesPreAutoStyle;
  },

  setPropertiesFromAutoStyle: function (params) {
    if (!params.definition) throw new Error('definition is required');
    if (!params.geometryType) throw new Error('geometryType is required');
    if (!params.widgetId) throw new Error('widgetId is required');

    if (!this._stylesPreAutoStyle) {
      this._stylesPreAutoStyle = JSON.parse(JSON.stringify(this.attributes));
    }

    // In order to trigger a proper change at the end of this function, we have
    // to make a clear change in the attributes, like delete the autoStyle property.
    delete this.attributes.autoStyle;

    var extendAutoStyleProperties = function (attribute, newProperties) {
      var properties = this.get(attribute);
      // Check domain quotes
      if (newProperties.color && newProperties.color.domain) {
        var quotedDomain = _.compact(
          _.map(newProperties.color.domain, function (name) {
            if (name && name !== true) {
              return '"' + name.toString().replace(/"/g, '\\"').replace(/\n/g, '\\n') + '"';
            } else {
              return name;
            }
          })
        );
        newProperties.color.static = true;
        newProperties.color.domain = quotedDomain;
        newProperties.color.quantification = 'category';
        newProperties.color.attribute_type = 'string';
      } else {
        newProperties.color.bins = newProperties.color.range.length;
        newProperties.color.quantification = 'quantiles';
        newProperties.color.attribute_type = 'number';
      }

      properties = _.extend(
        properties,
        newProperties
      );

      return properties;
    }.bind(this);

    var currentAttrs = JSON.parse(JSON.stringify(this.attributes));
    var geometryType = params.geometryType;
    var definition = params.definition[geometryType];

    if (definition) {
      if (geometryType === 'line') {
        currentAttrs.stroke = extendAutoStyleProperties('stroke', definition);
      } else {
        currentAttrs.fill = extendAutoStyleProperties('fill', definition);
      }
    }

    this.set(
      _.extend(
        {
          type: StyleConstants.Type.SIMPLE,
          autoStyle: params.widgetId
        },
        currentAttrs
      )
    );
  },

  setDefaultPropertiesByType: function (styleType, geometryType, silently) {
    // Get default aggregation and properties from factory and apply them
    this.set(
      _.extend(
        {
          type: styleType
        },
        StylesFactory.getDefaultStyleAttrsByType(styleType, geometryType)
      ), {
        silently: !!silently
      }
    );

    // Although we want to make the change silently, we have several places listening
    // for style changes, so we trigger this custom event
    if (silently) {
      this.trigger('style:update');
    }
  },

  setFill: function (type) {
    var simpleFill = StylesFactory.getDefaultStyleAttrsByType(type, 'point');
    this.set('fill', simpleFill.fill);
  },

  applyLastState: function () {
    this._undoManager.stopTracking();
    this.trigger('change');
    this._undoManager.startTracking();
  },

  resetStyles: function () {
    this.setDefaultPropertiesByType(StyleConstants.Type.NONE, '');
  },

  // Backend will migrate current wizard properties to style properties,
  // providing a flag which indicates if it is generated by them
  isAutogenerated: function () {
    return this.get('autogenerated');
  },

  isAggregatedType: function () {
    return _.contains(StylesFactory.getAggregationTypes(), this.get('type'));
  },

  isAnimation: function () {
    return this.get('type') === StyleConstants.Type.ANIMATION;
  },

  isHeatmap: function () {
    return this.get('type') === StyleConstants.Type.HEATMAP;
  },

  hasNoneStyles: function () {
    return this.get('type') === StyleConstants.Type.NONE;
  },

  canApplyAutoStyle: function () {
    return this.get('type') === StyleConstants.Type.SIMPLE;
  },

  getColumnsUsedForStyle: function () {
    var fillColumns = this._getFillColumns();
    var strokeColumns = this._getStrokeColumns();
    var labelColumns = this._getLabelColumns();
    var aggregationColumns = this._getAggregationColumns();

    return [].concat(fillColumns, strokeColumns, labelColumns, aggregationColumns);
  },

  // Unflatten attributes
  toJSON: function () {
    return {
      type: this.get('type'),
      properties: _.omit(this.attributes, 'type', 'autogenerated')
    };
  },

  _getFillColumns: function () {
    var columns = [];

    var fill = this.get('fill');
    if (fill && fill.color && fill.color.attribute) {
      columns.push({
        name: fill.color.attribute,
        type: fill.color.attribute_type || 'string'
      });
    }

    if (fill && fill.size && fill.size.attribute) {
      columns.push({
        name: fill.size.attribute,
        type: 'number'
      });
    }

    return columns;
  },

  _getStrokeColumns: function () {
    var columns = [];

    var stroke = this.get('stroke');
    if (stroke && stroke.color && stroke.color.attribute) {
      columns.push({
        name: stroke.color.attribute,
        type: stroke.color.attribute_type || 'string'
      });
    }
    if (stroke && stroke.size && stroke.size.attribute) {
      columns.push({
        name: stroke.size.attribute,
        type: 'number'
      });
    }

    return columns;
  },

  _getLabelColumns: function () {
    var columns = [];

    // Labels
    var labels = this.get('labels');
    if (labels && labels.attribute && labels.enabled) {
      columns.push({
        name: labels.attribute
      });
    }

    return columns;
  },

  _getAggregationColumns: function () {
    var columns = [];

    var aggregation = this.get('aggregation');
    if (aggregation && aggregation.value && aggregation.value.attribute) {
      columns.push({
        name: aggregation.value.attribute,
        type: aggregation.value.attribute_type || 'string'
      });
    }

    return columns;
  }
});