1  /*
     2   * Copyright the original author or authors.
     3   * 
     4   * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   * 
     8   *      http://www.mozilla.org/MPL/MPL-1.1.html
     9   * 
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  import org.as2lib.env.overload.Overload;
    18  import org.as2lib.app.exec.AbstractProcess;
    19  import org.as2lib.app.exec.Call;
    20  import org.as2lib.app.exec.Executable;
    21  import org.as2lib.app.exec.ForEachExecutable;
    22  import org.as2lib.env.event.impulse.FrameImpulse;
    23  
    24  /**
    25   * {@code Timeout} works as delayed execution of a executable.
    26   * 
    27   * <p>As {@code Timeout} implements {@link Executable} it works like a usual
    28   * executable and can be started with {@link #execute}. 
    29   * 
    30   * <p>As {@code Timeout} implements {@link Process} its possible to handle it
    31   * as process.
    32   * 
    33   * <p>{@code Timeout} works framebased, that means you have to define the delay 
    34   * in number of frames.
    35   * 
    36   * <p>Due to the definition of Call all arguments passed-in in {@link #execute}
    37   * will be passed to the connected executable
    38   * 
    39   * Example for a direct execution:
    40   * <code>
    41   *   import org.as2lib.app.exec.Timeout;
    42   *   import org.as2lib.app.exec.Call;
    43   * 
    44   *   Timeout.timeout(new Call(myObj, myMethod), 20, ["1", "2"]); 
    45   * </code>
    46   * 
    47   * Example for a controlable usage:
    48   * <code>
    49   *   import org.as2lib.app.exec.Timeout;
    50   *   import org.as2lib.app.exec.Call;
    51   * 
    52   *   var call:Call = new Call(myObj, myMethod);
    53   *   var frames:Number = 20;
    54   *   var t:Timeout = new Timeout(call, frames);
    55   *   t.execute("argument 1", "argument 2");
    56   * </code>
    57   * 
    58   * @author Martin Heidegger
    59   * @version 1.0
    60   * @see Executable#execute
    61   */
    62  class org.as2lib.app.exec.Timeout extends AbstractProcess implements ForEachExecutable {
    63  	
    64  	/** Connected Executable */
    65  	private var exe:Executable;
    66  	
    67  	/** Amount of frames until execution (delay) */
    68  	private var frames:Number;
    69  	
    70  	/** Amount of listened frames */
    71  	private var executed:Number;
    72  	
    73  	/**
    74  	 * List of the targets (arguments) for the execution.
    75  	 * used in {@link #forEach}.
    76  	 */
    77  	private var target:Array;
    78  	
    79  	/** Call to the onEnterFrame listener */
    80  	private var timeCall:Call;
    81  	
    82  	/**
    83  	 * Simplyfier for the execution of a timeout.
    84  	 * 
    85  	 * <p>Allows creation and execution of a {@code Timeout} with one call.
    86  	 * 
    87  	 * @param exe Executable to excute after a delay
    88  	 * @param frames Amout of frames during the end of the execution
    89  	 * @param args Arguments to be passed at execution
    90  	 */
    91  	public static function setTimeout(exe:Executable, frames:Number, args:Array) {
    92  		var t:Function = eval("th"+"is");
    93  		var o = new t(exe, frames).execute(args);
    94  	}
    95  	
    96  	/**
    97  	 * Creates a new {@code Timeout} instance.
    98  	 * 
    99  	 * @overload #setExecutable
   100  	 * @overload #setExecutableByObjectAndFunction
   101  	 */
   102  	public function Timeout() {
   103  		timeCall = new Call(this, onEnterFrame);
   104  		var o:Overload = new Overload(this);
   105  		o.addHandler([Executable, Number], setExecutable);
   106  		o.addHandler([Object, Function, Number], setExecutableByObjectAndFunction);
   107  		o.forward(arguments);
   108  	}
   109  	
   110  	/**
   111  	 * Sets the connected executable.
   112  	 * 
   113  	 * @param exe Executable to be executed after the delay
   114  	 * @param frames Delay in frames until execution.
   115  	 */
   116  	public function setExecutable(exe:Executable, frames:Number):Void {
   117  		this.exe = exe;
   118  		this.frames = frames;
   119  	}
   120  	
   121  	/**
   122  	 * Sets the connected executable with a generated call.
   123  	 * 
   124  	 * @param inObject Scope of the execution
   125  	 * @param func Method to execute
   126  	 * @param frames Delay in frames until execution.
   127  	 */
   128  	public function setExecutableByObjectAndFunction(inObject:Object, func:Function, frames:Number):Void {
   129  		setExecutable(new Call(inObject, func), frames);
   130  	}
   131  	
   132  	/**
   133  	 * Starts the delay until the execution of the connected Executable.
   134  	 * 
   135  	 * @see #setExecutable
   136  	 * @see #setExecutableByObjectAndFunction
   137  	 * @see Executable#execute
   138  	 */
   139  	public function execute() {
   140  		executed = 1;
   141  		if (!target) target = new Array();
   142  		target.push(arguments);
   143  		working = true;
   144  		FrameImpulse.getInstance().connectExecutable(timeCall);
   145  		return null;
   146  	}
   147  	
   148  	/**
   149  	 * Referes to execute.
   150  	 * 
   151  	 * <p>Implementation of {@link AbstractProcess#run} for using it as a
   152  	 * process.
   153  	 */
   154  	public function run(Void):Void {
   155  		execute.apply(this, arguments);
   156  	}
   157  	
   158  	/**
   159  	 * Executed the Timeout for all iterable objects.
   160  	 * 
   161  	 * <p>If you execute .forEach to Timeout it will redirect content, name and
   162  	 * the object to each execution of the connected call. 
   163  	 * 
   164  	 * Example:
   165  	 * <code>
   166  	 *   import org.as2lib.app.exec.Timeout;
   167  	 * 
   168  	 *   function display(content, name, inObject) {
   169  	 *     trace("Executed: "+content+", "+name+", "+inObject+";");
   170  	 *   }
   171  	 *   
   172  	 *   var t:Timeout = new Timeout(this, display, 40);
   173  	 *   t.forEach({a:"1", b:"2", c:"3"});
   174  	 * </code>
   175  	 * 
   176  	 * Delays for 40 frames:
   177  	 * <pre>
   178  	 * Executed: 1, a, [Object object];
   179  	 * Executed: 2, b, [Object object];
   180  	 * Executed: 3, c, [Object object];
   181  	 * </pre>
   182  	 * 
   183  	 * @param object Object to be iterated
   184  	 * @return null as the result isn't available yet.
   185  	 */
   186  	public function forEach(object):Array {
   187  		executed = 0;
   188  		if (!target) target = new Array();
   189  		var i:String;
   190  		for (i in object) {
   191  			target.push([object[i], i, object]);
   192  		}
   193  		execute();
   194  		FrameImpulse.getInstance().connectExecutable(timeCall);
   195  		return null;
   196  	}
   197  
   198  	/**
   199  	 * Executed on each interval execution.
   200  	 */
   201  	private function onEnterFrame(Void):Void {
   202  		if (executed++ > frames) {
   203  			finalExecution();
   204  		}
   205  	}
   206  	
   207  	/**
   208  	 * Internal method to finish the execution.
   209  	 */
   210  	private function finalExecution(impulse:FrameImpulse):Void {
   211  		executed = 1;
   212  		var i:Number;
   213  		impulse.disconnectExecutable(timeCall);
   214  		var oldTarget = target.concat();
   215  		target = new Array();
   216  		
   217  		// Applying the execution to multiple targets (foreach)
   218  		try {
   219  			for (i=0; i<oldTarget.length; i++) {
   220  				exe["execute"].apply(exe, oldTarget[i]);
   221  			}
   222  			finish();
   223  		} catch(e) {
   224  			interrupt(e);
   225  		}
   226  	}
   227  }