CartoDB/cartodb20

View on GitHub
lib/assets/javascripts/deep-insights/widgets/time-series/content-view.js

Summary

Maintainability
D
2 days
Test Coverage
var _ = require('underscore');
var CoreView = require('backbone/core-view');
var placeholderTemplate = require('./placeholder.tpl');
var contentTemplate = require('./content.tpl');
var HistogramView = require('./histogram-view');
var TimeSeriesHeaderView = require('./time-series-header-view');
var DropdownView = require('../dropdown/widget-dropdown-view');
var layerColors = require('../../util/layer-colors');
var analyses = require('../../data/analyses');
var escapeHTML = require('../../util/escape-html');

/**
 * Widget content view for a time-series
 */
module.exports = CoreView.extend({
  className: 'CDB-Widget-body CDB-Widget-body--timeSeries',

  initialize: function () {
    if (!this.model.dataviewModel) throw new Error('dataviewModel is required');
    if (!this.model.layerModel) throw new Error('layerModel is required');

    this._dataviewModel = this.model.dataviewModel;
    this._layerModel = this.model.layerModel;
    this._selectedAmount = 0;
    this._initBinds();
  },

  render: function () {
    this.clearSubViews();
    this.$el.empty();

    var sourceId = this._dataviewModel.get('source').id;
    var letter = layerColors.letter(sourceId);
    var sourceColor = layerColors.getColorForLetter(letter);
    var sourceType = this._dataviewModel.getSourceType() || '';
    var isSourceType = this._dataviewModel.isSourceType();
    var layerName = isSourceType
      ? this.model.get('table_name')
      : this._layerModel.get('layer_name');

    if (this._isDataEmpty() || this._hasError()) {
      this.$el.append(placeholderTemplate({
        hasTorqueLayer: false
      }));
    } else {
      this.$el.append(contentTemplate({
        sourceId: sourceId,
        sourceType: analyses.title(sourceType),
        isSourceType: isSourceType,
        showSource: this.model.get('show_source') && letter !== '',
        sourceColor: sourceColor,
        layerName: escapeHTML(layerName)
      }));
      this._createHistogramView();
      this._createHeaderView();
      this._createDropdownView();
      this._updateRange();
    }
    return this;
  },

  _initBinds: function () {
    this._dataviewModel.once('error', function () {
      console.log('the tiler does not support non-torque layers just yet…');
    });

    this.listenTo(this._dataviewModel, 'change:data', this.render);
    this.listenToOnce(this.model, 'change:hasInitialState', this.render);

    this.listenTo(this._layerModel, 'change:layer_name', this.render);
    this.add_related_model(this._layerModel);
  },

  _createHistogramView: function () {
    if (this._histogramView) {
      this._histogramView.remove();
    }

    this._histogramView = new HistogramView({
      timeSeriesModel: this.model,
      dataviewModel: this._dataviewModel,
      layerModel: this._layerModel,
      rangeFilter: this._dataviewModel.filter,
      displayShadowBars: !this.model.get('normalized'),
      normalized: !!this.model.get('normalized')
    });

    this.addView(this._histogramView);
    this.$('.js-content').append(this._histogramView.render().el);
  },

  _createHeaderView: function () {
    if (this._headerView) {
      this._headerView.remove();
    }

    this._headerView = new TimeSeriesHeaderView({
      dataviewModel: this._dataviewModel,
      layerModel: this._layerModel,
      rangeFilter: this._dataviewModel.filter,
      timeSeriesModel: this.model,
      showClearButton: true,
      selectedAmount: this._selectedAmount
    });

    if (!this._histogramView) {
      throw new Error('Histogram view must be instantiated before the header view');
    }
    this._headerView.bind('resetFilter', this._histogramView.resetFilter, this._histogramView);
    this.addView(this._headerView);
    this.$('.js-title').append(this._headerView.render().el);
  },

  _createDropdownView: function () {
    if (this._dropdownView) {
      this._dropdownView.remove();
    }

    this._dropdownView = new DropdownView({
      model: this.model,
      target: '.js-actions',
      container: this.$('.js-header'),
      flags: {
        localTimezone: this._dataviewModel.getColumnType() === 'date',
        normalizeHistogram: true,
        canCollapse: false
      }
    });

    this.addView(this._dropdownView);
  },

  _updateRange: function () {
    var bars = this._calculateBars();
    var bins = this._dataviewModel.get('bins');
    var lo = Math.max(bars.loBarIndex, 0);
    var hi = Math.min(bars.hiBarIndex, bins);
    if (lo > 0 || hi < bins) {
      this._histogramView.selectRange(lo, hi);
    }
  },

  _calculateBars: function () {
    var data = this._dataviewModel.getData();
    var min = this.model.get('min');
    var max = this.model.get('max');
    var loBarIndex = this.model.get('lo_index');
    var hiBarIndex = this.model.get('hi_index');
    var startMin;
    var startMax;

    if (data.length > 0) {
      if (!_.isNumber(min) && !_.isNumber(loBarIndex)) {
        loBarIndex = 0;
      } else if (_.isNumber(min) && !_.isNumber(loBarIndex)) {
        startMin = _.findWhere(data, {start: min});
        loBarIndex = (startMin && startMin.bin) || 0;
      }

      if (!_.isNumber(max) && !_.isNumber(hiBarIndex)) {
        hiBarIndex = data.length;
      } else if (_.isNumber(max) && !_.isNumber(hiBarIndex)) {
        startMax = _.findWhere(data, {end: max});
        hiBarIndex = (startMax && startMax.bin + 1) || data.length;
      }
    } else {
      loBarIndex = 0;
      hiBarIndex = data.length;
    }

    return {
      loBarIndex: loBarIndex,
      hiBarIndex: hiBarIndex
    };
  },

  _appendView: function (view) {
    this.addView(view);
    this.$el.append(view.render().el);
  },

  _isDataEmpty: function () {
    var data = this._dataviewModel.getUnfilteredData();
    return _.isEmpty(data) || _.size(data) === 0;
  },

  _hasError: function () {
    return this._dataviewModel.has('error');
  }
});