xevents.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. var log = require('hlogger').createLogger('xevents');
  2. class XPrefix {
  3. constructor(ctx,event,prefix) {
  4. this.ctx = ctx;
  5. this.event = event;
  6. this.prefix = prefix || "";
  7. }
  8. on(name,cb) {
  9. this.event.on(this.ctx,this.prefix + ":" + name, cb);
  10. return this;
  11. }
  12. }
  13. class XEvent {
  14. constructor(donecb,args) {
  15. this.count = 0;
  16. this.dowait = 0;
  17. this.done= donecb; // Callback
  18. this.errors = [];
  19. this.params = args;
  20. }
  21. wait() {
  22. this.dowait = 1;
  23. }
  24. }
  25. class XEventListener {
  26. constructor(ctx,name,cb,order) {
  27. this.ctx = ctx;
  28. this.name = name;
  29. this.callback = cb;
  30. this.order = order || 0; // Late call
  31. }
  32. }
  33. // Smarter event system?
  34. class XEventEmitter {
  35. constructor() {
  36. // Prepare this in a single context
  37. //this.listeners = new Map();
  38. this.listeners = [];
  39. this.auto = {};
  40. this.stat = {
  41. emitted:0,
  42. tried:0,
  43. called:0
  44. }
  45. }
  46. addListener(...args) {
  47. var ctx = this,evtName,cb,order;
  48. if(typeof(args[0]) == "string") {
  49. [evtName,cb,order] = args;
  50. } else {
  51. [ctx,evtName,cb,order] = args;
  52. }
  53. var listener = new XEventListener(ctx,evtName,cb,order);
  54. this.listeners.push(listener);
  55. //this.listeners.push(entry);
  56. // Process the autos
  57. var re = new RegExp(evtName);
  58. Object.keys(this.auto).map((v) => {
  59. var match = v.match(re);
  60. if(v.match(re)) {
  61. var tocall = [{match:match,listener:listener}];
  62. this.process(tocall,this.auto[v]); // No callback
  63. }
  64. });
  65. /* try {
  66. cb(this.auto[v], new XEvent());
  67. } catch(e) {
  68. console.log(e);
  69. }
  70. }
  71. });*/
  72. }
  73. // Remove listeners by ctx
  74. removeListener(ctx,name) {
  75. this.listeners = this.listeners.filter((v) => {
  76. if(v == ctx || v.ctx == ctx) {
  77. } else {
  78. return v;
  79. }
  80. });
  81. }
  82. // 1 arg only
  83. toggleOn(name,...args) {
  84. // Should be contextual if we remove context toggle will remain
  85. this.auto[name] = args; // should store array
  86. this.emit(name,...args);
  87. }
  88. toggleOff(name) {
  89. delete this.auto[name];
  90. }
  91. process(tocall,nargs,cb) {
  92. var self=this;
  93. var args = nargs || []; // or empty
  94. // Common for all for now
  95. var evt = new XEvent(next,args);
  96. this.stat.emitted++;
  97. // Sort
  98. tocall = tocall.sort((a,b) => {
  99. return (a.listener.order - b.listener.order)
  100. });
  101. // Get iter and setup evt
  102. var iter = tocall[Symbol.iterator]();
  103. function next() {
  104. process.nextTick(() => chain(iter));
  105. }
  106. // Internal thing
  107. function chain(iter) {
  108. // Stack overflow risk? maybe put this in process.tick();
  109. var v = iter.next();
  110. if(v.done) {
  111. if(cb != undefined) cb(evt); // Done callback with evt only
  112. return;
  113. }
  114. // Create evt here with some global event chaining iteraction
  115. try {
  116. var entry = v.value;
  117. evt.match = entry.match;
  118. evt.args = args;
  119. self.stat.called++;
  120. entry.listener.callback(...args,evt);
  121. evt.count++;
  122. }catch(e) {
  123. console.log(e);
  124. evt.errors.push(e);
  125. // Should we stop??
  126. }
  127. if(!evt.dowait) next();
  128. evt.dowait=0; // reset wait?
  129. }
  130. next();
  131. }
  132. search(name,ctx) {
  133. var ret = [];
  134. this.listeners.forEach((v) => {
  135. var re = new RegExp( "^" + v.name+ "$" );
  136. //var re = v.name;
  137. var matchres = name.match(re);
  138. if(matchres && (ctx == undefined || ctx == v.ctx) ) {
  139. ret.push({
  140. match: matchres,
  141. listener:v
  142. });
  143. }
  144. });
  145. return ret;
  146. }
  147. // Multiple argument
  148. // Should work like emit("event",1,2,3,4,5);
  149. emit(name,...args) {
  150. this.stat.tried++;
  151. var tocall = this.search(name);
  152. var evt,callback,dotrigger = 0;
  153. function trigger(ievt) {
  154. evt = ievt; // Will call
  155. if(callback)callback(evt);
  156. }
  157. // Mem leak???
  158. var ret = {
  159. done: function(cb) {
  160. if(evt) { cb(evt); }
  161. callback = cb;
  162. }
  163. }
  164. // Test
  165. this.process(tocall,args, trigger );
  166. return ret ;
  167. }
  168. // Why?
  169. /*emitTo(ctx,name,args) {
  170. var tocall = this.search(name,ctx)
  171. this.process(tocall,arg,cb);
  172. }*/
  173. // Alias
  174. on(ctx,match,cb) {
  175. this.addListener(ctx,match,cb);
  176. return this;
  177. }
  178. after(ctx,match,cb) {
  179. this.addListener(ctx,match,cb,1000);
  180. return this;
  181. }
  182. prefix(ctx,prefix) {
  183. return new XPrefix(ctx,this,prefix);
  184. }
  185. /*when(ctx,match,cb) {
  186. this.addListener(ctx,match,cb);
  187. }*/
  188. }
  189. module.exports=XEventEmitter;