/Foundation/CPTimer.j

http://github.com/cacaodev/cappuccino · Unknown · 309 lines · 254 code · 55 blank · 0 comment · 0 complexity · 12a574361a930e9e1af78176cf2e83d0 MD5 · raw file

  1. /*
  2. * CPTimer.j
  3. * Foundation
  4. *
  5. * Created by Nick Takayama.
  6. * Copyright 2008.
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public
  10. * License as published by the Free Software Foundation; either
  11. * version 2.1 of the License, or (at your option) any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * Lesser General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with this library; if not, write to the Free Software
  20. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  21. */
  22. @import "CPDate.j"
  23. @import "CPInvocation.j"
  24. @import "CPObject.j"
  25. @import "CPRunLoop.j"
  26. #define CPTimerDefaultTimeInterval 0.1
  27. /*!
  28. @class CPTimer
  29. @ingroup foundation
  30. @brief A timer object that can send a message after the given time interval.
  31. */
  32. @implementation CPTimer : CPObject
  33. {
  34. CPTimeInterval _timeInterval;
  35. CPInvocation _invocation;
  36. Function _callback;
  37. BOOL _repeats;
  38. BOOL _isValid;
  39. CPDate _fireDate;
  40. id _userInfo;
  41. }
  42. /*!
  43. Returns a new CPTimer object and adds it to the current CPRunLoop object in the default mode.
  44. */
  45. + (CPTimer)scheduledTimerWithTimeInterval:(CPTimeInterval)seconds invocation:(CPInvocation)anInvocation repeats:(BOOL)shouldRepeat
  46. {
  47. var timer = [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds invocation:anInvocation repeats:shouldRepeat];
  48. [[CPRunLoop currentRunLoop] addTimer:timer forMode:CPDefaultRunLoopMode];
  49. return timer;
  50. }
  51. /*!
  52. Returns a new CPTimer object and adds it to the current CPRunLoop object in the default mode.
  53. */
  54. + (CPTimer)scheduledTimerWithTimeInterval:(CPTimeInterval)seconds target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)shouldRepeat
  55. {
  56. var timer = [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds target:aTarget selector:aSelector userInfo:userInfo repeats:shouldRepeat];
  57. [[CPRunLoop currentRunLoop] addTimer:timer forMode:CPDefaultRunLoopMode];
  58. return timer;
  59. }
  60. /*!
  61. Returns a new CPTimer object and adds it to the current CPRunLoop object in the default mode.
  62. */
  63. + (CPTimer)scheduledTimerWithTimeInterval:(CPTimeInterval)seconds callback:(Function)aFunction repeats:(BOOL)shouldRepeat
  64. {
  65. var timer = [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds callback:aFunction repeats:shouldRepeat];
  66. [[CPRunLoop currentRunLoop] addTimer:timer forMode:CPDefaultRunLoopMode];
  67. return timer;
  68. }
  69. /*!
  70. Returns a new CPTimer that, when added to a run loop, will fire after seconds.
  71. */
  72. + (CPTimer)timerWithTimeInterval:(CPTimeInterval)seconds invocation:(CPInvocation)anInvocation repeats:(BOOL)shouldRepeat
  73. {
  74. return [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds invocation:anInvocation repeats:shouldRepeat];
  75. }
  76. /*!
  77. Returns a new CPTimer that, when added to a run loop, will fire after seconds.
  78. */
  79. + (CPTimer)timerWithTimeInterval:(CPTimeInterval)seconds target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)shouldRepeat
  80. {
  81. return [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds target:aTarget selector:aSelector userInfo:userInfo repeats:shouldRepeat];
  82. }
  83. /*!
  84. Returns a new CPTimer that, when added to a run loop, will fire after seconds.
  85. */
  86. + (CPTimer)timerWithTimeInterval:(CPTimeInterval)seconds callback:(Function)aFunction repeats:(BOOL)shouldRepeat
  87. {
  88. return [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds callback:aFunction repeats:shouldRepeat];
  89. }
  90. /*!
  91. Initializes a new CPTimer that, when added to a run loop, will fire at date and then, if repeats is YES, every seconds after that.
  92. */
  93. - (id)initWithFireDate:(CPDate)aDate interval:(CPTimeInterval)seconds invocation:(CPInvocation)anInvocation repeats:(BOOL)shouldRepeat
  94. {
  95. self = [super init];
  96. if (self)
  97. {
  98. _timeInterval = (seconds <= 0) ? CPTimerDefaultTimeInterval : seconds;
  99. _invocation = anInvocation;
  100. _repeats = shouldRepeat;
  101. _isValid = YES;
  102. _fireDate = aDate;
  103. }
  104. return self;
  105. }
  106. /*!
  107. Initializes a new CPTimer that, when added to a run loop, will fire at date and then, if repeats is YES, every seconds after that.
  108. */
  109. - (id)initWithFireDate:(CPDate)aDate interval:(CPTimeInterval)seconds target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)shouldRepeat
  110. {
  111. var invocation = [CPInvocation invocationWithMethodSignature:1];
  112. [invocation setTarget:aTarget];
  113. [invocation setSelector:aSelector];
  114. [invocation setArgument:self atIndex:2];
  115. self = [self initWithFireDate:aDate interval:seconds invocation:invocation repeats:shouldRepeat];
  116. if (self)
  117. _userInfo = userInfo;
  118. return self;
  119. }
  120. /*!
  121. Initializes a new CPTimer that, when added to a run loop, will fire at date and then, if repeats is YES, every seconds after that.
  122. */
  123. - (id)initWithFireDate:(CPDate)aDate interval:(CPTimeInterval)seconds callback:(Function)aFunction repeats:(BOOL)shouldRepeat
  124. {
  125. self = [super init];
  126. if (self)
  127. {
  128. _timeInterval = (seconds <= 0) ? CPTimerDefaultTimeInterval : seconds;
  129. _callback = aFunction;
  130. _repeats = shouldRepeat;
  131. _isValid = YES;
  132. _fireDate = aDate;
  133. }
  134. return self;
  135. }
  136. /*!
  137. Returns the receiver’s time interval.
  138. */
  139. - (CPTimeInterval)timeInterval
  140. {
  141. return _timeInterval;
  142. }
  143. /*!
  144. Returns the date at which the receiver will fire.
  145. */
  146. - (CPDate)fireDate
  147. {
  148. return _fireDate;
  149. }
  150. /*!
  151. Resets the receiver to fire next at a given date.
  152. */
  153. - (void)setFireDate:(CPDate)aDate
  154. {
  155. _fireDate = aDate;
  156. }
  157. /*!
  158. Causes the receiver’s message to be sent to its target.
  159. */
  160. - (void)fire
  161. {
  162. if (!_isValid)
  163. return;
  164. if (_callback)
  165. _callback();
  166. else
  167. [_invocation invoke];
  168. if (!_isValid)
  169. return;
  170. if (_repeats)
  171. _fireDate = [CPDate dateWithTimeIntervalSinceNow:_timeInterval];
  172. else
  173. [self invalidate];
  174. }
  175. /*!
  176. Returns a Boolean value that indicates whether the receiver is currently valid.
  177. */
  178. - (BOOL)isValid
  179. {
  180. return _isValid;
  181. }
  182. /*!
  183. Stops the receiver from ever firing again and requests its removal from its CPRunLoop object.
  184. */
  185. - (void)invalidate
  186. {
  187. _isValid = NO;
  188. _userInfo = nil;
  189. _invocation = nil;
  190. _callback = nil;
  191. }
  192. /*!
  193. Returns the receiver's userInfo object.
  194. */
  195. - (id)userInfo
  196. {
  197. return _userInfo;
  198. }
  199. @end
  200. var CPTimersTimeoutID = 1000,
  201. CPTimersForTimeoutIDs = {};
  202. var _CPTimerBridgeTimer = function(codeOrFunction, aDelay, shouldRepeat, functionArgs)
  203. {
  204. var timeoutID = CPTimersTimeoutID++,
  205. theFunction = nil;
  206. if (typeof codeOrFunction === "string")
  207. {
  208. theFunction = function()
  209. {
  210. new Function(codeOrFunction)();
  211. if (!shouldRepeat)
  212. CPTimersForTimeoutIDs[timeoutID] = nil;
  213. }
  214. }
  215. else
  216. {
  217. if (!functionArgs)
  218. functionArgs = [];
  219. theFunction = function()
  220. {
  221. codeOrFunction.apply(window, functionArgs);
  222. if (!shouldRepeat)
  223. CPTimersForTimeoutIDs[timeoutID] = nil;
  224. }
  225. }
  226. // A call such as setTimeout(f) is technically invalid but browsers seem to treat it as setTimeout(f, 0), so so will we.
  227. aDelay = aDelay | 0.0;
  228. CPTimersForTimeoutIDs[timeoutID] = [CPTimer scheduledTimerWithTimeInterval:aDelay / 1000 callback:theFunction repeats:shouldRepeat];
  229. return timeoutID;
  230. };
  231. // Avoid "TypeError: Result of expression 'window' [undefined] is not an object" when running unit tests.
  232. // We can't use a regular PLATFORM(DOM) check because that platform constant is not defined in Foundation.
  233. if (typeof(window) !== 'undefined')
  234. {
  235. window.setTimeout = function(codeOrFunction, aDelay)
  236. {
  237. return _CPTimerBridgeTimer(codeOrFunction, aDelay, NO, Array.prototype.slice.apply(arguments, [2]));
  238. };
  239. window.clearTimeout = function(aTimeoutID)
  240. {
  241. var timer = CPTimersForTimeoutIDs[aTimeoutID];
  242. if (timer)
  243. [timer invalidate];
  244. CPTimersForTimeoutIDs[aTimeoutID] = nil;
  245. };
  246. window.setInterval = function(codeOrFunction, aDelay, functionArgs)
  247. {
  248. return _CPTimerBridgeTimer(codeOrFunction, aDelay, YES, Array.prototype.slice.apply(arguments, [2]));
  249. };
  250. window.clearInterval = function(aTimeoutID)
  251. {
  252. window.clearTimeout(aTimeoutID);
  253. };
  254. }