Config
Table of Contents

Class

O.RunLoop

The run loop allows data to propagate through the app in stages, preventing multiple changes to an object firing off the same observers several times. To use, wrap the entry point functions in a call to O.RunLoop.invoke.

/*global setTimeout, clearTimeout, setImmediate, console */

"use strict";

( function ( NS, win, setImmediate ) {

var requestAnimFrame =
 win.requestAnimationFrame       ||
 win.oRequestAnimationFrame      ||
 win.webkitRequestAnimationFrame ||
 win.mozRequestAnimationFrame    ||
 win.msRequestAnimationFrame     ||
 ( function () {
   var lastTime = 0;
   return function ( callback ) {
     var time = Date.now(),
       timeToNextCall = Math.max( 0, 16 - ( time - lastTime ) );
       lastTime = time;
     win.setTimeout( function () {
       callback( time + timeToNextCall );
     }, timeToNextCall );
   };
 }() );

var Timeout = function ( time, period, fn, bind ) {
 this.time = time;
 this.period = period;
 this.fn = fn;
 this.bind = bind;
};


var RunLoop = {

 mayRedraw: false,

Private Property

NS.RunLoop._queueOrder

  • String[]
  • private

The order in which to flush the queues.

_queueOrder: [ 'before', 'bindings', 'middle', 'render', 'after' ],

Private Property

NS.RunLoop._queues

  • Object
  • private

Collection of queues. Each queue contains [fn, bind] tuples to call at <O.RunLoop.end>.

_queues: {
   before: [],
   bindings: [],
   middle: [],
   render: [],
   after: [],
   nextLoop: [],
   nextFrame: []
 },

Private Property

NS.RunLoop._timeouts

  • O.Heap
  • private

A priority queue of timeouts.

_timeouts: new NS.Heap( function ( a, b ) {
   return a.time - b.time;
 }),

Private Property

NS.RunLoop._nextTimeout

  • Number
  • private

Epoch time that the next browser timeout is scheduled for.

_nextTimeout: 0,

Private Property

NS.RunLoop._timer

  • Number
  • private

The browser timer id (response from setTimeout), which you need if you want to cancel the timeout.

_timer: null,

Private Property

NS.RunLoop._depth

  • Number
  • private

Number of calls to O.RunLoop.invoke currently in stack.

_depth: 0,

Method

O.RunLoop.flushQueue( queue )

Invokes each function in an array of [function, object] tuples, binding the this parameter of the function to the object, and empties the array.

Parameters

queueString name of the queue to flush.

Returns

Boolean Were any functions actually invoked?

flushQueue: function ( queue ) {
   var toInvoke = this._queues[ queue ],
     l = toInvoke.length,
     i, tuple, fn, bind;

   if ( l ) {
     this._queues[ queue ] = [];

     for ( i = 0; i < l; i += 1 ) {
       tuple = toInvoke[i];
       fn = tuple[0];
       bind = tuple[1];
       try {
         if ( bind ) {
           fn.call( bind );
         } else {
           fn();
         }
       }
       catch ( error ) {
         RunLoop.didError( error );
       }
     }
     return true;
   }
   return false;
 },

Method

O.RunLoop.flushAllQueues( queue )

Calls O.RunLoop#flushQueue on each queue in the order specified in _queueOrder, starting at the first queue again whenever the queue indicates something has changed.

Parameters

queueString name of the queue to flush.

Returns

Boolean Were any functions actually invoked?

flushAllQueues: function () {
   var order = this._queueOrder,
     i = 0, l = order.length;
   while ( i < l ) {
     // Render waits for next frame, except if in bg, since
     // animation frames don't fire while in the background
     // and we want to flush queues in a reasonable time, as they may
     // redraw the tab name, favicon etc.
     if ( i === 3 && !this.mayRedraw && !document.hidden ) {
       this.invokeInNextFrame( this.flushAllQueues, this );
       return;
     }
     i = this.flushQueue( order[i] ) ? 0 : i + 1;
   }
 },

Method

O.RunLoop.queueFn( queue, fn, bind, allowDups )

Add a [function, object] tuple to a queue, ensuring it is not added again if it is already there.

Parameters

queueString The name of the queue to add the tuple to.
fnFunction The function to add to the array.
bind(Object|undefined) The object the function will be bound to when called.
allowDupsBoolean Optional If not true, will search queue to check this fn/bind combination is not already present.

Returns

O.RunLoop Returns self.

queueFn: function ( queue, fn, bind, allowDups ) {
   var toInvoke = this._queues[ queue ],
     l = toInvoke.length,
     i, tuple;
   // Log error here, as the stack trace is useless inside flushQueue.
   if ( !fn ) {
     try {
       fn();
     } catch ( error ) {
       RunLoop.didError( error );
     }
   } else {
     if ( !allowDups ) {
       for ( i = 0; i < l; i += 1 ) {
         tuple = toInvoke[i];
         if ( tuple[0] === fn && tuple[1] === bind ) {
           return this;
         }
       }
     }
     toInvoke[l] = [ fn, bind ];
   }
   return this;
 },

Method

O.RunLoop.invoke( fn, bind, args )

Invoke a function inside the run loop. Note, to pass arguments you must supply a bind; use null if you would like the global scope.

Parameters

fnFunction The function to invoke
bindObject Optional The object to bind `this` to when calling the function.
argsArray Optional The arguments to pass to the function.

Returns

O.RunLoop Returns self.

invoke: function ( fn, bind, args ) {
   this._depth += 1;
   try {
     // IE8 will throw an error if args is undefined
     // when calling fn.apply for some reason.
     // Avoiding apply/call when not needed is also probably more
     // efficient.
     if ( args ) {
       fn.apply( bind, args );
     } else if ( bind ) {
       fn.call( bind );
     } else {
       fn();
     }
   } catch ( error ) {
     RunLoop.didError( error );
   }
   if ( this._depth === 1 ) {
     this.flushAllQueues();
     this.processTimeouts();
   }
   this._depth -= 1;
   return this;
 },

Method

O.RunLoop.invokeInNextEventLoop( fn, bind )

Use this to invoke a function in a new browser event loop, immediately after this event loop has finished.

Parameters

fnFunction The function to invoke.
bindObject Optional The object to make the 'this' parameter when the function is invoked.

Returns

O.RunLoop Returns self.

invokeInNextEventLoop: function ( fn, bind ) {
   var nextLoopQueue = this._queues.nextLoop;
   if ( !nextLoopQueue.length ) {
     setImmediate( nextLoop );
   }
   nextLoopQueue.push([ fn, bind ]);
   return this;
 },

Method

O.RunLoop.invokeInNextFrame( fn, bind )

Use this to invoke a function just before the browser next redraws.

Parameters

fnFunction The function to invoke.
bindObject Optional The object to make the 'this' parameter when the function is invoked.

Returns

O.RunLoop Returns self.

invokeInNextFrame: function ( fn, bind ) {
   var nextFrameQueue = this._queues.nextFrame;
   if ( !nextFrameQueue.length ) {
     requestAnimFrame( nextFrame );
   }
   nextFrameQueue.push([ fn, bind ]);
   return this;
 },

Method

O.RunLoop.invokeAfterDelay( fn, delay, bind )

Use this to invoke a function after a specified delay. The function will be called inside a new RunLoop, and optionally bound to a supplied object.

Parameters

fnFunction The function to invoke after a delay.
delayNumber The delay in milliseconds.
bindObject Optional The object to make the 'this' parameter when the function is invoked.

Returns

InvocationToken Returns a token that can be passed to the O.RunLoop.cancel method before the function is invoked, in order to cancel the scheduled invocation.

invokeAfterDelay: function ( fn, delay, bind ) {
   var timeout = new Timeout( Date.now() + delay, 0, fn, bind );
   this._timeouts.push( timeout );
   this._scheduleTimeout();
   return timeout;
 },

Method

O.RunLoop.invokePeriodically( fn, period, bind )

Use this to invoke a function periodically, with a set time between invocations.

Parameters

fnFunction The function to invoke periodically.
periodNumber The period in milliseconds between invocations.
bindObject Optional The object to make the 'this' parameter when the function is invoked.

Returns

InvocationToken Returns a token that can be passed to the O.RunLoop.cancel method to cancel all future invocations scheduled by this call.

invokePeriodically: function ( fn, period, bind ) {
   var timeout = new Timeout( Date.now() + period, period, fn, bind );
   this._timeouts.push( timeout );
   this._scheduleTimeout();
   return timeout;
 },

Private Method

NS.RunLoop._scheduleTimeout()

Sets the browser timer if necessary to trigger at the time of the next timeout in the priority queue.

_scheduleTimeout: function () {
   var timeout = this._timeouts.peek(),
     time = timeout ? timeout.time : 0,
     delay;
   if ( time && time !== this._nextTimeout ) {
     clearTimeout( this._timer );
     delay = time - Date.now();
     if ( delay > 0 ) {
       this._timer = setTimeout( processTimeouts, time - Date.now() );
       this._nextTimeout = time;
     } else {
       this._nextTimeout = 0;
     }
   }
 },

Method

NS.RunLoop.processTimeouts()

Invokes all functions in the timeout queue that were scheduled to trigger on or before "now".

Returns

O.RunLoop Returns self.

processTimeouts: function () {
   var timeouts = this._timeouts,
     timeout, period;
   while ( timeouts.length && timeouts.peek().time <= Date.now() ) {
     timeout = timeouts.pop();
     if ( period = timeout.period ) {
       timeout.time = Date.now() + period;
       timeouts.push( timeout );
     }
     this.invoke( timeout.fn, timeout.bind );
   }
   this._scheduleTimeout();
   return this;
 },

Method

O.RunLoop.cancel( token )

Use this to cancel the future invocations of functions scheduled with the O.RunLoop.invokeAfterDelay or O.RunLoop.invokePeriodically methods.

Parameters

tokenInvocationToken The InvocationToken returned by the call to invokeAfterDelay or invokePeriodically that you wish to cancel.

Returns

O.RunLoop Returns self.

cancel: function ( token ) {
   this._timeouts.remove( token );
   return this;
 },

Method

O.RunLoop.didError( error )

This method is invoked if an uncaught error is thrown in a run loop. Overwrite this method to do something more useful then just log the error to the console.

Parameters

errorError The error object.
didError: function ( error ) {
   console.log( error.name, error.message, error.stack );
 }
};

NS.RunLoop = RunLoop;

Function.implement({

Method

Function#queue( queue )

Parameters

queueString The name of the queue to add calls to this function to.

Returns

Function Returns wrapper that passes calls to O.RunLoop.queueFn.

queue: function ( queue ) {
   var fn = this;
   return function () {
     RunLoop.queueFn( queue, fn, this );
     return this;
   };
 },

Method

Function#nextLoop()

Returns

Function Returns wrapper that passes calls to O.RunLoop.invokeInNextEventLoop.

nextLoop: function () {
   var fn = this;
   return function () {
     RunLoop.invokeInNextEventLoop( fn, this );
     return this;
   };
 },

Method

Function#nextFrame()

Returns

Function Returns wrapper that passes calls to O.RunLoop.invokeInNextFrame.

nextFrame: function () {
   var fn = this;
   return function () {
     RunLoop.invokeInNextFrame( fn, this );
     return this;
   };
 },

Method

Function#invokeInRunLoop()

Wraps any calls to this function inside a call to O.RunLoop.invoke.

Returns

Function Returns wrapped function.

invokeInRunLoop: function () {
   var fn = this;
   return function () {
     RunLoop.invoke( fn, this, arguments );
   };
 }
});

var nextLoop = RunLoop.invoke.bind( RunLoop,
 RunLoop.flushQueue, RunLoop, [ 'nextLoop' ]
);
var processTimeouts = RunLoop.processTimeouts.bind( RunLoop );

var nextFrame = function ( time ) {
 RunLoop.frameStartTime = time;
 RunLoop.mayRedraw = true;
 RunLoop.invoke( RunLoop.flushQueue, RunLoop, [ 'nextFrame' ] );
 RunLoop.mayRedraw = false;
};

}( O, window, typeof setImmediate !== 'undefined' ?
 setImmediate :
 function ( fn ) {
   return setTimeout( fn, 0 );
 }
) );
Animation
Application
Core
DataStore
DOM
DragDrop
Foundation
IO
Localisation
Selection
Parser
TimeZones
Storage
Touch
CollectionViews
UA
ContainerViews
ControlViews
PanelViews
View