00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 @import "CPObject.j"
00024 @import "CPArray.j"
00025 @import "CPString.j"
00026
00031 CPDefaultRunLoopMode = @"CPDefaultRunLoopMode";
00032
00033 function _CPRunLoopPerformCompare(lhs, rhs)
00034 {
00035 return [rhs order] - [lhs order];
00036 }
00037
00038 var _CPRunLoopPerformPool = [],
00039 _CPRunLoopPerformPoolCapacity = 5;
00040
00041
00042 @implementation _CPRunLoopPerform : CPObject
00043 {
00044 id _target;
00045 SEL _selector;
00046 id _argument;
00047 unsigned _order;
00048 CPArray _runLoopModes;
00049 BOOL _isValid;
00050 }
00051
00052 + (void)_poolPerform:(_CPRunLoopPerform)aPerform
00053 {
00054 if (!aPerform || _CPRunLoopPerformPool.length >= _CPRunLoopPerformPoolCapacity)
00055 return;
00056
00057 _CPRunLoopPerformPool.push(aPerform);
00058 }
00059
00060 + (_CPRunLoopPerform)performWithSelector:(SEL)aSelector target:(id)aTarget argument:(id)anArgument order:(unsigned)anOrder modes:(CPArray)modes
00061 {
00062 if (_CPRunLoopPerformPool.length)
00063 {
00064 var perform = _CPRunLoopPerformPool.pop();
00065
00066 perform._target = aTarget;
00067 perform._selector = aSelector;
00068 perform._argument = anArgument;
00069 perform._order = anOrder;
00070 perform._runLoopModes = modes;
00071 perform._isValid = YES;
00072
00073 return perform;
00074 }
00075
00076 return [[self alloc] initWithSelector:aSelector target:aTarget argument:anArgument order:anOrder modes:modes];
00077 }
00078
00079 - (id)initWithSelector:(SEL)aSelector target:(SEL)aTarget argument:(id)anArgument order:(unsigned)anOrder modes:(CPArray)modes
00080 {
00081 self = [super init];
00082
00083 if (self)
00084 {
00085 _selector = aSelector;
00086 _target = aTarget;
00087 _argument = anArgument;
00088 _order = anOrder;
00089 _runLoopModes = modes;
00090 _isValid = YES;
00091 }
00092
00093 return self;
00094 }
00095
00096 - (SEL)selector
00097 {
00098 return _selector;
00099 }
00100
00101 - (id)target
00102 {
00103 return _target;
00104 }
00105
00106 - (id)argument
00107 {
00108 return _argument;
00109 }
00110
00111 - (unsigned)order
00112 {
00113 return _order;
00114 }
00115
00116 - (BOOL)fireInMode:(CPString)aRunLoopMode
00117 {
00118 if (!_isValid)
00119 return YES;
00120
00121 if ([_runLoopModes containsObject:aRunLoopMode])
00122 {
00123 [_target performSelector:_selector withObject:_argument];
00124
00125 return YES;
00126 }
00127
00128 return NO;
00129 }
00130
00131 - (void)invalidate
00132 {
00133 _isValid = NO;
00134 }
00135
00136 @end
00137
00138 var CPRunLoopLastNativeRunLoop = 0;
00139
00148 @implementation CPRunLoop : CPObject
00149 {
00150 BOOL _runLoopLock;
00151
00152 Object _timersForModes;
00153 Object _nativeTimersForModes;
00154 CPDate _nextTimerFireDatesForModes;
00155 BOOL _didAddTimer;
00156 CPDate _effectiveDate;
00157
00158 CPArray _orderedPerforms;
00159 }
00160
00161
00162
00163
00164 + (void)initialize
00165 {
00166 if (self != [CPRunLoop class])
00167 return;
00168
00169 CPMainRunLoop = [[CPRunLoop alloc] init];
00170 }
00171
00172 - (id)init
00173 {
00174 self = [super init];
00175
00176 if (self)
00177 {
00178 _orderedPerforms = [];
00179
00180 _timersForModes = {};
00181 _nativeTimersForModes = {};
00182 _nextTimerFireDatesForModes = {};
00183 }
00184
00185 return self;
00186 }
00187
00191 + (CPRunLoop)currentRunLoop
00192 {
00193 return CPMainRunLoop;
00194 }
00195
00199 + (CPRunLoop)mainRunLoop
00200 {
00201 return CPMainRunLoop;
00202 }
00203
00212 - (void)performSelector:(SEL)aSelector target:(id)aTarget argument:(id)anArgument order:(int)anOrder modes:(CPArray)modes
00213 {
00214 var perform = [_CPRunLoopPerform performWithSelector:aSelector target:aTarget argument:anArgument order:anOrder modes:modes],
00215 count = _orderedPerforms.length;
00216
00217
00218 while (count--)
00219 if (anOrder < [_orderedPerforms[count] order])
00220 break;
00221
00222 _orderedPerforms.splice(count + 1, 0, perform);
00223 }
00224
00231 - (void)cancelPerformSelector:(SEL)aSelector target:(id)aTarget argument:(id)anArgument
00232 {
00233 var count = _orderedPerforms.length;
00234
00235 while (count--)
00236 {
00237 var perform = _orderedPerforms[count];
00238
00239 if ([perform selector] === aSelector && [perform target] == aTarget && [perform argument] == anArgument)
00240 [_orderedPerforms[count] invalidate];
00241 }
00242 }
00243
00244
00245
00246
00247 - (void)performSelectors
00248 {
00249 [self limitDateForMode:CPDefaultRunLoopMode];
00250 }
00251
00255 - (void)addTimer:(CPTimer)aTimer forMode:(CPString)aMode
00256 {
00257
00258 if (_timersForModes[aMode])
00259 _timersForModes[aMode].push(aTimer);
00260 else
00261 _timersForModes[aMode] = [aTimer];
00262
00263 _didAddTimer = YES;
00264
00265 if (!aTimer._lastNativeRunLoopsForModes)
00266 aTimer._lastNativeRunLoopsForModes = {};
00267
00268 aTimer._lastNativeRunLoopsForModes[aMode] = CPRunLoopLastNativeRunLoop;
00269 }
00270
00274 - (CPDate)limitDateForMode:(CPString)aMode
00275 {
00276
00277 if (_runLoopLock)
00278 return;
00279
00280 _runLoopLock = YES;
00281
00282 var now = _effectiveDate ? [_effectiveDate laterDate:[CPDate date]] : [CPDate date],
00283 nextFireDate = nil,
00284 nextTimerFireDate = _nextTimerFireDatesForModes[aMode];
00285
00286
00287 if (_didAddTimer || nextTimerFireDate && nextTimerFireDate <= now)
00288 {
00289 _didAddTimer = NO;
00290
00291
00292 if (_nativeTimersForModes[aMode] !== nil)
00293 {
00294 window.clearNativeTimeout(_nativeTimersForModes[aMode]);
00295
00296 _nativeTimersForModes[aMode] = nil;
00297 }
00298
00299
00300 var timers = _timersForModes[aMode],
00301 index = timers.length;
00302
00303 _timersForModes[aMode] = nil;
00304
00305
00306 while (index--)
00307 {
00308 var timer = timers[index];
00309
00310 if (timer._lastNativeRunLoopsForModes[aMode] < CPRunLoopLastNativeRunLoop && timer._isValid && timer._fireDate <= now)
00311 [timer fire];
00312
00313
00314 if (timer._isValid)
00315 nextFireDate = (nextFireDate === nil) ? timer._fireDate : [nextFireDate earlierDate:timer._fireDate];
00316
00317 else
00318 {
00319
00320 timer._lastNativeRunLoopsForModes[aMode] = 0;
00321
00322 timers.splice(index, 1);
00323 }
00324 }
00325
00326
00327
00328
00329 var newTimers = _timersForModes[aMode];
00330
00331 if (newTimers && newTimers.length)
00332 {
00333 index = newTimers.length;
00334
00335 while (index--)
00336 {
00337 var timer = newTimers[index];
00338
00339 if ([timer isValid])
00340 nextFireDate = (nextFireDate === nil) ? timer._fireDate : [nextFireDate earlierDate:timer._fireDate];
00341 else
00342 newTimers.splice(index, 1);
00343 }
00344
00345 _timersForModes[aMode] = newTimers.concat(timers);
00346 }
00347 else
00348 _timersForModes[aMode] = timers;
00349
00350 _nextTimerFireDatesForModes[aMode] = nextFireDate;
00351
00352
00353 if (_nextTimerFireDatesForModes[aMode] !== nil)
00354 _nativeTimersForModes[aMode] = window.setNativeTimeout(function() { _effectiveDate = nextFireDate; _nativeTimersForModes[aMode] = nil; ++CPRunLoopLastNativeRunLoop; [self limitDateForMode:aMode]; _effectiveDate = nil; }, MAX(0, [nextFireDate timeIntervalSinceNow] * 1000));
00355 }
00356
00357
00358 var performs = _orderedPerforms,
00359 index = performs.length;
00360
00361 _orderedPerforms = [];
00362
00363 while (index--)
00364 {
00365 var perform = performs[index];
00366
00367 if ([perform fireInMode:CPDefaultRunLoopMode])
00368 {
00369 [_CPRunLoopPerform _poolPerform:perform];
00370
00371 performs.splice(index, 1);
00372 }
00373 }
00374
00375 if (_orderedPerforms.length)
00376 {
00377 _orderedPerforms = _orderedPerforms.concat(performs);
00378 _orderedPerforms.sort(_CPRunLoopPerformCompare);
00379 }
00380 else
00381 _orderedPerforms = performs;
00382
00383 _runLoopLock = NO;
00384
00385 return nextFireDate;
00386 }
00387
00388 @end