callbacks.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. define( [
  2. "./core",
  3. "./var/rnotwhite"
  4. ], function( jQuery, rnotwhite ) {
  5. "use strict";
  6. // Convert String-formatted options into Object-formatted ones
  7. function createOptions( options ) {
  8. var object = {};
  9. jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
  10. object[ flag ] = true;
  11. } );
  12. return object;
  13. }
  14. /*
  15. * Create a callback list using the following parameters:
  16. *
  17. * options: an optional list of space-separated options that will change how
  18. * the callback list behaves or a more traditional option object
  19. *
  20. * By default a callback list will act like an event callback list and can be
  21. * "fired" multiple times.
  22. *
  23. * Possible options:
  24. *
  25. * once: will ensure the callback list can only be fired once (like a Deferred)
  26. *
  27. * memory: will keep track of previous values and will call any callback added
  28. * after the list has been fired right away with the latest "memorized"
  29. * values (like a Deferred)
  30. *
  31. * unique: will ensure a callback can only be added once (no duplicate in the list)
  32. *
  33. * stopOnFalse: interrupt callings when a callback returns false
  34. *
  35. */
  36. jQuery.Callbacks = function( options ) {
  37. // Convert options from String-formatted to Object-formatted if needed
  38. // (we check in cache first)
  39. options = typeof options === "string" ?
  40. createOptions( options ) :
  41. jQuery.extend( {}, options );
  42. var // Flag to know if list is currently firing
  43. firing,
  44. // Last fire value for non-forgettable lists
  45. memory,
  46. // Flag to know if list was already fired
  47. fired,
  48. // Flag to prevent firing
  49. locked,
  50. // Actual callback list
  51. list = [],
  52. // Queue of execution data for repeatable lists
  53. queue = [],
  54. // Index of currently firing callback (modified by add/remove as needed)
  55. firingIndex = -1,
  56. // Fire callbacks
  57. fire = function() {
  58. // Enforce single-firing
  59. locked = options.once;
  60. // Execute callbacks for all pending executions,
  61. // respecting firingIndex overrides and runtime changes
  62. fired = firing = true;
  63. for ( ; queue.length; firingIndex = -1 ) {
  64. memory = queue.shift();
  65. while ( ++firingIndex < list.length ) {
  66. // Run callback and check for early termination
  67. if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
  68. options.stopOnFalse ) {
  69. // Jump to end and forget the data so .add doesn't re-fire
  70. firingIndex = list.length;
  71. memory = false;
  72. }
  73. }
  74. }
  75. // Forget the data if we're done with it
  76. if ( !options.memory ) {
  77. memory = false;
  78. }
  79. firing = false;
  80. // Clean up if we're done firing for good
  81. if ( locked ) {
  82. // Keep an empty list if we have data for future add calls
  83. if ( memory ) {
  84. list = [];
  85. // Otherwise, this object is spent
  86. } else {
  87. list = "";
  88. }
  89. }
  90. },
  91. // Actual Callbacks object
  92. self = {
  93. // Add a callback or a collection of callbacks to the list
  94. add: function() {
  95. if ( list ) {
  96. // If we have memory from a past run, we should fire after adding
  97. if ( memory && !firing ) {
  98. firingIndex = list.length - 1;
  99. queue.push( memory );
  100. }
  101. ( function add( args ) {
  102. jQuery.each( args, function( _, arg ) {
  103. if ( jQuery.isFunction( arg ) ) {
  104. if ( !options.unique || !self.has( arg ) ) {
  105. list.push( arg );
  106. }
  107. } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) {
  108. // Inspect recursively
  109. add( arg );
  110. }
  111. } );
  112. } )( arguments );
  113. if ( memory && !firing ) {
  114. fire();
  115. }
  116. }
  117. return this;
  118. },
  119. // Remove a callback from the list
  120. remove: function() {
  121. jQuery.each( arguments, function( _, arg ) {
  122. var index;
  123. while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
  124. list.splice( index, 1 );
  125. // Handle firing indexes
  126. if ( index <= firingIndex ) {
  127. firingIndex--;
  128. }
  129. }
  130. } );
  131. return this;
  132. },
  133. // Check if a given callback is in the list.
  134. // If no argument is given, return whether or not list has callbacks attached.
  135. has: function( fn ) {
  136. return fn ?
  137. jQuery.inArray( fn, list ) > -1 :
  138. list.length > 0;
  139. },
  140. // Remove all callbacks from the list
  141. empty: function() {
  142. if ( list ) {
  143. list = [];
  144. }
  145. return this;
  146. },
  147. // Disable .fire and .add
  148. // Abort any current/pending executions
  149. // Clear all callbacks and values
  150. disable: function() {
  151. locked = queue = [];
  152. list = memory = "";
  153. return this;
  154. },
  155. disabled: function() {
  156. return !list;
  157. },
  158. // Disable .fire
  159. // Also disable .add unless we have memory (since it would have no effect)
  160. // Abort any pending executions
  161. lock: function() {
  162. locked = queue = [];
  163. if ( !memory && !firing ) {
  164. list = memory = "";
  165. }
  166. return this;
  167. },
  168. locked: function() {
  169. return !!locked;
  170. },
  171. // Call all callbacks with the given context and arguments
  172. fireWith: function( context, args ) {
  173. if ( !locked ) {
  174. args = args || [];
  175. args = [ context, args.slice ? args.slice() : args ];
  176. queue.push( args );
  177. if ( !firing ) {
  178. fire();
  179. }
  180. }
  181. return this;
  182. },
  183. // Call all the callbacks with the given arguments
  184. fire: function() {
  185. self.fireWith( this, arguments );
  186. return this;
  187. },
  188. // To know if the callbacks have already been called at least once
  189. fired: function() {
  190. return !!fired;
  191. }
  192. };
  193. return self;
  194. };
  195. return jQuery;
  196. } );