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.io.file.AbstractFileLoader;
    18  import org.as2lib.data.type.Byte;
    19  import org.as2lib.data.holder.Iterator;
    20  import org.as2lib.env.event.impulse.FrameImpulse;
    21  import org.as2lib.env.event.impulse.FrameImpulseListener;
    22  import org.as2lib.io.file.FileLoader;
    23  import org.as2lib.io.file.FileNotLoadedException;
    24  import org.as2lib.data.type.Time;
    25  import org.as2lib.app.exec.Executable;
    26  import org.as2lib.data.holder.Map;
    27  import org.as2lib.io.file.File;
    28  import org.as2lib.io.file.SwfFile;
    29  
    30  /**
    31   * {@code SwfLoader} is a implementation of {@link FileLoader} to load
    32   * files with {@code loadMovie} (usually {@code .swf} files}.
    33   * 
    34   * <p>Any content to be loaded with {@code MovieClip#loadMovie} can be load with
    35   * {@code SwfLoader} to a concrete {@code MovieClip} instance that has to be
    36   * passed-in with the constructor.
    37   *
    38   * <p>{@code SwfLoader} represents the time consuming part of accessing external
    39   * {@code .swf}' ({@code SwfFile} is the handleable part} and therefore
    40   * contains a event system to add listeners to listen to the concrete events.
    41   * It is possible to add listeners using {@code addListener}.
    42   * 
    43   * <p>Example listener:
    44   * <code>
    45   *   import org.as2lib.io.file.AbstractFileLoader;
    46   *   import org.as2lib.io.file.LoadProgressListener;
    47   *   import org.as2lib.io.file.LoadStartListener;
    48   *   import org.as2lib.io.file.LoadCompleteListener;
    49   *   import org.as2lib.io.file.LoadErrorListener;
    50   *   import org.as2lib.io.file.FileLoader;
    51   *   import org.as2lib.io.file.SwfFile;
    52   *   
    53   *   class MySwfListener implements 
    54   *        LoadProgressListener, LoadStartListener,
    55   *        LoadCompleteListener, LoadErrorListener {
    56   *        
    57   *     public function onLoadComplete(fileLoader:FileLoader):Void {
    58   *       var swf:SwfFile = SwfFile(fileLoader.getFile());
    59   *       if (swf != null) {
    60   *         // Proper swf available
    61   *       } else {
    62   *         // Wrong event handled
    63   *       }
    64   *     }
    65   *     
    66   *     public function onLoadError(fileLoader:FileLoader, errorCode:String, error):Void {
    67   *       if (errorCode == AbstractFileLoader.FILE_NOT_FOUND) {
    68   *         var notExistantUrl = error;
    69   *         // Use that url
    70   *       }
    71   *     }
    72   *     
    73   *     public function onLoadStart(fileLoader:FileLoader) {
    74   *       // show that this file just gets loaded
    75   *     }
    76   *     
    77   *     public function onLoadProgress(fileLoader:FileLoader) {
    78   *       // update the percentage display with fileLoader.getPercentage();
    79   *     }
    80   *   }
    81   * </code>
    82   * 
    83   * <p>Example of the usage:
    84   * <code>
    85   *   import org.as2lib.io.file.SwfLoader;
    86   *   
    87   *   var swfLoader:SwfLoader = new SwfLoader();
    88   *   swfLoader.addListener(new MySwfListener());
    89   *   swfLoader.load("test.swf");
    90   * </code>
    91   * 
    92   * @author Martin Heidegger
    93   * @version 1.1
    94   */
    95  class org.as2lib.io.file.SwfFileLoader extends AbstractFileLoader
    96  	implements FileLoader, FrameImpulseListener {
    97  	
    98  	/** Time until the method breaks with "File not found". */
    99  	public static var TIMEOUT:Time = new Time(3000);
   100  	
   101  	/** Helper for loading the {@code File}. */
   102  	private var holder:MovieClip;
   103  	
   104  	/** Loaded {@code File}. */
   105  	private var result:File;
   106  	
   107  	/**
   108  	 * Constructs a new {@code SwfLoader} instance.
   109  	 * 
   110  	 * @param holder {@code MovieClip} instance to load the {@code .swf} into
   111  	 */
   112  	public function SwfFileLoader(holder:MovieClip) {
   113  		this.holder = holder;
   114  	}
   115  	
   116  	/**
   117  	 * Loads a certain {@code .swf} by a http request.
   118  	 * 
   119  	 * <p>It sends http request by using the passed-in {@code uri}, {@code method}
   120  	 * and {@code parameters} with {@code .loadMovie}. 
   121  	 * 
   122  	 * <p>If you only need to listen if the {@code SwfFile} finished loading
   123  	 * you can apply a {@code callBack} that gets called if the {@code File} is loaded.
   124  	 * 
   125  	 * @param uri location of the file to load
   126  	 * @param parameters (optional) parameters for loading the file
   127  	 * @param method (optional) POST/GET as method for submitting the parameters,
   128  	 *        default method used if {@code method} was not passed-in is POST.
   129  	 * @param callBack (optional) {@link Executable} to be executed after the
   130  	 *        the file was loaded.
   131  	 */
   132  	public function load(uri:String, method:String, parameters:Map, callBack:Executable):Void {
   133  		super.load(uri, method, parameters, callBack);
   134  		result = null;
   135  		if(parameters) {
   136  			var keys:Iterator = parameters.keyIterator();
   137  			while (keys.hasNext()) {
   138  				var key = keys.next();
   139  				holder[key.toString()] = parameters.get(key);
   140  			}
   141  		}
   142  		holder.loadMovie(uri, method);
   143  		sendStartEvent();
   144  		FrameImpulse.getInstance().addFrameImpulseListener(this);
   145  	}
   146  	
   147  	/**
   148  	 * Returns the loaded file.
   149  	 * 
   150  	 * @return file that has been loaded
   151  	 * @throws FileNotLoadedException if the file has not been loaded yet
   152  	 */
   153  	public function getFile(Void):File {
   154  		if (!result) {
   155  			throw new FileNotLoadedException("No File has been loaded.", this, arguments);
   156  		}
   157  		return result;
   158  	}
   159  	
   160  	/**
   161  	 * Returns the total amount of bytes that has been loaded.
   162  	 * 
   163  	 * <p>Returns {@code null} if its not possible to get the loaded bytes.
   164  	 * 
   165  	 * @return amount of bytes that has been loaded
   166  	 */
   167  	public function getBytesLoaded(Void):Byte {
   168  		var result:Number = holder.getBytesLoaded();
   169  		if (result >= 0) {
   170  			return new Byte(result);
   171  		}
   172  		return null;
   173  	}
   174  	
   175  	/**
   176  	 * Returns the total amount of bytes that will approximately be loaded.
   177  	 * 
   178  	 * <p>Returns {@code null} if its not possible to get the total amount of bytes.
   179  	 * 
   180  	 * @return amount of bytes to load
   181  	 */
   182  	public function getBytesTotal(Void):Byte {
   183  		var total:Number = holder.getBytesTotal();
   184  		if (total >= 0) {
   185  			return new Byte(total);
   186  		}
   187  		return null;
   188  	}
   189  	
   190  	/**
   191  	 * Handles a {@code frame} execution.
   192  	 * 
   193  	 * <p>Helper that checks every frame if the {@code .swf} finished loading.
   194  	 * 
   195  	 * @param impulse {@code FrameImpulse} that sent the event
   196  	 */
   197  	public function onFrameImpulse(impulse:FrameImpulse):Void {
   198  		if (checkFinished()) {
   199  			successLoading();
   200  			return;
   201  		}
   202  		if (checkTimeout()) {
   203  			failLoading();
   204  		}
   205  	}
   206  	
   207  	/**
   208  	 * Checks if the {@code .swf} finished loading.
   209  	 * 
   210  	 * @return {@code true} if the {@code .swf} finished loading
   211  	 */
   212  	private function checkFinished():Boolean {
   213  		holder = eval(""+holder._target);
   214  		if ( holder.getBytesTotal() > 10 
   215  			&& holder.getBytesTotal() - holder.getBytesLoaded() < 10) { 
   216  			return true;
   217  		}
   218  		return false;
   219  	}
   220  	
   221  	/**
   222  	 * Checks if the {@code TIMEOUT} has been exceeded by the durating.
   223  	 * 
   224  	 * @return {@code true} if the duration exceeded the {@code TIMEOUT} value
   225  	 */
   226  	private function checkTimeout():Boolean {
   227  		if (holder.getBytesTotal() > 10) {
   228  			return false;
   229  		}
   230  		return (getDuration().valueOf() > TIMEOUT);
   231  	}
   232  	
   233  	/**
   234  	 * Handles if the loading of file was successful.
   235  	 */
   236  	private function successLoading(Void):Void {
   237  		finished = true;
   238  		started = false;
   239  		result = new SwfFile(holder, uri, getBytesTotal());
   240  		endTime = getTimer();
   241  		sendCompleteEvent();
   242  		tearDown();
   243  	}
   244  	
   245  	/**
   246  	 * Handles if the loading of the file failed.
   247  	 */
   248  	private function failLoading(Void):Void {
   249  		finished = true;
   250  		started = false;
   251  		endTime = getTimer();
   252  		sendErrorEvent(FILE_NOT_FOUND_ERROR, uri);
   253  		tearDown();
   254  	}
   255  	
   256  	/**
   257  	 * Removes instance from listening to {@code FrameImpulse}.
   258  	 * 
   259  	 * @see #onFrameImpulse
   260  	 */
   261  	private function tearDown(Void):Void {
   262  		FrameImpulse.getInstance().removeListener(this);
   263  	}
   264  
   265  }