Drip

Drip is a event-emitter framework for node and the browser that supports namespaces and wildcards with a focus on perfomance.

Fork me on GitHub
git clone https://github.com/logicalparadox/drip

lib/drip.js [view on github]

Drip#constructor

@param{ Object }options
@apipublic

Create new instance of drip. Can also be easily
be used as the basis for other objects.

 // for normal events
 var drop = new Drip();

 // for namespaced/wildcarded events
 var drop = new Drip({ wildcard: true });

Wildcards and namespacing are off by default. By sending
the wildcard option as true, drip will enable both
wildcards and namespacing.

Options

  • delimeter {String} defaults to :
  • wildcard {Boolean} defaults to false
View Source
function Drip (opts) {
  if (opts) {
    this._drip = {};
    this._drip.delimeter = opts.delimeter || ':';
    this._drip.wildcard = opts.wildcard || (opts.delimeter ? true : false);
  }
}
            

.on(event, callback)

Drip.prototype.on()

@param{ String | Array }event
@param{ Function }callback
@apipublic

Bind a callback function to all emits of event.
Wildcards *, will be executed for every event. At
that heirarchy.

 // normal events
 drop.on('foo', callback);

 // namespaced events as string
 drop.on('foo:bar', callback);

 // namespaced events as array
 drop.on(['foo', 'bar', '*', 'fu'], callback);

An array can be passed for event when namespacing is enabled
if that is your preference.

View Source
Drip.prototype.on = function (ev, fn) {

  if (!this._drip || (this._drip && !this._drip.wildcard)) {
    var map = this._events || (this._events = {});

    if (!map[ev]) {
      map[ev] = fn;
    } else if ('function' === typeof map[ev]) {
      map[ev] = [ map[ev], fn ];
    } else {
      map[ev].push(fn);
    }

  } else if (this._drip && this._drip.wildcard) {
    var evs = Array.isArray(ev) ? ev : ev.split(this._drip.delimeter)
      , store = this._events || (this._events = {});

    var traverse = function (events, map) {
      var event = events.shift();
      map[event] = map[event] || {};

      if (events.length) {
        traverse(events, map[event]);
      } else {

        if (!map[event]._) {
          map[event]._= fn;
        } else if ('function' === typeof map[event]._) {
          map[event]._ = [ map[event]._, fn ];
        } else {
          map[event]._.push(fn);
        }

      }
    };

    traverse(evs, store);
  }

  return true;
};
            

.many(event, ttl, callback)

Drip.prototype.many()

@param{ String | Array }event
@param{ Integer }TTLTimes to listen
@param{ Function }callback
@apipublic

Bind a callback function to count(ttl) emits of event.

 // 3 times then auto turn off callback
 drop.many('event', 3, callback)
View Source
Drip.prototype.many = function (ev, times, fn) {
  var self = this;

  var wrap = function() {
    if (--times === 0) {
      self.off(ev, wrap);
    }
    fn.apply(null, arguments);
  };

  this.on(ev, wrap);
  return this;
};
            

.once(event, callback)

Drip.prototype.once()

@param{ String | Array }event
@param{ Function }callback
@apipublic

Bind a callback function to one emit of event.

 drip.once('event', callback)
View Source
Drip.prototype.once = function (ev, fn) {
  this.many(ev, 1, fn);
  return this;
};
            

.off([event], [callback])

Drip.prototype.off()

@param{ String | Array }eventoptional
@param{ Function }callbackoptional
@apipublic

Unbind callback function from event. If no function
is provided will unbind all callbacks from event. If
no event is provided, event store will be purged.

 drop.off('event', callback);
