#ifndef _EVENT_EMITTER_ #define _EVENT_EMITTER_ #include #include #include #define THIS_EMITTER \ EventEmitter *emitter = ObjectWrap::Unwrap(info.This()); class EventEmitter : public Nan::ObjectWrap { typedef Nan::CopyablePersistentTraits::CopyablePersistent FN_TYPE; typedef std::deque VEC_TYPE; typedef std::map MAP_TYPE; typedef std::map FNMAP_TYPE; typedef VEC_TYPE::iterator IT_TYPE; typedef MAP_TYPE::iterator MAP_IT_TYPE; public: EventEmitter () { _maxListeners = _defaultMaxListeners; } ~EventEmitter () {} static void extend(v8::Local &ctor, v8::Local &proto) { v8::Local ctorFunc = Nan::GetFunction(ctor).ToLocalChecked(); v8::Local ctorObj = ctorFunc; // -------- static Nan::SetMethod(ctorFunc, "listenerCount", jsStaticListenerCount); ACCESSOR_RW(ctorObj, defaultMaxListeners); // -------- dynamic Nan::SetPrototypeMethod(ctor, "listenerCount", jsListenerCount); Nan::SetPrototypeMethod(ctor, "addListener", jsAddListener); Nan::SetPrototypeMethod(ctor, "emit", jsEmit); Nan::SetPrototypeMethod(ctor, "eventNames", jsEventNames); Nan::SetPrototypeMethod(ctor, "getMaxListeners", jsGetMaxListeners); Nan::SetPrototypeMethod(ctor, "listeners", jsListeners); Nan::SetPrototypeMethod(ctor, "on", jsOn); Nan::SetPrototypeMethod(ctor, "once", jsOnce); Nan::SetPrototypeMethod(ctor, "prependListener", jsPrependListener); Nan::SetPrototypeMethod(ctor, "prependOnceListener", jsPrependOnceListener); Nan::SetPrototypeMethod(ctor, "removeAllListeners", jsRemoveAllListeners); Nan::SetPrototypeMethod(ctor, "removeListener", jsRemoveListener); Nan::SetPrototypeMethod(ctor, "setMaxListeners", jsSetMaxListeners); Nan::SetPrototypeMethod(ctor, "rawListeners", jsRawListeners); // ACCESSOR_RW(proto, type); } void emit(const std::string &name, int argc = 0, v8::Local *argv = NULL) { VEC_TYPE &list = _listeners[name]; if (list.empty()) { return; } for (IT_TYPE it = list.begin(); it != list.end(); ++it) { Nan::Callback callback(Nan::New(*it)); if ( ! callback.IsEmpty() ) { callback.Call(argc, argv); } } } // Deprecated method static NAN_METHOD(jsStaticListenerCount) { EventEmitter *emitter = ObjectWrap::Unwrap(info[0]); REQ_UTF8_ARG(1, name); const VEC_TYPE &list = emitter->_listeners[*name]; RET_VALUE(JS_INT(list.size())); } static NAN_SETTER(defaultMaxListenersSetter) { THIS_EMITTER; SETTER_INT32_ARG; EventEmitter::_defaultMaxListeners = v; } static NAN_GETTER(defaultMaxListenersGetter) { THIS_EMITTER; RET_VALUE(JS_INT(EventEmitter::_defaultMaxListeners)); } static NAN_METHOD(jsAddListener) { THIS_EMITTER; REQ_UTF8_ARG(0, name); REQ_FUN_ARG(1, cb); Nan::Persistent persistentCb; persistentCb.Reset(cb); emitter->_listeners[std::string(*name)].push_back(persistentCb); } static NAN_METHOD(jsEmit) { THIS_EMITTER; REQ_UTF8_ARG(0, name); emitter->emit(*name, info.Length() - 1, &info[1]); } static NAN_METHOD(jsEventNames) { THIS_EMITTER; v8::Local jsNames = Nan::New(emitter->_listeners.size()); if (emitter->_listeners.empty()) { RET_VALUE(jsNames); return; } int i = 0; for (MAP_IT_TYPE it = emitter->_listeners.begin(); it != emitter->_listeners.end(); ++it, i++) { jsNames->Set(JS_INT(i), JS_STR(it->first)); } RET_VALUE(jsNames); } static NAN_METHOD(jsGetMaxListeners) { THIS_EMITTER; RET_VALUE(JS_INT(emitter->_maxListeners)); } static NAN_METHOD(jsListenerCount) { THIS_EMITTER; REQ_UTF8_ARG(0, name); const VEC_TYPE &list = emitter->_listeners[*name]; RET_VALUE(JS_INT(list.size())); } static NAN_METHOD(jsListeners) { THIS_EMITTER; REQ_UTF8_ARG(0, name); VEC_TYPE &list = emitter->_listeners[*name]; v8::Local jsListeners = Nan::New(list.size()); if (list.empty()) { RET_VALUE(jsListeners); return; } int i = 0; for (IT_TYPE it = list.begin(); it != list.end(); ++it, i++) { jsListeners->Set(JS_INT(i), Nan::New(*it)); } RET_VALUE(jsListeners); } static inline void _addListener( const Nan::FunctionCallbackInfo &info, const std::string &name, Nan::Persistent &cb, bool isFront ) { THIS_EMITTER; if (isFront) { emitter->_listeners[name].push_front(cb); emitter->_raw[name].push_front(cb); } else { emitter->_listeners[name].push_back(cb); emitter->_raw[name].push_back(cb); } } static inline void _wrapListener( const Nan::FunctionCallbackInfo &info, bool isFront = false ) { REQ_UTF8_ARG(0, name); REQ_FUN_ARG(1, cb); Nan::Persistent persistentCb; persistentCb.Reset(cb); _addListener(info, *name, persistentCb, isFront); } static inline void _addOnceListener( const Nan::FunctionCallbackInfo &info, const std::string &name, Nan::Persistent &raw, Nan::Persistent &cb, bool isFront ) { THIS_EMITTER; if (isFront) { emitter->_listeners[name].push_front(cb); emitter->_raw[name].push_front(raw); } else { emitter->_listeners[name].push_back(cb); emitter->_raw[name].push_back(raw); } emitter->_wrapped[raw] = cb; } static inline void _wrapOnceListener( const Nan::FunctionCallbackInfo &info, bool isFront = false ) { REQ_UTF8_ARG(0, name); REQ_FUN_ARG(1, raw); v8::Local code = JS_STR( "((emitter, cb) => (...args) => {\n\ cb(...args);\n\ emitter.removeListener(cb);\n\ })" ); v8::Local decor = v8::Local::Cast(v8::Script::Compile(code)->Run()); Nan::Callback decorCb(decor); v8::Local argv[] = { info.This(), raw }; v8::Local wrap = v8::Local::Cast(decorCb.Call(2, argv)); Nan::Persistent persistentWrap; persistentWrap.Reset(wrap); Nan::Persistent persistentRaw; persistentRaw.Reset(raw); _addOnceListener(info, *name, persistentRaw, persistentWrap, isFront); } static NAN_METHOD(jsOn) { _wrapListener(info); } static NAN_METHOD(jsOnce) { _wrapOnceListener(info); } static NAN_METHOD(jsPrependListener) { _wrapListener(info, true); } static NAN_METHOD(jsPrependOnceListener) { _wrapOnceListener(info, true); } static NAN_METHOD(jsRemoveAllListeners) { THIS_EMITTER; if (info->Length > 0 && info[0]->IsString()) { _listeners->clear(); _raw->clear(); _wrapped->clear(); return; } REQ_UTF8_ARG(0, n); std::string name = std::string(*n); VEC_TYPE &list = emitter->_raw[name]; if (list.empty()) { return; } for (IT_TYPE it = list.begin(); it != list.end(); ++it) { emitter->_wrapped.erase(*it) } emitter->_listeners[*name].clear(); emitter->_raw[*name].clear(); } static NAN_METHOD(jsRemoveListener) { THIS_EMITTER; REQ_UTF8_ARG(0, n); REQ_FUN_ARG(1, raw); Nan::Persistent persistentCb; persistentCb.Reset(raw); std::string name = std::string(*n); VEC_TYPE &rawList = emitter->_raw[name]; if (rawList.empty()) { return; } for (IT_TYPE it = rawList.begin(); it != rawList.end(); ++it) { if (*it === persistentCb) { rawList.erase(it); break; } } VEC_TYPE &wrapList = emitter->_listeners[name]; if (emitter->_wrapped.count(persistentCb) == 0) { for (IT_TYPE it = wrapList.begin(); it != wrapList.end(); ++it) { if (*it === persistentCb) { wrapList.erase(it); break; } } return; } const FN_TYPE &wrapped = _wrapped[persistentCb]; for (IT_TYPE it = wrapList.begin(); it != wrapList.end(); ++it) { if (*it === wrapped) { wrapList.erase(it); break; } } emitter->_wrapped.erase(persistentCb); } static NAN_METHOD(jsSetMaxListeners) { THIS_EMITTER; REQ_INT32_ARG(0, value); emitter->_maxListeners = value; } static NAN_METHOD(jsRawListeners) { THIS_EMITTER; REQ_UTF8_ARG(0, name); VEC_TYPE &list = emitter->_raw[*name]; Local jsListeners = Nan::New(list.size()); if (list.empty()) { RET_VALUE(jsListeners); return; } int i = 0; for (IT_TYPE it = list.begin(); it != list.end(); ++it, i++) { jsListeners->Set(JS_INT(i), Nan::New(*it)); } RET_VALUE(jsListeners); } private: static int _defaultMaxListeners; private: int _maxListeners; MAP_TYPE _listeners; MAP_TYPE _raw; FNMAP_TYPE _wrapped; }; #endif // _EVENT_EMITTER_