callbacks.js 5.4 KB

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