package flare.animate {
import flash.display.Shape;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.utils.Timer;
/**
* Scheduler that oversees animation and time-based processing. Uses an
* internal timer to regularly invoke the current set of scheduled
* objects. Typically, interaction with the scheduler is automatically
* handled by Transition classes. However, custom implmentations of
* the ISchedulable interface will need to be scheduled. Use the
* Scheduler.instance property, and not the constructor, to get
* a reference to the active scheduler.
*
*
By default, the Scheduler issues updates to all scheduled items each
* time the Flash Player advances to the next frame, as reported by the
* Event.ENTER_FRAME
event. To instead set the update interval
* manually, see the timerInterval
property.
*/
public class Scheduler
{
private static const _instance:Scheduler = new Scheduler(_Lock);
/** The default Scheduler instance. */
public static function get instance():Scheduler { return _instance; }
private var _scheduled:Vector. = new Vector.(0); // list of all currently scheduled items
private var _ids:Object; // map of all named items
private var _timer:Timer; // timer for interval-based scheduling
private var _obj:Shape; // shape for getting ENTER_FRAME events
/**
* Sets the timer interval (in milliseconds) at which the scheduler
* should process events. If this value is greater than zero, a
* Timer
instance will be used to trigger scheduler
* updates at the given interval. If this value is less than or equal
* to zero (the default), scheduler updates will be issued with each
* time the Flash Player advances to the next frame according to the
* Event.ENTER_FRAME
event.
*/
public function get timerInterval():Number { return _timer.delay; }
public function set timerInterval(t:Number):void {
pause(); _timer.delay = (t>0 ? t : 0); play();
}
/**
* Creates a new Scheduler--this constructor should be not used;
* instead use the instance
property.
* @param lock a lock object to emulate a private constructor
*/
public function Scheduler(lock:Class) {
if (lock == _Lock) {
_scheduled.length = 0;
_ids = {};
_obj = new Shape();
_timer = new Timer(0);
_timer.addEventListener(TimerEvent.TIMER, tick);
} else {
throw new Error("Invalid constructor. Use Scheduler.instance.");
}
}
/**
* Plays the scheduler, allowing it to process events.
*/
private function play():void
{
if (timerInterval <= 0) {
if (!_obj.hasEventListener(Event.ENTER_FRAME))
_obj.addEventListener(Event.ENTER_FRAME, tick);
} else if (!_timer.running) {
_timer.start();
}
}
/**
* Pauses the scheduler, so that events are not processed.
*/
private function pause():void
{
if (timerInterval <= 0) {
_obj.removeEventListener(Event.ENTER_FRAME, tick);
} else {
_timer.stop();
}
}
/**
* Adds an object to the scheduling list.
* @param item a schedulable object to add
*/
public function add(item:ISchedulable) : void
{
if (item.id && _ids[item.id] != item) {
cancel(item.id);
_ids[item.id] = item;
}
_scheduled.push(item);
play();
}
/**
* Removes an object from the scheduling list.
* @param item the object to remove
* @return true if the object was found and removed, false otherwise
*/
public function remove(item:ISchedulable) : Boolean
{
var idx:int = _scheduled.indexOf(item);
if (idx >= 0) {
_scheduled.splice(idx,1);
if (item.id && _ids[item.id] == item) {
if (_scheduled.indexOf(item) < 0)
delete _ids[item.id];
}
}
return (idx >= 0);
}
/**
* Indicates if an object with the given id is currently in the
* scheduler queue.
* @param id the id to check for
* @return true if an object with that id is currently scheduled,
* false otherwise
*/
public function isScheduled(id:String) : Boolean
{
return _ids[id] != undefined;
}
/**
* Looks up the scheduled object indicated by the given id, if any.
* @param id the id to lookup
* @return the scheduled object with matching id, of null if none
*/
public function lookup(id:String) : ISchedulable
{
return id==null ? null : _ids[id];
}
/**
* Cancels any scheduled object with a matching id.
* @param id the id to cancel
* @return true if an object was found and cancelled, false otherwise
*/
public function cancel(id:String) : Boolean
{
var s:ISchedulable = _ids[id];
if (s != null) {
remove(s);
s.cancelled();
return true;
} else {
return false;
}
}
/**
* Frame/timer callback that invokes each scheduled object.
* @param event the event that triggered the callback
*/
public function tick(event:Event) : void
{
// all events will see the same timestamp
var time:Number = new Date().time;
for each (var s:ISchedulable in _scheduled) {
if (s.evaluate(time))
remove(s);
}
if (_scheduled.length == 0) {
pause();
}
}
} // end of class Scheduler
}
// scheduler lock class to enforce singleton pattern
class _Lock { }