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; } });