#ifndef _EVENT_EMITTER_ #define _EVENT_EMITTER_ #include #include #include #include #define THIS_EMITTER \ EventEmitter *emitter = ObjectWrap::Unwrap(info.This()); // Use the dummy template for static-int initialization template< class Dummy > class EventEmitterStatics { protected: static int _defaultMaxListeners; }; template< class Dummy > int EventEmitterStatics::_defaultMaxListeners = 10; class EventEmitter : public Nan::ObjectWrap, public EventEmitterStatics { 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; typedef FNMAP_TYPE::iterator FNMAP_IT_TYPE; public: // C++ side emit() method 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); } } } // C++ side on() method void on(const std::string &name, v8::Local that, const std::string &method) { v8::Local code = JS_STR( "((emitter, name, that, method) => emitter.on(name, that[method]))" ); v8::Local connector = v8::Local::Cast(v8::Script::Compile(code)->Run()); Nan::Callback connectorCb(connector); v8::Local emitter = Nan::New(); this->Wrap(emitter); v8::Local argv[] = { emitter, JS_STR(name.c_str()), that, JS_STR(method.c_str()) }; connectorCb.Call(4, argv); } protected: EventEmitter () { _maxListeners = _defaultMaxListeners; _freeId = 0; } ~EventEmitter () {} static void extend(v8::Local &ctor) { // -------- 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); // -------- static v8::Local ctorFunc = Nan::GetFunction(ctor).ToLocalChecked(); v8::Local ctorObj = ctorFunc; Nan::SetMethod(ctorFunc, "listenerCount", jsStaticListenerCount); ACCESSOR_RW(ctorObj, defaultMaxListeners); //Local proto = ctor->PrototypeTemplate(); // ACCESSOR_RW(proto, type); } // Deprecated static method static NAN_METHOD(jsStaticListenerCount) { REQ_OBJ_ARG(0, obj); EventEmitter *emitter = ObjectWrap::Unwrap(obj); REQ_UTF8_ARG(1, name); const VEC_TYPE &list = emitter->_listeners[*name]; RET_VALUE(JS_INT(static_cast(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; std::cout << "emit 0 " << emitter->_listeners.size() << std::endl; for (MAP_IT_TYPE it = emitter->_listeners.begin(); it != emitter->_listeners.end(); ++it) { std::cout << "emit 0 _listeners " << it->first << std::endl; } for (MAP_IT_TYPE it = emitter->_raw.begin(); it != emitter->_raw.end(); ++it) { std::cout << "emit 0 _raw " << it->first << std::endl; } REQ_UTF8_ARG(0, name); int length = info.Length(); std::vector< v8::Local > args; std::cout << "emit 1 " << emitter->_listeners.size() << std::endl; for (int i = 1; i < length; i++) { args.push_back(info[i]); } std::cout << "emit 2 " << emitter->_listeners.size() << std::endl; emitter->emit(*name, length - 1, &args[0]); std::cout << "emit 3 " << emitter->_listeners.size() << std::endl; } static NAN_METHOD(jsEventNames) { THIS_EMITTER; std::cout << "jsEventNames 0 " << emitter->_raw.size() << std::endl; v8::Local jsNames = Nan::New(emitter->_raw.size()); if (emitter->_raw.empty()) { RET_VALUE(jsNames); return; } std::cout << "jsEventNames 1 " << emitter->_raw.size() << std::endl; int i = 0; for (MAP_IT_TYPE it = emitter->_raw.begin(); it != emitter->_raw.end(); ++it, i++) { jsNames->Set(JS_INT(i), JS_STR(it->first)); } std::cout << "jsEventNames 2 " << emitter->_raw.size() << std::endl; 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(static_cast(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; v8::Local args[] = { info[0], info[1] }; emitter->emit("newListener", 2, args); 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; v8::Local args[] = { info[0], info[1] }; emitter->emit("newListener", 2, args); 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); } int nextId = emitter->_freeId++; emitter->_wrappedIds[nextId] = cb; emitter->_rawIds[nextId] = raw; } 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, name, cb) => (...args) => {\n\ cb(...args);\n\ emitter.removeListener(name, cb);\n\ })" ); v8::Local decor = v8::Local::Cast(v8::Script::Compile(code)->Run()); Nan::Callback decorCb(decor); v8::Local argv[] = { info.This(), info[0], raw }; v8::Local wrap = v8::Local::Cast(decorCb.Call(3, 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()) { MAP_TYPE tmpMap = emitter->_raw; emitter->_listeners.clear(); emitter->_raw.clear(); emitter->_wrappedIds.clear(); emitter->_rawIds.clear(); for (MAP_IT_TYPE itMap = tmpMap.begin(); itMap != tmpMap.end(); ++itMap) { const std::string ¤t = itMap->first; VEC_TYPE &list = itMap->second; for (IT_TYPE it = list.begin(); it != list.end(); ++it) { v8::Local args[] = { JS_STR(current.c_str()), Nan::New(*it) }; emitter->emit("removeListener", 2, args); } } return; } REQ_UTF8_ARG(0, n); std::string name = std::string(*n); VEC_TYPE &list = emitter->_raw[name]; if (list.empty()) { return; } if (emitter->_rawIds.size()) { std::vector removes; for (IT_TYPE it = list.begin(); it != list.end(); ++it) { FN_TYPE fn = *it; for (FNMAP_IT_TYPE itRaw = emitter->_rawIds.begin(); itRaw != emitter->_rawIds.end(); ++itRaw) { if (fn == itRaw->second) { removes.push_back(itRaw->first); } } } if (removes.size()) { for (std::vector::const_iterator it = removes.begin(); it != removes.end(); ++it) { emitter->_wrappedIds.erase(*it); emitter->_rawIds.erase(*it); } } } VEC_TYPE tmpVec = emitter->_raw[name]; emitter->_listeners[name].clear(); emitter->_raw[name].clear(); for (IT_TYPE it = tmpVec.begin(); it != tmpVec.end(); ++it) { v8::Local args[] = { JS_STR(name.c_str()), Nan::New(*it) }; emitter->emit("removeListener", 2, args); } } static NAN_METHOD(jsRemoveListener) { THIS_EMITTER; REQ_UTF8_ARG(0, n); REQ_FUN_ARG(1, raw); Nan::Persistent persistentRaw; persistentRaw.Reset(raw); std::string name = std::string(*n); VEC_TYPE &rawList = emitter->_raw[name]; if (rawList.empty()) { return; } v8::Local args[] = { info[0], info[1] }; for (IT_TYPE it = rawList.begin(); it != rawList.end(); ++it) { if (*it == persistentRaw) { rawList.erase(it); if (rawList.empty()) { emitter->_raw.erase(name); } break; } } VEC_TYPE &wrapList = emitter->_listeners[name]; if (emitter->_wrappedIds.size() == 0) { for (IT_TYPE it = wrapList.begin(); it != wrapList.end(); ++it) { if (*it == persistentRaw) { wrapList.erase(it); if (wrapList.empty()) { emitter->_listeners.erase(name); } break; } } emitter->emit("removeListener", 2, args); return; } for (FNMAP_IT_TYPE itRaw = emitter->_rawIds.begin(); itRaw != emitter->_rawIds.end(); ++itRaw) { if (persistentRaw == itRaw->second) { FN_TYPE fn = emitter->_wrappedIds[itRaw->first]; for (IT_TYPE it = wrapList.begin(); it != wrapList.end(); ++it) { if (*it == fn) { wrapList.erase(it); if (wrapList.empty()) { emitter->_listeners.erase(name); } break; } } emitter->_wrappedIds.erase(itRaw->first); emitter->_rawIds.erase(itRaw->first); break; } } emitter->emit("removeListener", 2, args); } 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]; 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); } private: int _maxListeners; MAP_TYPE _listeners; MAP_TYPE _raw; int _freeId; FNMAP_TYPE _wrappedIds; FNMAP_TYPE _rawIds; }; #endif // _EVENT_EMITTER_