dragdrop/dragDropUtils.js

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

define([ 'Promise',
        'bajaux/dragdrop/Envelope',
        'bajaux/dragdrop/NavNodeEnvelope',
        'bajaux/dragdrop/StringEnvelope' ], function (
         Promise,
         Envelope,
         NavNodeEnvelope,
         StringEnvelope) {

  'use strict';

  //TODO: i expect this will become a BIJavaScript/agent-on mechanism as well
  function toEnvelope(mimeType, values) {
    switch (mimeType) {
      case 'niagara/navnodes':
        return NavNodeEnvelope;
      case 'niagara/strings':
        return StringEnvelope;
    }
  }

  function reject(msg) { return Promise.reject(new Error(msg)); }

  /**
   * Utilities for interacting with the HTML5 drag/drop APIs.
   *
   * @exports bajaux/dragdrop/dragDropUtils
   */
  var exports = {};

  /**
   * Add the given data onto the clipboard.
   *
   * @param {DataTransfer} clipboard the `DataTransfer` on which to set the data
   * @param {String|module:bajaux/dragdrop/Envelope} mimeType
   * a supported Niagara mime type for the given data, in which case the given
   * values will be converted to an `Envelope` instance; or an `Envelope`
   * instance directly
   * @param {Array} [values] if a mime type given, the values to convert to an
   * `Envelope` instance
   * @returns {Promise} promise to be resolved when the `Envelope`
   * instance has been created or received, and has written JSON data onto the
   * clipboard
   * 
   * @example
   *   $('.dragSource').on('dragstart', function (e) {
   *     var dataTransfer = e.originalEvent.dataTransfer;
   *     dragDropUtils.toClipboard(dataTransfer, 'niagara/strings', 
   *         [ 'hello', 'world' ])
   *       .then(function () {
   *         console.log('clipboard populated with: ' + 
   *           dataTransfer.getData('Text'));
   *       });
   *   });
   */
  exports.toClipboard = function (clipboard, mimeType, values) {
      if (!clipboard) {
        return reject('DataTransfer required');
      }

      var env;

      if (mimeType instanceof Envelope) {
        env = mimeType;
        mimeType = env.getMimeType();
      } else {
        var EnvelopeCtor = toEnvelope(mimeType);

        if (!EnvelopeCtor) {
          return reject('unknown mime type ' + mimeType);
        }

        env = new EnvelopeCtor(values);
      }

      return env.toJson()
        .then(function (json) {
          clipboard.setData('Text', JSON.stringify({
            mime: mimeType,
            data: json
          }));
        });
  };

  /**
   * Read data previously written to the clipboard by a call to `toClipboard`.
   *
   * @param {DataTransfer} clipboard
   * @returns {Promise} promise to be resolved with an `Envelope`
   * instance
   * 
   * @example
   *   $('.dropTarget').on('drop', function (e) {
   *     dragDropUtils.fromClipboard(e.originalEvent.dataTransfer)
   *       .then(function (envelope) {
   *         envelope.toValues().then(function (values) {
   *           values.forEach(handleValue);
   *         });
   *       });
   *   });

   */
  exports.fromClipboard = function (clipboard) {
    if (!clipboard) {
      return reject('DataTransfer required');
    }

    var data = clipboard.getData('Text'),
        json;

    if (!data) {
      return reject('no text data on clipboard');
    }

    try {
      json = JSON.parse(data);
    } catch (e) {
      return reject('invalid JSON on clipboard');
    }

    var mime = json.mime,
        Envelope = toEnvelope(mime);

    if (!Envelope) {
      return reject('unknown mime type ' + mime);
    }

    try {
      return Promise.resolve(new Envelope(json.data));
    } catch (e) {
      return Promise.reject(e);
    }
  };

  return exports;
});