balloon.js 4.26 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
/**
 * Wrapper for the notifu 1.6 (http://www.paralint.com/projects/notifu/)

Usage
/t <value>      The type of message to display values are:
                    info      The message is an informational message
                    warn      The message is an warning message
                    error     The message is an error message
/d <value>      The number of milliseconds to display (omit or 0 for infinit)
/p <value>      The title (or prompt) of the ballon
/m <value>      The message text
/i <value>      Specify an icon to use ("parent" uses the icon of the parent process)
/e              Enable ballon tips in the registry (for this user only)
/q              Do not play a sound when the tooltip is displayed
/w              Show the tooltip even if the user is in the quiet period that follows his very first login (Windows 7 and up)
/xp             Use IUserNotification interface event when IUserNotification2 is available

// Kill codes:
  2 = Timeout
  3 = Clicked
  4 = Closed or faded out

 */
var path = require('path');
var notifier = path.resolve(__dirname, '../vendor/notifu/notifu');
var checkGrowl = require('../lib/checkGrowl');
var utils = require('../lib/utils');
var Toaster = require('./toaster');
var Growl = require('./growl');
var os = require('os');

var EventEmitter = require('events').EventEmitter;
var util = require('util');

var hasGrowl;

module.exports = WindowsBalloon;

function WindowsBalloon(options) {
  options = utils.clone(options || {});
  if (!(this instanceof WindowsBalloon)) {
    return new WindowsBalloon(options);
  }

  this.options = options;

  EventEmitter.call(this);
}
util.inherits(WindowsBalloon, EventEmitter);

function noop() {}
WindowsBalloon.prototype.notify = function(options, callback) {
  var fallback;
  var notifierOptions = this.options;
  options = utils.clone(options || {});
  callback = callback || noop;

  if (typeof options === 'string') {
    options = { title: 'node-notifier', message: options };
  }

  var actionJackedCallback = utils.actionJackerDecorator(
    this,
    options,
    callback,
    function(data) {
      if (data === 'activate') {
        return 'click';
      }
      if (data === 'timeout') {
        return 'timeout';
      }
      return false;
    }
  );

  if (!!this.options.withFallback && utils.isWin8()) {
    fallback = fallback || new Toaster(notifierOptions);
    return fallback.notify(options, callback);
  }

  if (
    !!this.options.withFallback &&
    (!utils.isLessThanWin8() || hasGrowl === true)
  ) {
    fallback = fallback || new Growl(notifierOptions);
    return fallback.notify(options, callback);
  }

  if (!this.options.withFallback || hasGrowl === false) {
    doNotification(options, notifierOptions, actionJackedCallback);
    return this;
  }

  checkGrowl(notifierOptions, function(_, hasGrowlResult) {
    hasGrowl = hasGrowlResult;

    if (hasGrowl) {
      fallback = fallback || new Growl(notifierOptions);
      return fallback.notify(options, callback);
    }

    doNotification(options, notifierOptions, actionJackedCallback);
  });

  return this;
};

var allowedArguments = ['t', 'd', 'p', 'm', 'i', 'e', 'q', 'w', 'xp'];

function doNotification(options, notifierOptions, callback) {
  var is64Bit = os.arch() === 'x64';
  options = options || {};
  options = utils.mapToNotifu(options);
  options.p = options.p || 'Node Notification:';

  var fullNotifierPath = notifier + (is64Bit ? '64' : '') + '.exe';
  var localNotifier = notifierOptions.customPath || fullNotifierPath;

  if (!options.m) {
    callback(new Error('Message is required.'));
    return this;
  }

  var argsList = utils.constructArgumentList(options, {
    wrapper: '',
    noEscape: true,
    explicitTrue: true,
    allowedArguments: allowedArguments
  });

  if (options.wait) {
    return utils.fileCommand(localNotifier, argsList, function(error, data) {
      var action = fromErrorCodeToAction(error.code);
      if (action === 'error') return callback(error, data);

      return callback(null, action);
    });
  }
  utils.immediateFileCommand(localNotifier, argsList, callback);
}

function fromErrorCodeToAction(errorCode) {
  switch (errorCode) {
    case 2:
      return 'timeout';
    case 3:
    case 6:
    case 7:
      return 'activate';
    case 4:
      return 'close';
    default:
      return 'error';
  }
}