emitter.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. var _ = require('./util')
  2. /**
  3. * Simple event emitter based on component/emitter.
  4. *
  5. * @constructor
  6. * @param {Object} ctx - the context to call listners with.
  7. */
  8. function Emitter (ctx) {
  9. this._cancelled = false
  10. this._ctx = ctx || this
  11. }
  12. var p = Emitter.prototype
  13. /**
  14. * Listen on the given `event` with `fn`.
  15. *
  16. * @param {String} event
  17. * @param {Function} fn
  18. * @return {Emitter}
  19. */
  20. p.on = function (event, fn) {
  21. this._cbs = this._cbs || {}
  22. ;(this._cbs[event] || (this._cbs[event] = []))
  23. .push(fn)
  24. return this
  25. }
  26. /**
  27. * Adds an `event` listener that will be invoked a single
  28. * time then automatically removed.
  29. *
  30. * @param {String} event
  31. * @param {Function} fn
  32. * @return {Emitter}
  33. */
  34. p.once = function (event, fn) {
  35. var self = this
  36. this._cbs = this._cbs || {}
  37. function on () {
  38. self.off(event, on)
  39. fn.apply(this, arguments)
  40. }
  41. on.fn = fn
  42. this.on(event, on)
  43. return this
  44. }
  45. /**
  46. * Remove the given callback for `event` or all
  47. * registered callbacks.
  48. *
  49. * @param {String} event
  50. * @param {Function} fn
  51. * @return {Emitter}
  52. */
  53. p.off = function (event, fn) {
  54. this._cbs = this._cbs || {}
  55. // all
  56. if (!arguments.length) {
  57. this._cbs = {}
  58. return this
  59. }
  60. // specific event
  61. var callbacks = this._cbs[event]
  62. if (!callbacks) return this
  63. // remove all handlers
  64. if (arguments.length === 1) {
  65. delete this._cbs[event]
  66. return this
  67. }
  68. // remove specific handler
  69. var cb
  70. for (var i = 0; i < callbacks.length; i++) {
  71. cb = callbacks[i]
  72. if (cb === fn || cb.fn === fn) {
  73. callbacks.splice(i, 1)
  74. break
  75. }
  76. }
  77. return this
  78. }
  79. /**
  80. * The internal, faster emit with fixed amount of arguments
  81. * using Function.call.
  82. *
  83. * @param {Object} event
  84. * @return {Emitter}
  85. */
  86. p.emit = function (event, a, b, c) {
  87. this._cbs = this._cbs || {}
  88. var callbacks = this._cbs[event]
  89. if (callbacks) {
  90. callbacks = _.toArray(callbacks)
  91. for (var i = 0, l = callbacks.length; i < l; i++) {
  92. callbacks[i].call(this._ctx, a, b, c)
  93. }
  94. }
  95. return this
  96. }
  97. /**
  98. * The external emit using Function.apply, used
  99. * by Vue instance event methods.
  100. *
  101. * @param {Object} event
  102. * @return {Emitter}
  103. */
  104. p.applyEmit = function (event) {
  105. this._cancelled = false
  106. this._cbs = this._cbs || {}
  107. var callbacks = this._cbs[event]
  108. if (callbacks) {
  109. // avoid leaking arguments:
  110. // http://jsperf.com/closure-with-arguments
  111. var i
  112. var l = arguments.length
  113. var args = new Array(l - 1)
  114. for (i = 1; i < l; i++) {
  115. args[i - 1] = arguments[i]
  116. }
  117. callbacks = _.toArray(callbacks)
  118. for (i = 0, l = callbacks.length; i < l; i++) {
  119. if (callbacks[i].apply(this._ctx, args) === false) {
  120. this._cancelled = true
  121. }
  122. }
  123. }
  124. return this
  125. }
  126. module.exports = Emitter