1  /**
     2   * com.sekati.utils.ClassUtils
     3   * @version 2.2.0
     4   * @author jason m horwitz | sekati.com
     5   * Copyright (C) 2007  jason m horwitz, Sekat LLC. All Rights Reserved.
     6   * Released under the MIT License: http://www.opensource.org/licenses/mit-license.php
     7   */
     8   
     9  import com.sekati.core.KeyFactory;
    10  
    11  /**
    12   * Static class for wrapping various Class utilities. For example linking 'extend MovieClip' type 
    13   * classes to MovieClips thru attachMovie, createEmptyMovieClip or MovieClip Instances on stage.<br/><br/>
    14   * 
    15   * An initObject param is available in methods: {@link #createEmptyMovieClip}, {@link #attachMovie}
    16   * and {@link #attachClass}.  _depth is a custom initObject param which will set the clip to this depth 
    17   * regardless of method but *will not* store _depth as a MovieClip property; use getDepth if needed.
    18   *
    19   * {@code Example Class:
    20   * class com.sekati.Test extends MovieClip {
    21   * 	public function Test(){
    22   * 		trace("Test Class instantiated on: "+this._name);
    23   * 	}
    24   * }
    25   * }
    26   * 
    27   * @TODO Add an AS2 Class index getter for debugging purposes.
    28   */
    29  class com.sekati.utils.ClassUtils {
    30  
    31  	/**
    32  	 * create a movieclip with linked class (various init options)
    33  	 * @param classRef (Function) reference to class to attach
    34  	 * @param target (MovieClip) target scope to create MovieClip
    35  	 * @param instanceName (String) created MovieClip instance name
    36  	 * @param initObject (Object) object of properties to create MovieClip with. Depth will automatically be created if none is specified
    37  	 * @return MovieClip
    38  	 * {@code Usage:
    39  	 * var mc0:MovieClip = ClassUtils.createEmptyMovieClip (com.sekati.Test, this, "mc0");
    40  	 * var mc0:MovieClip = ClassUtils.createEmptyMovieClip (com.sekati.Test, _root, "mc0", {_depth: 100, _x:25, _y:25});
    41  	 * var mc0:MovieClip = ClassUtils.createEmptyMovieClip (com.sekati.Test, this, "mc0", {_x:25, _y:25});
    42  	 * }
    43  	 */
    44  	public static function createEmptyMovieClip(classRef:Function, target:MovieClip, instanceName:String, initObject:Object):MovieClip {
    45  		var depth:Number = (!initObject._depth) ? target.getNextHighestDepth( ) : initObject._depth;
    46  		var mc:MovieClip = target.createEmptyMovieClip( instanceName, depth );
    47  		mc.__proto__ = classRef.prototype;
    48  		if (initObject) {
    49  			for (var i in initObject) {
    50  				if (i != "_depth") {
    51  					mc[i] = initObject[i];
    52  				}
    53  			}
    54  		}
    55  		classRef.apply( mc );
    56  		KeyFactory.inject( mc );
    57  		return mc;
    58  	}	
    59  
    60  	/**
    61  	 * attach a MovieClip from library and extend with class (various init options)
    62  	 * @param classRef (Function) reference to class to attach
    63  	 * @param target (MovieClip) target scope to create MovieClip
    64  	 * @param idName (String) linkage id for exported MovieClip in library	
    65  	 * @param instanceName (String) created MovieClip instance name
    66  	 * @param initObject (Object) object of properties to create MovieClip with. Depth will automatically be created if none is specified
    67  	 * @return MovieClip
    68  	 * {@code Usage:
    69  	 * var mc1:MovieClip = ClassUtils.attachMovie (com.sekati.Test, _root, "linkedMc", "mc1");
    70  	 * var mc1:MovieClip = ClassUtils.attachMovie (com.sekati.Test, _root, "linkedMc", "mc1", {_x:50, _y:50});
    71  	 * var mc1:MovieClip = ClassUtils.attachMovie (com.sekati.Test, _root, "linkedMc", "mc1", {_depth:200, _x:50, _y:50});
    72  	 * }
    73  	 */
    74  	public static function attachMovie(classRef:Function, target:MovieClip, idName:String, instanceName:String, initObject:Object):MovieClip {
    75  		var depth:Number = (!initObject._depth) ? target.getNextHighestDepth( ) : initObject._depth;
    76  		var mc:MovieClip = target.attachMovie( idName, instanceName, depth, initObject );
    77  		mc.__proto__ = classRef.prototype;
    78  		if (mc._depth) {
    79  			delete mc._depth;
    80  		}
    81  		classRef.apply( mc );
    82  		KeyFactory.inject( mc );
    83  		return mc;
    84  	}
    85  
    86  	/**
    87  	 * Attach a movie from a DLL swf's library (loads dll then attaches requested movie).
    88  	 * NOTE: you should insert a delay between your callback and methods calls in the shared library as it initializes.
    89  	 * @param dll (Function) url of the dll.swf which contains exported assets in its library
    90  	 * @param target (MovieClip) target scope to attachMovie within
    91  	 * @param idName (String) linkage id for exported MovieClip in library	
    92  	 * @param instanceName (String) created MovieClip instance name
    93  	 * @param initObject (Object) object of properties to create MovieClip with. Depth will automatically be created if none is specified
    94  	 * @param cb (Function) callback function to fire when dll has been loaded and clip attached
    95  	 * @return MovieClip
    96  	 * {@code Usage:
    97  	 * var mc0:MovieClip = ClassUtils.attachDllMovie("dll.swf", _root, "myDllExportedItem", "mc0", {_x:50, _y:50, _depth:20}, myCallBackFn);
    98  	 * }
    99  	 */
   100  	public static function attachDllMovie(dll:String, target:MovieClip, idName:String, instanceName:String, initObject:Object, cb:Function):MovieClip {
   101  		var depth:Number = (!initObject._depth) ? target.getNextHighestDepth( ) : initObject._depth;
   102  		var mc:MovieClip = target.createEmptyMovieClip( instanceName, depth );
   103  		var mcLoader:MovieClipLoader = new MovieClipLoader( );
   104  		var onDLLLoaded:Function = function():Void {
   105  			mcLoader.removeListener( listener );
   106  			if(cb) _global["setTimeout"]( this, "cb", 50 );
   107  		};
   108  		var listener:Object = new Object( );
   109  		listener.onLoadInit = function(mc:MovieClip):Void {
   110  			mc.attachMovie( idName, instanceName, mc.getNextHighestDepth( ) );
   111  			target[instanceName] = target[instanceName][instanceName];
   112  			if (initObject) {
   113  				for (var i in initObject) {
   114  					if (i != "_depth") mc[i] = initObject[i];
   115  				}
   116  			}				
   117  			onDLLLoaded( );
   118  		};
   119  		mcLoader.addListener( listener );
   120  		mcLoader.loadClip( dll, mc );
   121  		return mc;
   122  	}	
   123  
   124  	/**
   125  	 * extend a MovieClip instance (on stage) with class (various init options)
   126  	 * @param classRef (Function) reference to class to attach
   127  	 * @param target (MovieClip) target scope to create MovieClip
   128  	 * @param initObject (Object) object of properties to create MovieClip with. Depth will automatically be created if none is specified
   129  	 * @return MovieClip
   130  	 * {@code Usage:
   131  	 * var mc2:MovieClip = ClassUtils.attachClass (com.sekati.Test, mc2);
   132  	 * var mc2:MovieClip = ClassUtils.attachClass (com.sekati.Test, _root.mc2, {_x:75, _y:75});
   133  	 * var mc2:MovieClip = ClassUtils.attachClass (com.sekati.Test, mc2, {_depth:300, _x:75, _y:75});
   134  	 * }
   135  	 */
   136  	public static function attachClass(classRef:Function, target:MovieClip, initObject:Object):MovieClip {
   137  		var mc:MovieClip = target;
   138  		mc.__proto__ = classRef.prototype;
   139  		if (initObject) {
   140  			for (var i in initObject) {
   141  				if (i != "_depth") {
   142  					mc[i] = initObject[i];
   143  				} else {
   144  					target.swapDepths( initObject[i] );
   145  				}
   146  			}
   147  		}
   148  		classRef.apply( mc );
   149  		KeyFactory.inject( mc );
   150  		return target;
   151  	}
   152  
   153  	/**
   154  	 * Create and return a new instance of a defined class
   155  	 * @param classRef (Function) reference to full class namespace
   156  	 * @param args (Array) array of constructor arguments
   157  	 * @return Object - instantiated class object
   158  	 * {@code Usage:
   159  	 * var o:Point = ClassUtils.createInstance (com.sekati.geom.Point, [15,50]);
   160  	 * }
   161  	 */
   162  	public static function createInstance(classRef:Function, args:Array):Object {
   163  		var o:Object = {__constructor__:classRef, __proto__:classRef.prototype};
   164  		classRef.apply( o, args );
   165  		KeyFactory.inject( o );
   166  		return o;
   167  	}
   168  
   169  	/**
   170  	 *  Create and return a new instance of a defined class without 
   171  	 *  invoking its constructor
   172  	 *  @param classRef (Function) reference to full class namespace
   173  	 *  @return Object - class object
   174  	 *  {@code Usage:
   175  	 *  var scr:Scroll = ClassUtils.createCleanInstance(com.sekati.ui.Scroll);
   176  	 *  }
   177  	 */
   178  	public static function createCleanInstance(classRef:Function):Object {
   179  		var o:Object = new Object;
   180  		o.__proto__ = classRef.prototype;
   181  		o.__constructor__ = classRef;
   182  		KeyFactory.inject( o );
   183  		return o;	
   184  	}
   185  
   186  	/**
   187  	 * Check if a subclass is extended by a superclass
   188  	 * @param subclassRef (Function) reference to the full subclass namespace
   189  	 * @param superclassRef (Function) reference to the full superclass namespace
   190  	 * @return Boolean
   191  	 * {@code Usage:
   192  	 * 	trace(ClassUtils.isSubclassOf(com.sekati.display.AbstractClip, com.sekati.display.CoreClip)); // returns: true
   193  	 * }
   194  	 */
   195  	public static function isSubclassOf(subclassRef:Function, superclassRef:Function):Boolean {
   196  		var o:Object = subclassRef.prototype;
   197  		while(o !== undefined) {
   198  			o = o.__proto__;
   199  			if(o === superclassRef.prototype) {
   200  				return true;
   201  			}	
   202  		}
   203  		return false;
   204  	}
   205  
   206  	/**
   207  	 * Check if a class implements an interface
   208  	 * @param classRef (Function) reference to the full class namespace
   209  	 * @param interfaceRef (Function) reference to the full interface namespace
   210  	 * @return Boolean
   211  	 * {@code Usage:
   212  	 * 	trace(ClassUtils.isImplementationOf(com.sekati.display.CoreClip, com.sekati.display.ICoreClip)); // returns: true
   213  	 * }
   214  	 */
   215  	public static function isImplementationOf(classRef:Function, interfaceRef:Function):Boolean {
   216  		// interface will not be in prototype chain
   217  		if(isSubclassOf( classRef, interfaceRef )) {
   218  			return false;	
   219  		}
   220  		// if an instance it is not extended, the class has to be an instance of it
   221  		return (createCleanInstance( classRef ) instanceof interfaceRef);
   222  	}
   223  
   224  	private function ClassUtils() {
   225  	}
   226  }