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
00149 @implementation CPRunLoop : CPObject
00150 {
00151 BOOL _runLoopLock;
00152
00153 Object _timersForModes;
00154 Object _nativeTimersForModes;
00155 CPDate _nextTimerFireDatesForModes;
00156 BOOL _didAddTimer;
00157 CPDate _effectiveDate;
00158
00159 CPArray _orderedPerforms;
00160 }
00161
00162
00163
00164
00165 + (void)initialize
00166 {
00167 if (self != [CPRunLoop class])
00168 return;
00169
00170 CPMainRunLoop = [[CPRunLoop alloc] init];
00171 }
00172
00173 - (id)init
00174 {
00175 self = [super init];
00176
00177 if (self)
00178 {
00179 _orderedPerforms = [];
00180
00181 _timersForModes = {};
00182 _nativeTimersForModes = {};
00183 _nextTimerFireDatesForModes = {};
00184 }
00185
00186 return self;
00187 }
00188
00192 + (CPRunLoop)currentRunLoop
00193 {
00194 return CPMainRunLoop;
00195 }
00196
00200 + (CPRunLoop)mainRunLoop
00201 {
00202 return CPMainRunLoop;
00203 }
00204
00213 - (void)performSelector:(SEL)aSelector target:(id)aTarget argument:(id)anArgument order:(int)anOrder modes:(CPArray)modes
00214 {
00215 var perform = [_CPRunLoopPerform performWithSelector:aSelector target:aTarget argument:anArgument order:anOrder modes:modes],
00216 count = _orderedPerforms.length;
00217
00218
00219 while (count--)
00220 if (anOrder < [_orderedPerforms[count] order])
00221 break;
00222
00223 _orderedPerforms.splice(count + 1, 0, perform);
00224 }
00225
00232 - (void)cancelPerformSelector:(SEL)aSelector target:(id)aTarget argument:(id)anArgument
00233 {
00234 var count = _orderedPerforms.length;
00235
00236 while (count--)
00237 {
00238 var perform = _orderedPerforms[count];
00239
00240 if ([perform selector] === aSelector && [perform target] == aTarget && [perform argument] == anArgument)
00241 [_orderedPerforms[count] invalidate];
00242 }
00243 }
00244
00245
00246
00247
00248 - (void)performSelectors
00249 {
00250 [self limitDateForMode:CPDefaultRunLoopMode];
00251 }
00252
00256 - (void)addTimer:(CPTimer)aTimer forMode:(CPString)aMode
00257 {
00258
00259 if (_timersForModes[aMode])
00260 _timersForModes[aMode].push(aTimer);
00261 else
00262 _timersForModes[aMode] = [aTimer];
00263
00264 _didAddTimer = YES;
00265
00266 if (!aTimer._lastNativeRunLoopsForModes)
00267 aTimer._lastNativeRunLoopsForModes = {};
00268
00269 aTimer._lastNativeRunLoopsForModes[aMode] = CPRunLoopLastNativeRunLoop;
00270 }
00271
00275 - (CPDate)limitDateForMode:(CPString)aMode
00276 {
00277
00278 if (_runLoopLock)
00279 return;
00280
00281 _runLoopLock = YES;
00282
00283 var now = _effectiveDate ? [_effectiveDate laterDate:[CPDate date]] : [CPDate date],
00284 nextFireDate = nil,
00285 nextTimerFireDate = _nextTimerFireDatesForModes[aMode];
00286
00287
00288 if (_didAddTimer || nextTimerFireDate && nextTimerFireDate <= now)
00289 {
00290 _didAddTimer = NO;
00291
00292
00293 if (_nativeTimersForModes[aMode] !== nil)
00294 {
00295 window.clearNativeTimeout(_nativeTimersForModes[aMode]);
00296
00297 _nativeTimersForModes[aMode] = nil;
00298 }
00299
00300
00301 var timers = _timersForModes[aMode],
00302 index = timers.length;
00303
00304 _timersForModes[aMode] = nil;
00305
00306
00307 while (index--)
00308 {
00309 var timer = timers[index];
00310
00311 if (timer._lastNativeRunLoopsForModes[aMode] < CPRunLoopLastNativeRunLoop && timer._isValid && timer._fireDate <= now)
00312 [timer fire];
00313
00314
00315 if (timer._isValid)
00316 nextFireDate = (nextFireDate === nil) ? timer._fireDate : [nextFireDate earlierDate:timer._fireDate];
00317
00318 else
00319 {
00320
00321 timer._lastNativeRunLoopsForModes[aMode] = 0;
00322
00323 timers.splice(index, 1);
00324 }
00325 }
00326
00327
00328
00329
00330 var newTimers = _timersForModes[aMode];
00331
00332 if (newTimers && newTimers.length)
00333 {
00334 index = newTimers.length;
00335
00336 while (index--)
00337 {
00338 var timer = newTimers[index];
00339
00340 if ([timer isValid])
00341 nextFireDate = (nextFireDate === nil) ? timer._fireDate : [nextFireDate earlierDate:timer._fireDate];
00342 else
00343 newTimers.splice(index, 1);
00344 }
00345
00346 _timersForModes[aMode] = newTimers.concat(timers);
00347 }
00348 else
00349 _timersForModes[aMode] = timers;
00350
00351 _nextTimerFireDatesForModes[aMode] = nextFireDate;
00352
00353
00354 if (_nextTimerFireDatesForModes[aMode] !== nil)
00355 _nativeTimersForModes[aMode] = window.setNativeTimeout(function() { _effectiveDate = nextFireDate; _nativeTimersForModes[aMode] = nil; ++CPRunLoopLastNativeRunLoop; [self limitDateForMode:aMode]; _effectiveDate = nil; }, MAX(0, [nextFireDate timeIntervalSinceNow] * 1000));
00356 }
00357
00358
00359 var performs = _orderedPerforms,
00360 index = performs.length;
00361
00362 _orderedPerforms = [];
00363
00364 while (index--)
00365 {
00366 var perform = performs[index];
00367
00368 if ([perform fireInMode:CPDefaultRunLoopMode])
00369 {
00370 [_CPRunLoopPerform _poolPerform:perform];
00371
00372 performs.splice(index, 1);
00373 }
00374 }
00375
00376 if (_orderedPerforms.length)
00377 {
00378 _orderedPerforms = _orderedPerforms.concat(performs);
00379 _orderedPerforms.sort(_CPRunLoopPerformCompare);
00380 }
00381 else
00382 _orderedPerforms = performs;
00383
00384 _runLoopLock = NO;
00385
00386 return nextFireDate;
00387 }
00388
00389 @end