View Source
Drip.prototype.off = function (ev, fn) {
  if (!this._events) return false;

  if (arguments.length === 0) {
    delete this._events;
    return true;
  }

  if (!this._drip || (this._drip && !this._drip.wildcard)) {
    if ('function' !== typeof fn) {
      this._events[ev] = null;
      return true;
    }

    var fns = this._events[ev];

    if (!fns) {
      return false;
    } else if ('function' === typeof fns && fns == fn) {
      this._events[ev] = null;
    } else if (Array.isArray(fns)) {
      for (var i = 0; i < fns.length; i++) {
        if (fns[i] == fn) fns.splice(i, 1);
      }

      if (fns.length === 0)
        this._events[ev] = null;

      if (fns.length == 1)
        this._events[ev] = fns[0];
    }
  } else {
    var evs = Array.isArray(ev) ? ev : ev.split(this._drip.delimeter);

    if (evs.length === 1) {
      if (this._events[ev]) this._events[ev]._ = null;
      return true;
    } else {
      var isEmpty = function (obj) {
        for (var name in obj) {
          if (obj[name] && name != '_') return false;
        }
        return true;
      };

      var clean = function (event) {
        if (fn && 'function' === typeof fn) {
          for (var i = 0; i < event._.length; i++) {
            if (fn == event._[i]) {
              event._.splice(i, 1);
            }
          }

          if (event._.length === 0)
            event._ = null;

          if (event._ && event._.length == 1)
            event._ = event._[0];
        } else {
          event._ = null;
        }

        if (!event._ && isEmpty(event))
          event = null;

        return event;
      };

      var traverse = function (events, map) {
        var event = events.shift();

        if (map[event] && map[event]._ && !events.length)
          map[event] = clean(map[event]);

        if (map[event] && events.length)
          map[event] = traverse(events, map[event]);

        if (!map[event]) {
          if (isEmpty(map)) map = null;
        }

        return map;
      };

      this._events = traverse(evs, this._events);
    }
  }

  return this;
};
            

.removeAllListeners([event], [callback])

Drip.prototyperemoveAllListeners

@seeDrip.prototype.off
@apipublic

This is an alias for .off().

View Source
Drip.prototype.removeAllListeners = Drip.prototype.off;
            

.emit(event, [args], [...])

Drip.prototype.emit()

@param{ String | Array }eventname
@param{ String | Object }argumentsmultiple parameters to pass to callback functions
@apipublic

Trigger event, passing any arguments to callback functions.

 // normal event
 drop.emit('event', arg, ...);

 // namespaced as string
 drop.emit('foo:bar', arg, ...)

 // namespaced as array
 drop.emit(['foo', 'bar'], arg, ...);
View Source
Drip.prototype.emit = function () {
  var ev = arguments[0]
    , fns;

  if (!this._events) return false;

  if (!this._drip || (this._drip && !this._drip.wildcard)) {
    fns = this._events[ev];
  } else if (this._drip && this._drip.wildcard) {
    var evs = Array.isArray(ev) ? ev : ev.split(this._drip.delimeter)
      , fns = [];

    var addFns = function (funcs) {
      if ('function' == typeof funcs) {
        fns.push(funcs);
      } else {
        for (var i = 0; i < funcs.length; i++) {
          fns.push(funcs[i]);
        }
      }
    };

    var traverse = function (events, map) {
      var event = events.shift();

      if (map[event] && map[event]._ && !events.length)
        addFns(map[event]._);

      if (map['*'] && map['*']._ && !events.length)
        addFns(map['*']._);

      if (events.length) {
        if (map[event])
          traverse(events.slice(0), map[event]);

        if (map['*'])
          traverse(events.slice(0), map['*']);
      }
    };

    traverse(evs, this._events);
  }

  if ('function' === typeof fns) {
    switch (arguments.length) {
      case 1:
        fns.call(this);
        break;
      case 2:
        fns.call(this, arguments[1]);
        break;
      case 3:
        fns.call(this, arguments[1], arguments[2]);
        break;
      default:
        var l = arguments.length
          , _a = new Array(l - 1);

        for (var i = 1; i < l; i++) {
          _a[i - 1] = arguments[i];
        }

        fns.apply(this, _a);
        break;
    }
  } else if (Array.isArray(fns)) {
    var l = arguments.length
      , _a = new Array(l - 1);

    for (var i = 1; i < l; i++) {
      _a[i - 1] = arguments[i];
    }

    for (var i = 0; i < fns.length; ++i) {
      fns[i].apply(this, _a);
    }
  }

  return true;
};