wb/mgr/model/MgrModel.js

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

/**
 * @module nmodule/webEditors/rc/wb/mgr/model/MgrModel
 */
define([ 'baja!',
        'Promise',
        'underscore',
        'nmodule/webEditors/rc/wb/mgr/MgrTypeInfo',
        'nmodule/webEditors/rc/wb/table/model/ComponentTableModel' ], function (
         baja,
         Promise,
         _,
         MgrTypeInfo,
         ComponentTableModel) {

  'use strict';

  function checkNewTypes(newTypes) {
    _.each(newTypes, function (typeInfo) {
      if (!(typeInfo instanceof MgrTypeInfo)) {
        throw new Error('new type is not a MgrTypeInfo instance');
      }
    });
  }

  /**
   * If component is parented, unparent it and return its old property name.
   *
   * @inner
   * @param {baja.Complex} comp
   * @param {baja.comm.Batch} batch
   * @returns {Promise}
   */
  function unparentAndGetPropName(comp, batch) {
    var parent = comp.getParent(),
        prop = comp.getPropertyInParent();

    return Promise.resolve(parent && parent.remove({ slot: prop, batch: batch }))
      .then(function () {
        return prop && String(prop);
      });
  }

  /**
   * API Status: **Development**
   *
   * A layer that adds edit/add/remove functionality on top of a
   * `ComponentTableModel`.
   *
   * Due to the incubating status of the manager framework, it is *not
   * recommended* that you extend `MgrModel` directly. Instead, extend
   * `DeviceMgrModel` or `PointMgrModel`: these will provide more robust
   * functionality for most use cases.
   *
   * @class
   * @alias module:nmodule/webEditors/rc/wb/mgr/model/MgrModel
   * @extends module:nmodule/webEditors/rc/wb/table/model/ComponentTableModel
   * @param {Object} params
   * @param {Array.<module:nmodule/webEditors/rc/wb/mgr/MgrTypeInfo>} [params.newTypes]
   * array of `MgrTypeInfo` instances representing types that will be offered when creating new
   * instances for this model.
   * @see module:nmodule/driver/rc/wb/mgr/DeviceMgrModel
   * @see module:nmodule/driver/rc/wb/mgr/PointMgrModel
   */
  var MgrModel = function MgrModel(params) {
    ComponentTableModel.apply(this, arguments);

    var newTypes = params.newTypes || [];

    checkNewTypes(newTypes);
    this.$newTypes = newTypes;
  };
  MgrModel.prototype = Object.create(ComponentTableModel.prototype);
  MgrModel.prototype.constructor = MgrModel;

  /**
   * Clean up the `MgrModel` when the parent `Manager` is being destroyed. This is
   * an opportunity to unhook any remaining event handlers on the model or its data source.
   *
   * @returns Promise.<*>
   */
  MgrModel.prototype.destroy = function () {
    var componentSource = this.getComponentSource();

    //TODO: proper way of removing column event handlers
    this.removeAllListeners('columnsFlagsChanged');

    if (componentSource) { componentSource.destroy(); }

    return this.removeColumns(this.getColumns());
  };

  /**
   * Get the columns contained in this `MgrModel`.
   *
   * @override
   * @function getColumns
   * @memberOf module:nmodule/webEditors/rc/wb/mgr/model/MgrModel
   * @param {Number} [flags] if given, only return columns that have these
   * flags.
   * @returns {Array.<module:nmodule/webEditors/rc/wb/mgr/model/MgrColumn>}
   */

  /**
   * Get the `MgrTypeInfo` instances representing the types that are acceptable to add to
   * this model.
   *
   * @returns {Array.<module:nmodule/webEditors/rc/wb/mgr/MgrTypeInfo>} array of MgrTypeInfos to add
   */
  MgrModel.prototype.getNewTypes = function () {
    return this.$newTypes;
  };

  /**
   * Override point to customize how new instances of the selected `MgrTypeInfo`
   * are instantiated. The default implementation is to simply delegate to the type
   * info's #newInstance() function.
   *
   * @param {module:nmodule/webEditors/rc/wb/mgr/MgrTypeInfo} typeInfo
   * @returns {baja.Value|Promise}
   */
  MgrModel.prototype.newInstance = function (typeInfo) {
    return typeInfo.newInstance();
  };

  /**
   * Add the instances to this model, to be called when an add dialog or
   * other add operation is committed. The default implementation is to add the
   * given instances to the underlying `ComponentSource` container, which will
   * commit these changes up to the station.
   *
   * Like Workbench, parented instances will be unparented and re-added to the
   * container component. (Workbench does this via a Mark; here it will be
   * done using BajaScript remove/add calls.)
   *
   * @param {Array.<baja.Component>} instances the instances to add
   * @param {Array.<String>} [names] the desired slot names for the instances.
   * If omitted, default names derived from the instance Types will be used.
   * @returns {Promise} promise to be resolved when the instances are
   * added, or rejected if the add fails.
   */
  MgrModel.prototype.addInstances = function (instances, names) {
    names = names || [];

    var source = this.getComponentSource(),
        batch = new baja.comm.Batch(),
        getNames = _.map(instances, function (instance, i) {
          return unparentAndGetPropName(instance, batch)
            .then(function (prop) {
              return names[i] || prop;
            });
        });

    batch.commit();

    return Promise.all(getNames)
      .then(function (names) {
        return source.addComponents(instances, names);
      });
  };

  return (MgrModel);
});