/** * @class MovieClipHistory * @interface * @author senocular * @version 1.1 * @description Provides a way to record and replay many methods * used in dynamically defining a MovieClip's visual appearance. * Methods recorded can be replayed on the original MovieClip * or another MovieClip which is to "inherit" the original * MovieClip's dynamic appearance. Supported methods include: * * These methods are overridden for the MovieClip so that new, history * recording methods can be used in their place. The new methods record * the method call and call for the MovieClip the original method. *

* When using MovieClipHistory, you don't don't create instances of the * class but, rather, use the class to initialize a pre-existing MovieClip * instance so that that MovieClip may be set up to record history for itself. * An instance of MovieClipHistory is then automatically assigned to that * MovieClip as the property history. This history is an array * of generic history objects that contain the properties: *

*

* @usage
MovieClipHistory.initialize(movieClipToReceiveHistory [, epoch]);
*/ class MovieClipHistory extends Array { /* * container for method names for supported/overridden methods */ private static var _methodNames:Object; /* * container for method functions for supported/overridden methods */ private static var _methods:Object; /* * Defines methods to record history for. These methods * are only defined after the initialize has been called * for the first time. Methods with values of true represent * methods which create MovieClips which too would need * to be initialized with MovieClipHistory in order for * their history to be recorded as well. */ private static function init():Void{ _methods = Object(); // Object without new to assure no inheritance _methodNames = Object(); // use most visual properties (those that create in and draw in MovieClips) _methodNames.attachMovie = true; _methodNames.duplicateMovieClip = true; _methodNames.createEmptyMovieClip = true; _methodNames.loadMovie = false; _methodNames.swapDepths = false; _methodNames.removeMovieClip = false; _methodNames.lineStyle = false; _methodNames.beginFill = false; _methodNames.beginGradientFill = false; _methodNames.endFill = false; _methodNames.moveTo = false; _methodNames.lineTo = false; _methodNames.curveTo = false; _methodNames.clear = false; } /** * @method MovieClipHistory.initialize * @description (Static) Initializes a MovieClip in order for it's methods to be recorded. * When initialized, MovieClips recieve an instance of MovieClipHistory * as a history property. Set epoch to true if you want child clips to store * history in the MovieClip being initialized * @param mc (MovieClip) MovieClip to be set up to record history * @param epoch (MovieClip, Boolean) Optional, MovieClip to record history for mc. If null, mc is used and all child MovieClips created through historic events record methods independantly to their own history instances. If true, mc is used and all child MovieClips created through historic events use mc as their epoch recording their history to mc's history instance. Default: null. * @usage
MovieClipHistory.initialize(movieClipToReceiveHistory [, epoch]);
*/ public static function initialize(mc:MovieClip, epoch/*:Boolean, MovieClip*/):Void{ if (!_methods || !_methodNames) init(); // define _methodNames and _methods if not already // for all methods in _methodNames array for (var methodName in _methodNames){ // if the method doesn't yet exist as a function in _methods, create it if (!_methods[methodName]){ // this function will be what overrides the mc MovieClip's own methods // it adds the method to the history list and calls the MovieClip's // original through its __proto__ reference _methods[methodName] = function(){ // determine the history object to save in based on what the epoch is var epochHistory = (this.history.epoch) ? this.history.epoch.history : this.history; // if history is enabled, add properties of the method call to the array of history events if (epochHistory.enabled) epochHistory.push({methodName:arguments.callee.methodName, arguments:arguments, target:this.history.target}); // call the original method for the MovieClip object (this is the MovieClip since this method is copied to it) var result = this.__proto__[arguments.callee.methodName].apply(this, arguments); // if the method creates a MovieClip (e.g. attachMovie, duplicateMovieClip, createEmptyMovieClip ...) initialize that new MovieClip if (epochHistory.enabled && arguments.callee.creates) MovieClipHistory.initialize(result, epochHistory.epoch); // return whatever result came from the original method call return result; } // assign to this method its name and whether or not it creates MovieClips so that // it can refernce these properties via arguments.callee _methods[methodName].methodName = methodName; _methods[methodName].creates = _methodNames[methodName]; } // assign the current method to the MovieClip overriding it's default. // Note: overridden methods need to be inherited such as those inherited from MovieClip (all defaults) mc[methodName] = _methods[methodName]; } // assign a history object to the MovieClip which is a // MovieClipHistory with the MovieClip as its owner and // epoch based on that which was passed to initialze mc.history = new MovieClipHistory(mc, epoch); } private var __parent:MovieClip; // parent clip containing this history object. private var __epoch:MovieClip; // epoch MovieClip recording this __parent's history. private var __target:String; // target from epoch to this __parent. /** * @property enabled if true history is recorded as normal. When set to false historic methods are not recorded in history. Default: true. */ public var enabled:Boolean = true; /** * Constructor (for invocation by initialize, typically implicit). * param owner (MovieClip) The movieclip to enable history recording * param epoch (MovieClip, Boolean) Optional, Specifies which MovieClip will record historic * events. If null, the owner records only it's own events. * If another MovieClip, owner will record all it's historic * events in epoch. Default: null. */ function MovieClipHistory(owner:MovieClip, epoch/*:Boolean, MovieClip*/){ __parent = owner; // __parent is used to reference the MovieClip this history property belongs to if (epoch == true) __epoch = __parent; // epoch is passed as true if all child history events are to remain in this history object else __epoch = epoch; // otherwise use what was passed (another MovieClip or undefined) // develop a __target based on __epoch and __parent values. If an epoch is used, as child clips are created, // the path extends itself to correctly reference each child in respect to the epoch __target = (!__epoch || __parent == __epoch) ? "epoch" : __parent._parent.history.target + "." + __parent._name; } /** * @property _parent The MovieClip owning the MovieClipHistory instance. */ public function get _parent():MovieClip{ return __parent; } /** * @property epoch The epoch MovieClip if _parent is not recording it's own history. */ public function get epoch():MovieClip{ return __epoch; } /** * @property target A string path to _parent from the epoch. */ public function get target():String{ return __target; } /** * @method recall * @description Recalls one, a range of, or all historic events calling them on the passed epoch. * @param anyMovieClip (MovieClip) Optional, specifies which MovieClip will recieve calls of historic events from this history instance. If null, the history owner recieves the events. Default: null. * @param start (Number) Optional, The position in history to start playback. Default: 0 (first event). * @param end (Number) Optional, The position in history to stop playback. If start is specified but end is not, only the history event at start is called. Default: length-1 (last event). * @usage
initializedMovieClip.history.recall(anyMovieClip [, start [,end]]);
*/ public function recall(epoch:MovieClip, start:Number, end:Number):Void{ // epoch here represents the MovieClip to /receive/ historic events // if no epoch/MovieClip is passed, use this history's _parent if (!epoch) epoch = __parent; var target, result, len = this.length; if (end == undefined){ if (start == undefined){ // if no end or start is specified, use the whole history array start = 0; end = len; }else end = start + 1; // if just start, use only start (loops once) }else end++; // assume user wants end value to be inclusive // if the epoch MovieClip passed has not been initialized, do so now // using whatever epoch was used for this history if (!(epoch.history instanceof MovieClipHistory)) initialize(epoch, (__epoch) ? epoch : null); var e = enabled; // save current enabled setting enabled = false; // disable history recording when recalling history // loop through this history based on start and end values for (var i=start; i