1 // ========================================================================== 2 // Project: SproutCore - JavaScript Application Framework 3 // Copyright: ©2006-2011 Strobe Inc. and contributors. 4 // Portions ©2008-2011 Apple Inc. All rights reserved. 5 // License: Licensed under MIT license (see license.js) 6 // ========================================================================== 7 8 // Create anonymous subclass of SC.RunLoop to add support for processing 9 // view queues and Timers. 10 SC.RunLoop = SC.RunLoop.extend( 11 /** @scope SC.RunLoop.prototype */ { 12 13 /** 14 The time the current run loop began executing. 15 16 All timers scheduled during this run loop will begin executing as if 17 they were scheduled at this time. 18 19 @type Number 20 */ 21 startTime: function() { 22 if (!this._start) { this._start = Date.now(); } 23 return this._start ; 24 }.property(), 25 26 /* 27 28 Override to fire and reschedule timers once per run loop. 29 30 Note that timers should fire only once per run loop to avoid the 31 situation where a timer might cause an infinite loop by constantly 32 rescheduling itself every time it is fired. 33 */ 34 endRunLoop: function() { 35 this.fireExpiredTimers(); // fire them timers! 36 var ret = sc_super(); // do everything else 37 this.scheduleNextTimeout(); // schedule a timeout if timers remain 38 return ret; 39 }, 40 41 // .......................................................... 42 // TIMER SUPPORT 43 // 44 45 /** 46 Schedules a timer to execute at the specified runTime. You will not 47 usually call this method directly. Instead you should work with SC.Timer, 48 which will manage both creating the timer and scheduling it. 49 50 Calling this method on a timer that is already scheduled will remove it 51 from the existing schedule and reschedule it. 52 53 @param {SC.Timer} timer the timer to schedule 54 @param {Time} runTime the time offset when you want this to run 55 @returns {SC.RunLoop} receiver 56 */ 57 scheduleTimer: function(timer, runTime) { 58 // if the timer is already in the schedule, remove it. 59 this._timerQueue = timer.removeFromTimerQueue(this._timerQueue); 60 61 // now, add the timer ot the timeout queue. This will walk down the 62 // chain of timers to find the right place to insert it. 63 this._timerQueue = timer.scheduleInTimerQueue(this._timerQueue, runTime); 64 return this ; 65 }, 66 67 /** 68 Removes the named timer from the timeout queue. If the timer is not 69 currently scheduled, this method will have no effect. 70 71 @param {SC.Timer} timer the timer to schedule 72 @returns {SC.RunLoop} receiver 73 */ 74 cancelTimer: function(timer) { 75 this._timerQueue = timer.removeFromTimerQueue(this._timerQueue) ; 76 return this ; 77 }, 78 79 /** @private - shared array used by fireExpiredTimers to avoid memory */ 80 TIMER_ARRAY: [], 81 82 /** 83 Invokes any timers that have expired since this method was last called. 84 Usually you will not call this method directly, but it will be invoked 85 automatically at the end of the run loop. 86 87 @returns {Boolean} YES if timers were fired, NO otherwise 88 */ 89 fireExpiredTimers: function() { 90 if (!this._timerQueue || this._firing) { return NO; } // nothing to do 91 92 // max time we are allowed to run timers 93 var now = this.get('startTime'), 94 timers = this.TIMER_ARRAY, 95 idx, len, didFire; 96 97 // avoid recursive calls 98 this._firing = YES; 99 100 // collect timers to fire. we do this one time up front to avoid infinite 101 // loops where firing a timer causes it to schedule itself again, causing 102 // it to fire again, etc. 103 this._timerQueue = this._timerQueue.collectExpiredTimers(timers, now); 104 105 // now step through timers and fire them. 106 len = timers.length; 107 for(idx=0;idx<len;idx++) { timers[idx].fire(); } 108 109 // cleanup 110 didFire = timers.length > 0 ; 111 timers.length = 0 ; // reset for later use... 112 this._firing = NO ; 113 return didFire; 114 }, 115 116 /** @private 117 Invoked at the end of a runloop, if there are pending timers, a timeout 118 will be scheduled to fire when the next timer expires. You will not 119 usually call this method yourself. It is invoked automatically at the 120 end of a run loop. 121 122 @returns {Boolean} YES if a timeout was scheduled 123 */ 124 scheduleNextTimeout: function() { 125 var ret = NO, 126 timer = this._timerQueue; 127 128 // if no timer, and there is an existing timeout, attempt to cancel it. 129 // NOTE: if this happens to be an invokeNext based timer, it will not be 130 // cancelled. 131 if (!timer) { 132 if (this._timerTimeout) { this.unscheduleRunLoop(); } 133 this._timerTimeout = null; 134 135 // otherwise, determine if the timeout needs to be rescheduled. 136 } else { 137 var nextTimeoutAt = timer._timerQueueRunTime ; 138 this._timerTimeout = this.scheduleRunLoop(nextTimeoutAt); 139 ret = YES; 140 } 141 142 return ret ; 143 } 144 145 }); 146 147 // Recreate the currentRunLoop with the new methods 148 SC.RunLoop.currentRunLoop = SC.RunLoop.create(); 149 SC.RunLoop.runLoopClass = SC.RunLoop; 150