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