PageRenderTime 103ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/ajax/libs/eventproxy/0.2.3/eventproxy.js

https://gitlab.com/Mirros/cdnjs
JavaScript | 513 lines | 270 code | 32 blank | 211 comment | 45 complexity | 9da8c69f588837d213b0e6735263c0f0 MD5 | raw file
  1. /*global exports */
  2. /*!
  3. * This file is used for define the EventProxy library.
  4. * @author <a href="mailto:shyvo1987@gmail.com">Jackson Tian</a>
  5. * @version 0.2.2
  6. */
  7. ;(function (name, definition) {
  8. // this is considered "safe":
  9. var hasDefine = typeof define === 'function',
  10. // hasDefine = typeof define === 'function',
  11. hasExports = typeof module !== 'undefined' && module.exports;
  12. if (hasDefine) {
  13. // AMD Module or CMD Module
  14. define(definition);
  15. } else if (hasExports) {
  16. // Node.js Module
  17. module.exports = definition();
  18. } else {
  19. // Assign to common namespaces or simply the global object (window)
  20. this[name] = definition();
  21. }
  22. })('EventProxy', function () {
  23. /**
  24. * EventProxy. An implementation of task/event based asynchronous pattern.
  25. * A module that can be mixed in to *any object* in order to provide it with custom events.
  26. * You may `bind` or `unbind` a callback function to an event;
  27. * `trigger`-ing an event fires all callbacks in succession.
  28. * Examples:
  29. * ```js
  30. * var render = function (template, resources) {};
  31. * var proxy = new EventProxy();
  32. * proxy.assign("template", "l10n", render);
  33. * proxy.trigger("template", template);
  34. * proxy.trigger("l10n", resources);
  35. * ```
  36. */
  37. var EventProxy = function () {
  38. if (!(this instanceof EventProxy)) {
  39. return new EventProxy();
  40. }
  41. this._callbacks = {};
  42. this._fired = {};
  43. };
  44. /**
  45. * Bind an event, specified by a string name, `ev`, to a `callback` function.
  46. * Passing `all` will bind the callback to all events fired.
  47. * @param {String} eventName Event name.
  48. * @param {Function} callback Callback.
  49. */
  50. EventProxy.prototype.addListener = function (ev, callback) {
  51. this._callbacks = this._callbacks || {};
  52. this._callbacks[ev] = this._callbacks[ev] || [];
  53. this._callbacks[ev].push(callback);
  54. return this;
  55. };
  56. /**
  57. * `addListener` alias, bind
  58. */
  59. EventProxy.prototype.bind = EventProxy.prototype.addListener;
  60. /**
  61. * `addListener` alias, on
  62. */
  63. EventProxy.prototype.on = EventProxy.prototype.addListener;
  64. /**
  65. * `addListener` alias, subscribe
  66. */
  67. EventProxy.prototype.subscribe = EventProxy.prototype.addListener;
  68. /**
  69. * Remove one or many callbacks. If `callback` is null, removes all callbacks for the event.
  70. * If `ev` is null, removes all bound callbacks
  71. * for all events.
  72. * @param {String} eventName Event name.
  73. * @param {Function} callback Callback.
  74. */
  75. EventProxy.prototype.removeListener = function (ev, callback) {
  76. var calls = this._callbacks, i, l;
  77. if (!ev) {
  78. this._callbacks = {};
  79. } else if (calls) {
  80. if (!callback) {
  81. calls[ev] = [];
  82. } else {
  83. var list = calls[ev];
  84. if (!list) {
  85. return this;
  86. }
  87. l = list.length;
  88. for (i = 0; i < l; i++) {
  89. if (callback === list[i]) {
  90. list[i] = null;
  91. break;
  92. }
  93. }
  94. }
  95. }
  96. return this;
  97. };
  98. /**
  99. * `removeListener` alias, unbind
  100. */
  101. EventProxy.prototype.unbind = EventProxy.prototype.removeListener;
  102. /**
  103. * Remove all listeners. It equals unbind()
  104. * Just add this API for as same as Event.Emitter.
  105. * @param {String} event Event name.
  106. */
  107. EventProxy.prototype.removeAllListeners = function (event) {
  108. return this.unbind(event);
  109. };
  110. /**
  111. * Trigger an event, firing all bound callbacks. Callbacks are passed the
  112. * same arguments as `trigger` is, apart from the event name.
  113. * Listening for `"all"` passes the true event name as the first argument.
  114. * @param {String} eventName Event name
  115. * @param {Mix} data Pass in data
  116. */
  117. EventProxy.prototype.trigger = function (eventName, data) {
  118. var list, ev, callback, args, i, l;
  119. var both = 2;
  120. var calls = this._callbacks;
  121. while (both--) {
  122. ev = both ? eventName : 'all';
  123. list = calls[ev];
  124. if (list) {
  125. for (i = 0, l = list.length; i < l; i++) {
  126. if (!(callback = list[i])) {
  127. list.splice(i, 1); i--; l--;
  128. } else {
  129. args = both ? Array.prototype.slice.call(arguments, 1) : arguments;
  130. callback.apply(this, args);
  131. }
  132. }
  133. }
  134. }
  135. return this;
  136. };
  137. /**
  138. * `trigger` alias
  139. */
  140. EventProxy.prototype.emit = EventProxy.prototype.trigger;
  141. /**
  142. * `trigger` alias
  143. */
  144. EventProxy.prototype.fire = EventProxy.prototype.trigger;
  145. /**
  146. * Bind an event like the bind method, but will remove the listener after it was fired.
  147. * @param {String} ev Event name
  148. * @param {Function} callback Callback
  149. */
  150. EventProxy.prototype.once = function (ev, callback) {
  151. var self = this;
  152. var wrapper = function () {
  153. callback.apply(self, arguments);
  154. self.unbind(ev, wrapper);
  155. };
  156. this.bind(ev, wrapper);
  157. return this;
  158. };
  159. /**
  160. * Bind an event, and trigger it immediately.
  161. * @param {String} ev Event name.
  162. * @param {Function} callback Callback.
  163. * @param {Mix} data The data that will be passed to calback as arguments.
  164. */
  165. EventProxy.prototype.immediate = function (ev, callback, data) {
  166. this.bind(ev, callback);
  167. this.trigger(ev, data);
  168. return this;
  169. };
  170. /**
  171. * `immediate` alias
  172. */
  173. EventProxy.prototype.asap = EventProxy.prototype.immediate;
  174. var _assign = function (eventname1, eventname2, cb, once) {
  175. var proxy = this, length, index = 0, argsLength = arguments.length,
  176. bind, _all,
  177. callback, events, isOnce, times = 0, flag = {};
  178. // Check the arguments length.
  179. if (argsLength < 3) {
  180. return this;
  181. }
  182. events = Array.prototype.slice.apply(arguments, [0, argsLength - 2]);
  183. callback = arguments[argsLength - 2];
  184. isOnce = arguments[argsLength - 1];
  185. // Check the callback type.
  186. if (typeof callback !== "function") {
  187. return this;
  188. }
  189. length = events.length;
  190. bind = function (key) {
  191. var method = isOnce ? "once" : "bind";
  192. proxy[method](key, function (data) {
  193. proxy._fired[key] = proxy._fired[key] || {};
  194. proxy._fired[key].data = data;
  195. if (!flag[key]) {
  196. flag[key] = true;
  197. times++;
  198. }
  199. });
  200. };
  201. for (index = 0; index < length; index++) {
  202. bind(events[index]);
  203. }
  204. _all = function (event) {
  205. if (times < length) {
  206. return;
  207. }
  208. if (!flag[event]) {
  209. return;
  210. }
  211. var data = [];
  212. for (index = 0; index < length; index++) {
  213. data.push(proxy._fired[events[index]].data);
  214. }
  215. if (isOnce) {
  216. proxy.unbind("all", _all);
  217. }
  218. callback.apply(null, data);
  219. };
  220. proxy.bind("all", _all);
  221. };
  222. /**
  223. * Assign some events, after all events were fired, the callback will be executed once.
  224. * Examples:
  225. * ```js
  226. * proxy.all(ev1, ev2, callback);
  227. * proxy.all([ev1, ev2], callback);
  228. * proxy.all(ev1, [ev2, ev3], callback);
  229. * ```
  230. * @param {String} eventName1 First event name.
  231. * @param {String} eventName2 Second event name.
  232. * @param {Function} callback Callback, that will be called after predefined events were fired.
  233. */
  234. EventProxy.prototype.all = function (eventname1, eventname2, callback) {
  235. var args = Array.prototype.concat.apply([], arguments);
  236. args.push(true);
  237. _assign.apply(this, args);
  238. return this;
  239. };
  240. /**
  241. * `all` alias
  242. */
  243. EventProxy.prototype.assign = EventProxy.prototype.all;
  244. /**
  245. * Assign the only one 'error' event handler.
  246. * @param {Function(err)} callback
  247. */
  248. EventProxy.prototype.fail = function (callback) {
  249. var that = this;
  250. that.once('error', function (err) {
  251. that.unbind();
  252. callback(err);
  253. });
  254. return this;
  255. };
  256. /**
  257. * Assign some events, after all events were fired, the callback will be executed first time.
  258. * Then any event that predefined be fired again, the callback will executed with the newest data.
  259. * Examples:
  260. * ```js
  261. * proxy.tail(ev1, ev2, callback);
  262. * proxy.tail([ev1, ev2], callback);
  263. * proxy.tail(ev1, [ev2, ev3], callback);
  264. * ```
  265. * @param {String} eventName1 First event name.
  266. * @param {String} eventName2 Second event name.
  267. * @param {Function} callback Callback, that will be called after predefined events were fired.
  268. */
  269. EventProxy.prototype.tail = function () {
  270. var args = Array.prototype.concat.apply([], arguments);
  271. args.push(false);
  272. _assign.apply(this, args);
  273. return this;
  274. };
  275. /**
  276. * `tail` alias, assignAll
  277. */
  278. EventProxy.prototype.assignAll = EventProxy.prototype.tail;
  279. /**
  280. * `tail` alias, assignAlways
  281. */
  282. EventProxy.prototype.assignAlways = EventProxy.prototype.tail;
  283. /**
  284. * The callback will be executed after the event be fired N times.
  285. * @param {String} eventName Event name.
  286. * @param {Number} times N times.
  287. * @param {Function} callback Callback, that will be called after event was fired N times.
  288. */
  289. EventProxy.prototype.after = function (eventName, times, callback) {
  290. if (times === 0) {
  291. callback.call(null, []);
  292. return this;
  293. }
  294. var proxy = this,
  295. firedData = [],
  296. all;
  297. this._after = this._after || {};
  298. var group = eventName + '_group';
  299. this._after[group] = {
  300. index: 0,
  301. results: []
  302. };
  303. all = function (name, data) {
  304. if (name === eventName) {
  305. times--;
  306. firedData.push(data);
  307. if (times < 1) {
  308. proxy.unbind("all", all);
  309. callback.apply(null, [firedData]);
  310. }
  311. }
  312. if (name === group) {
  313. times--;
  314. proxy._after[group].results[data.index] = data.result;
  315. if (times < 1) {
  316. proxy.unbind("all", all);
  317. callback.call(null, proxy._after[group].results);
  318. }
  319. }
  320. };
  321. proxy.bind("all", all);
  322. return this;
  323. };
  324. /**
  325. * The `after` method's helper. Use it will return ordered results.
  326. * If you need manipulate result, you need callback
  327. * Examples:
  328. * ```js
  329. * var ep = new EventProxy();
  330. * ep.after('file', files.length, function (list) {
  331. * // Ordered results
  332. * });
  333. * for (var i = 0; i < files.length; i++) {
  334. * fs.readFile(files[i], 'utf-8', ep.group('file'));
  335. * }
  336. * ```
  337. * @param {String} eventName Event name, shoule keep consistent with `after`.
  338. * @param {Function} callback Callback function, should return the final result.
  339. */
  340. EventProxy.prototype.group = function (eventName, callback) {
  341. var that = this;
  342. var group = eventName + '_group';
  343. var index = that._after[group].index;
  344. that._after[group].index++;
  345. return function (err, data) {
  346. if (err) {
  347. return that.emit('error', err);
  348. }
  349. that.emit(group, {
  350. index: index,
  351. result: callback ? callback(data) : data
  352. });
  353. };
  354. };
  355. /**
  356. * The callback will be executed after any registered event was fired. It only executed once.
  357. * @param {String} eventName1 Event name.
  358. * @param {String} eventName2 Event name.
  359. * @param {Function} callback The callback will get a map that has data and eventName attributes.
  360. */
  361. EventProxy.prototype.any = function () {
  362. var proxy = this,
  363. index,
  364. _bind,
  365. len = arguments.length,
  366. callback = arguments[len - 1],
  367. events = Array.prototype.slice.apply(arguments, [0, len - 1]),
  368. count = events.length,
  369. _eventName = events.join("_");
  370. proxy.once(_eventName, callback);
  371. _bind = function (key) {
  372. proxy.bind(key, function (data) {
  373. proxy.trigger(_eventName, {"data": data, eventName: key});
  374. });
  375. };
  376. for (index = 0; index < count; index++) {
  377. _bind(events[index]);
  378. }
  379. };
  380. /**
  381. * The callback will be executed when the event name not equals with assigned event.
  382. * @param {String} eventName Event name.
  383. * @param {Function} callback Callback.
  384. */
  385. EventProxy.prototype.not = function (eventName, callback) {
  386. var proxy = this;
  387. proxy.bind("all", function (name, data) {
  388. if (name !== eventName) {
  389. callback(data);
  390. }
  391. });
  392. };
  393. /**
  394. * Success callback wraper, will handler err for you.
  395. *
  396. * ```js
  397. * fs.readFile('foo.txt', ep.done('content'));
  398. *
  399. * // equal to =>
  400. *
  401. * fs.readFile('foo.txt', function (err, content) {
  402. * if (err) {
  403. * return ep.emit('error', err);
  404. * }
  405. * ep.emit('content', content);
  406. * });
  407. * ```
  408. *
  409. * ```js
  410. * fs.readFile('foo.txt', ep.done('content', function (content) {
  411. * return content.trim();
  412. * }));
  413. *
  414. * // equal to =>
  415. *
  416. * fs.readFile('foo.txt', function (err, content) {
  417. * if (err) {
  418. * return ep.emit('error', err);
  419. * }
  420. * ep.emit('content', content.trim());
  421. * });
  422. * ```
  423. * @param {Function|String} handler, success callback or event name will be emit after callback.
  424. * @return {Function}
  425. */
  426. EventProxy.prototype.done = function (handler, callback) {
  427. var that = this;
  428. return function (err, data) {
  429. if (err) {
  430. return that.emit('error', err);
  431. }
  432. if (typeof handler === 'string') {
  433. // getAsync(query, ep.done('query'));
  434. // or
  435. // getAsync(query, ep.done('query', function (data) {
  436. // return data.trim();
  437. // }));
  438. return that.emit(handler, callback ? callback.call(data) : data);
  439. }
  440. // speed improve for mostly case: `callback(err, data)`
  441. if (arguments.length <= 2) {
  442. return handler(data);
  443. }
  444. // callback(err, args1, args2, ...)
  445. var args = Array.prototype.slice.call(arguments, 1);
  446. handler.apply(null, args);
  447. };
  448. };
  449. /**
  450. * Create a new EventProxy
  451. * Examples:
  452. * ```js
  453. * var ep = EventProxy.create();
  454. * ep.assign('user', 'articles', function(user, articles) {
  455. * // do something...
  456. * });
  457. * // or one line ways: Create EventProxy and Assign
  458. * var ep = EventProxy.create('user', 'articles', function(user, articles) {
  459. * // do something...
  460. * });
  461. * ```
  462. * @returns {EventProxy} EventProxy instance
  463. */
  464. EventProxy.create = function () {
  465. var ep = new EventProxy();
  466. var args = Array.prototype.concat.apply([], arguments);
  467. if (args.length) {
  468. var errorHandler = args[args.length - 1];
  469. var callback = args[args.length - 2];
  470. if (typeof errorHandler === 'function' && typeof callback === 'function') {
  471. args.pop();
  472. ep.fail(errorHandler);
  473. }
  474. ep.assign.apply(ep, Array.prototype.slice.call(args));
  475. }
  476. return ep;
  477. };
  478. // Backwards compatibility
  479. EventProxy.EventProxy = EventProxy;
  480. return EventProxy;
  481. });