wb/table/model/ComponentTableModel.js

/**
 * @copyright 2015 Tridium, Inc. All Rights Reserved.
 * @author Logan Byam
 */

/**
 * @module nmodule/webEditors/rc/wb/table/model/ComponentTableModel
 */
define([ 'baja!',
        'log!nmodule.webEditors.rc.wb.table.model.ComponentTableModel',
        'underscore',
        'nmodule/webEditors/rc/fe/baja/util/typeUtils',
        'nmodule/webEditors/rc/wb/table/model/ComponentSource',
        'nmodule/webEditors/rc/wb/table/model/TableModel',
        'nmodule/webEditors/rc/wb/table/model/source/ContainerComponentSource' ], function (
         baja,
         log,
         _,
         typeUtils,
         ComponentSource,
         TableModel,
         ContainerComponentSource) {

  'use strict';

  var isComponent = typeUtils.isComponent,
      logError = log.severe.bind(log),

      DEFAULT_CHANGE_EVENT_THROTTLE_MS = 250;

  /**
   * API Status: **Development**
   *
   * Table model where each row in the table represents a `Component`.
   *
   * A `ComponentTableModel` is backed by a `ComponentSource`, which provides
   * the list of `Components` to build into table rows.
   *
   * @class
   * @extends module:nmodule/webEditors/rc/wb/table/model/TableModel
   * @alias module:nmodule/webEditors/rc/wb/table/model/ComponentTableModel
   * @param {Object|baja.Component} params parameters object, or a `Component`
   * if no parameters required.
   * @param {baja.Component|module:nmodule/webEditors/rc/wb/table/model/ComponentSource} params.componentSource
   * the source of components to build into table rows.
   * If a `Component` is given it will just be wrapped in a `ComponentSource`
   * with no parameters.
   * @param {Array.<module:nmodule/webEditors/rc/wb/table/model/Column>} params.columns
   */
  var ComponentTableModel = function ComponentTableModel(params) {
    params = baja.objectify(params, 'componentSource');

    var that = this,
        componentSource = params.componentSource,
        rowsChangedEventDelay = params.rowsChangedEventDelay;

    if (isComponent(componentSource)) {
      componentSource = new ContainerComponentSource(componentSource);
    }

    if (!(componentSource instanceof ComponentSource)) {
      throw new Error('Component or ComponentSource required');
    }

    if (_(rowsChangedEventDelay).isUndefined()) {
      rowsChangedEventDelay = DEFAULT_CHANGE_EVENT_THROTTLE_MS;
    }

    that.$componentSource = componentSource;
    that.$bufferedChanges = [];
    that.$rowsChangedEventDelay = rowsChangedEventDelay;

    that.$emitThrottledRowsChangedEvent = _.throttle(function () {
      if (that.$bufferedChanges.length) {
        var rows = [];

        _.each(that.$bufferedChanges, function (comp) {
          var rowAndIndex = that.$getRowForSubject(comp);
          if (rowAndIndex) {
            rows.push(rowAndIndex.row);
          }
        });

        if (rows.length) { that.emit('rowsChanged', rows); }

        that.$bufferedChanges = [];
      }
    }, rowsChangedEventDelay, { leading: false });

    componentSource
      .on('added', function (comps) {
        that.insertRows(comps).catch(logError);
      })
      .on('removed', function (comps) {
        _.each(comps, function (comp) {
          var obj = that.$getRowForSubject(comp);
          if (obj) { that.removeRows([ obj.row ]).catch(logError); }
        });
      })
      .on('changed', function (comp) {
        if (!(_.contains(that.$bufferedChanges, comp))) {
          that.$bufferedChanges.push(comp);
          that.$emitThrottledRowsChangedEvent();
        }
      });

    TableModel.call(that, {
      columns: params.columns,
      rows: componentSource.getComponents()
    });
  };
  ComponentTableModel.prototype = Object.create(TableModel.prototype);
  ComponentTableModel.prototype.constructor = ComponentTableModel;

  /**
   * Find the first row that has the given subject (identity equal).
   *
   * @private
   * @param {*} subject
   * @returns {Object} object with `row` (the actual Row) and `index`
   * properties, or undefined if not found
   */
  ComponentTableModel.prototype.$getRowForSubject = function (subject) {
    var rows = this.getRows(), i;
    for (i = 0; i < rows.length; i++) {
      if (rows[i].getSubject() === subject) {
        return { row: rows[i], index: i };
      }
    }
  };

  /**
   * Get the `ComponentSource` backing this table model.
   *
   * @returns {module:nmodule/webEditors/rc/wb/table/model/ComponentSource}
   */
  ComponentTableModel.prototype.getComponentSource = function () {
    return this.$componentSource;
  };

  /**
   * Return the delay (in milliseconds) used by throttled 'rowsChanged' event
   * emission function.
   *
   * @private
   */
  ComponentTableModel.prototype.$getRowsChangedEventDelay = function () {
    return this.$rowsChangedEventDelay;
  };

  return (ComponentTableModel);
});