1  /**
     2   * Tweener
     3   * Transition controller for movieclips, sounds, textfields and other objects
     4   *
     5   * @author		Zeh Fernando, Nate Chatellier, Arthur Debert
     6   * @version		1.31.69
     7   */
     8  
     9  /*
    10  Licensed under the MIT License
    11  
    12  Copyright (c) 2006-2007 Zeh Fernando, Nate Chatellier and Arthur Debert
    13  
    14  Permission is hereby granted, free of charge, to any person obtaining a copy of
    15  this software and associated documentation files (the "Software"), to deal in
    16  the Software without restriction, including without limitation the rights to
    17  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
    18  the Software, and to permit persons to whom the Software is furnished to do so,
    19  subject to the following conditions:
    20  
    21  The above copyright notice and this permission notice shall be included in all
    22  copies or substantial portions of the Software.
    23  
    24  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    25  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
    26  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
    27  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
    28  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    29  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    30  
    31  http://code.google.com/p/tweener/
    32  http://code.google.com/p/tweener/wiki/License
    33  */
    34  
    35  import caurina.transitions.Equations;
    36  import caurina.transitions.AuxFunctions;
    37  import caurina.transitions.SpecialProperty;
    38  import caurina.transitions.SpecialPropertyModifier;
    39  import caurina.transitions.SpecialPropertySplitter;
    40  import caurina.transitions.TweenListObj;
    41  import caurina.transitions.PropertyInfoObj;
    42  
    43  class caurina.transitions.Tweener {
    44  
    45  	private static var _engineExists:Boolean = false;		// Whether or not the engine is currently running
    46  	private static var _inited:Boolean = false;				// Whether or not the class has been initiated
    47  	private static var _currentTime:Number;					// The current time. This is generic for all tweenings for a "time grid" based update
    48  	private static var _currentTimeFrame:Number;			// The current frame. Used on frame-based tweenings
    49  
    50  	private static var _tweenList:Array;					// List of active tweens
    51  
    52  	private static var _timeScale:Number = 1;				// Time scale (default = 1)
    53  
    54  	private static var _transitionList:Object;				// List of "pre-fetched" transition functions
    55  	private static var _specialPropertyList:Object;			// List of special properties
    56  	private static var _specialPropertyModifierList:Object;	// List of special property modifiers
    57  	private static var _specialPropertySplitterList:Object;	// List of special property splitters
    58  
    59  
    60  	/**
    61  	 * There's no constructor.
    62  	 */
    63  	public function Tweener () {
    64  		trace ("Tweener is an static class and should not be instantiated.");
    65  	}
    66  
    67  
    68  	// ==================================================================================================================================
    69  	// TWEENING CONTROL functions -------------------------------------------------------------------------------------------------------
    70  
    71  	/**
    72  	 * Adds a new tweening
    73  	 *
    74  	 * @param		(first-n param)		Object				Object that should be tweened: a movieclip, textfield, etc.. OR an array of objects
    75  	 * @param		(last param)		Object				Object containing the specified parameters in any order, as well as the properties that should be tweened and their values
    76  	 * @param		.time				Number				Time in seconds or frames for the tweening to take (defaults 2)
    77  	 * @param		.delay				Number				Delay time (defaults 0)
    78  	 * @param		.useFrames			Boolean				Whether to use frames instead of seconds for time control (defaults false)
    79  	 * @param		.transition			String/Function		Type of transition equation... (defaults to "easeoutexpo")
    80  	 * @param		.transitionParams	Object				* Direct property, See the TweenListObj class
    81  	 * @param		.onStart			Function			* Direct property, See the TweenListObj class
    82  	 * @param		.onUpdate			Function			* Direct property, See the TweenListObj class
    83  	 * @param		.onComplete			Function			* Direct property, See the TweenListObj class
    84  	 * @param		.onOverwrite		Function			* Direct property, See the TweenListObj class
    85  	 * @param		.onStartParams		Array				* Direct property, See the TweenListObj class
    86  	 * @param		.onUpdateParams		Array				* Direct property, See the TweenListObj class
    87  	 * @param		.onCompleteParams	Array				* Direct property, See the TweenListObj class
    88  	 * @param		.onOverwriteParams	Array				* Direct property, See the TweenListObj class
    89  	 * @param		.rounded			Boolean				* Direct property, See the TweenListObj class
    90  	 * @param		.skipUpdates		Number				* Direct property, See the TweenListObj class
    91  	 * @return							Boolean				TRUE if the tween was successfully added, FALSE if otherwise
    92  	 */
    93  	public static function addTween (p_scopes:Object, p_parameters:Object):Boolean {
    94  		if (p_scopes == undefined) return false;
    95  
    96  		var i:Number, j:Number, istr:String;
    97  
    98  		var rScopes:Array; // List of objects to tween
    99  		if (p_scopes instanceof Array) {
   100  			// The first argument is an array
   101  			rScopes = p_scopes.concat();
   102  		} else {
   103  			// The first argument(s) is(are) object(s)
   104  			rScopes = [p_scopes];
   105  		}
   106  
   107          // make properties chain ("inheritance")
   108  		var p_obj:Object = TweenListObj.makePropertiesChain(p_parameters);
   109  
   110  		// Creates the main engine if it isn't active
   111  		if (!_inited) init();
   112  		if (!_engineExists || _root[getControllerName()] == undefined) startEngine(); // Quick fix for Flash not resetting the vars on double ctrl+enter...
   113  
   114  		// Creates a "safer", more strict tweening object
   115  		var rTime:Number = (isNaN(p_obj.time) ? 0 : p_obj.time); // Real time
   116  		var rDelay:Number = (isNaN(p_obj.delay) ? 0 : p_obj.delay); // Real delay
   117  
   118  		// Creates the property list; everything that isn't a hardcoded variable
   119  		var rProperties:Object = new Object(); // Object containing a list of PropertyInfoObj instances
   120  		var restrictedWords:Object = {time:true, delay:true, useFrames:true, skipUpdates:true, transition:true, transitionParams:true, onStart:true, onUpdate:true, onComplete:true, onOverwrite:true, onError:true, rounded:true, onStartParams:true, onUpdateParams:true, onCompleteParams:true, onOverwriteParams:true, onStartScope:true, onUpdateScope:true, onCompleteScope:true, onOverwriteScope:true, onErrorScope:true, quickAdd:true};
   121  		var modifiedProperties:Object = new Object();
   122  		for (istr in p_obj) {
   123  			if (!restrictedWords[istr]) {
   124  				// It's an additional pair, so adds
   125  				if (_specialPropertySplitterList[istr] != undefined) {
   126  					// Special property splitter
   127  					var splitProperties:Array = _specialPropertySplitterList[istr].splitValues(p_obj[istr], _specialPropertySplitterList[istr].parameters);
   128  					for (i = 0; i < splitProperties.length; i++) {
   129  						if (_specialPropertySplitterList[splitProperties[i].name] != undefined) {
   130  							var splitProperties2:Array = _specialPropertySplitterList[splitProperties[i].name].splitValues(splitProperties[i].value, _specialPropertySplitterList[splitProperties[i].name].parameters);
   131  							for (j = 0; j < splitProperties2.length; j++) {
   132  								rProperties[splitProperties2[j].name] = {valueStart:undefined, valueComplete:splitProperties2[j].value, arrayIndex:splitProperties2[j].arrayIndex, isSpecialProperty:false};
   133  							}
   134  						} else {
   135  							rProperties[splitProperties[i].name] = {valueStart:undefined, valueComplete:splitProperties[i].value, arrayIndex:splitProperties[i].arrayIndex, isSpecialProperty:false};
   136  						}
   137  					}
   138  				} else if (_specialPropertyModifierList[istr] != undefined) {
   139  					// Special property modifier
   140  					var tempModifiedProperties:Array = _specialPropertyModifierList[istr].modifyValues(p_obj[istr]);
   141  					for (i = 0; i < tempModifiedProperties.length; i++) {
   142  						modifiedProperties[tempModifiedProperties[i].name] = {modifierParameters:tempModifiedProperties[i].parameters, modifierFunction:_specialPropertyModifierList[istr].getValue};
   143  					}
   144  				} else {
   145  					// Regular property or special property, just add the property normally
   146  					rProperties[istr] = {valueStart:undefined, valueComplete:p_obj[istr]};
   147  				}
   148  			}
   149  		}
   150  
   151  		// Verifies whether the properties exist or not, for warning messages
   152  		for (istr in rProperties) {
   153  			if (_specialPropertyList[istr] != undefined) {
   154  				rProperties[istr].isSpecialProperty = true;
   155  			} else {
   156  				if (rScopes[0][istr] == undefined) {
   157  					printError("The property '" + istr + "' doesn't seem to be a normal object property of " + rScopes[0].toString() + " or a registered special property.");
   158  				}
   159  			}
   160  		}
   161  
   162  		// Adds the modifiers to the list of properties
   163  		for (istr in modifiedProperties) {
   164  			if (rProperties[istr] != undefined) {
   165  				rProperties[istr].modifierParameters = modifiedProperties[istr].modifierParameters;
   166  				rProperties[istr].modifierFunction = modifiedProperties[istr].modifierFunction;
   167  			}
   168  			
   169  		}
   170  		
   171  		var rTransition:Function; // Real transition
   172  
   173  		if (typeof p_obj.transition == "string") {
   174  			// String parameter, transition names
   175  			var trans:String = p_obj.transition.toLowerCase();
   176  			rTransition = _transitionList[trans];
   177  		} else {
   178  			// Proper transition function
   179  			rTransition = p_obj.transition;
   180  		}
   181  		if (rTransition == undefined) rTransition = _transitionList["easeoutexpo"];
   182  
   183  		var nProperties:Object;
   184  		var nTween:TweenListObj;
   185  		var myT:Number;
   186  
   187  		for (i = 0; i < rScopes.length; i++) {
   188  			// Makes a copy of the properties
   189  			nProperties = new Object();
   190  			for (istr in rProperties) {
   191  				nProperties[istr] = new PropertyInfoObj(rProperties[istr].valueStart, rProperties[istr].valueComplete, rProperties[istr].valueComplete, rProperties[istr].arrayIndex, {}, rProperties[istr].isSpecialProperty, rProperties[istr].modifierFunction, rProperties[istr].modifierParameters);
   192  			}
   193  
   194  			if (p_obj.useFrames == true) {
   195  				nTween = new TweenListObj(
   196  					/* scope			*/	rScopes[i],
   197  					/* timeStart		*/	_currentTimeFrame + (rDelay / _timeScale),
   198  					/* timeComplete		*/	_currentTimeFrame + ((rDelay + rTime) / _timeScale),
   199  					/* useFrames		*/	true,
   200  					/* transition		*/	rTransition,
   201  											p_obj.transitionParams
   202  				);
   203  			} else {
   204  				nTween = new TweenListObj(
   205  					/* scope			*/	rScopes[i],
   206  					/* timeStart		*/	_currentTime + ((rDelay * 1000) / _timeScale),
   207  					/* timeComplete		*/	_currentTime + (((rDelay * 1000) + (rTime * 1000)) / _timeScale),
   208  					/* useFrames		*/	false,
   209  					/* transition		*/	rTransition,
   210  											p_obj.transitionParams
   211  				);
   212  			}
   213  
   214  			nTween.properties			=	nProperties;
   215  			nTween.onStart				=	p_obj.onStart;
   216  			nTween.onUpdate				=	p_obj.onUpdate;
   217  			nTween.onComplete			=	p_obj.onComplete;
   218  			nTween.onOverwrite			=	p_obj.onOverwrite;
   219  			nTween.onError              =   p_obj.onError;
   220  			nTween.onStartParams		=	p_obj.onStartParams;
   221  			nTween.onUpdateParams		=	p_obj.onUpdateParams;
   222  			nTween.onCompleteParams		=	p_obj.onCompleteParams;
   223  			nTween.onOverwriteParams	=	p_obj.onOverwriteParams;
   224  			nTween.onStartScope			=	p_obj.onStartScope;
   225  			nTween.onUpdateScope		=	p_obj.onUpdateScope;
   226  			nTween.onCompleteScope		=	p_obj.onCompleteScope;
   227  			nTween.onOverwriteScope		=	p_obj.onOverwriteScope;
   228  			nTween.onErrorScope			=	p_obj.onErrorScope;
   229  			nTween.rounded				=	p_obj.rounded;
   230  			nTween.skipUpdates			=	p_obj.skipUpdates;
   231  
   232  			// Remove other tweenings that occur at the same time
   233  			if (!p_obj.quickAdd) removeTweensByTime(nTween.scope, nTween.properties, nTween.timeStart, nTween.timeComplete);
   234  
   235  			// And finally adds it to the list
   236  			_tweenList.push(nTween);
   237  
   238  			// Immediate update and removal if it's an immediate tween -- if not deleted, it executes at the end of this frame execution
   239  			if (rTime == 0 && rDelay == 0) {
   240  				myT = _tweenList.length-1;
   241  				updateTweenByIndex(myT);
   242  				removeTweenByIndex(myT);
   243  			}
   244  		}
   245  
   246  		return true;
   247  	}
   248  
   249  	// A "caller" is like this: [          |     |  | ||] got it? :)
   250  	// this function is crap - should be fixed later/extend on addTween()
   251  
   252  	/**
   253  	 * Adds a new *caller* tweening
   254  	 *
   255  	 * @param		(first-n param)		Object				Object that should be tweened: a movieclip, textfield, etc.. OR an array of objects
   256  	 * @param		(last param)		Object				Object containing the specified parameters in any order, as well as the properties that should be tweened and their values
   257  	 * @param		.time				Number				Time in seconds or frames for the tweening to take (defaults 2)
   258  	 * @param		.delay				Number				Delay time (defaults 0)
   259  	 * @param		.count				Number				Number of times this caller should be called
   260  	 * @param		.transition			String/Function		Type of transition equation... (defaults to "easeoutexpo")
   261  	 * @param		.onStart			Function			Event called when tween starts
   262  	 * @param		.onUpdate			Function			Event called when tween updates
   263  	 * @param		.onComplete			Function			Event called when tween ends
   264  	 * @param		.waitFrames			Boolean				Whether to wait (or not) one frame for each call
   265  	 * @return							Boolean				TRUE if the tween was successfully added, FALSE if otherwise
   266  	 */
   267  	public static function addCaller (p_scopes:Object, p_parameters:Object):Boolean {
   268  		if (p_scopes == undefined) return false;
   269  
   270  		var i:Number;
   271  
   272  		var rScopes:Array; // List of objects to tween
   273  		if (p_scopes instanceof Array) {
   274  			// The first argument is an array
   275  			rScopes = p_scopes.concat();
   276  		} else {
   277  			// The first argument(s) is(are) object(s)
   278  			rScopes = [p_scopes];
   279  		}
   280  
   281  		var p_obj:Object = p_parameters;
   282  
   283  		// Creates the main engine if it isn't active
   284  		if (!_inited) init();
   285  		if (!_engineExists || _root[getControllerName()] == undefined) startEngine(); // Quick fix for Flash not resetting the vars on double ctrl+enter...
   286  
   287  		// Creates a "safer", more strict tweening object
   288  		var rTime:Number = (isNaN(p_obj.time) ? 0 : p_obj.time); // Real time
   289  		var rDelay:Number = (isNaN(p_obj.delay) ? 0 : p_obj.delay); // Real delay
   290  
   291  		var rTransition:Function; // Real transition
   292  		if (typeof p_obj.transition == "string") {
   293  			// String parameter, transition names
   294  			var trans:String = p_obj.transition.toLowerCase();
   295  			rTransition = _transitionList[trans];
   296  		} else {
   297  			// Proper transition function
   298  			rTransition = p_obj.transition;
   299  		}
   300  		if (rTransition == undefined) rTransition = _transitionList["easeoutexpo"];
   301  
   302  		var nTween:TweenListObj;
   303  		var myT:Number;
   304  		for (i = 0; i < rScopes.length; i++) {
   305  
   306  			if (p_obj.useFrames == true) {
   307  				nTween = new TweenListObj(
   308  					/* scope			*/	rScopes[i],
   309  					/* timeStart		*/	_currentTimeFrame + (rDelay / _timeScale),
   310  					/* timeComplete		*/	_currentTimeFrame + ((rDelay + rTime) / _timeScale),
   311  					/* useFrames		*/	true,
   312  					/* transition		*/	rTransition,
   313  											p_obj.transitionParams
   314  				);
   315  			} else {
   316  				nTween = new TweenListObj(
   317  					/* scope			*/	rScopes[i],
   318  					/* timeStart		*/	_currentTime + ((rDelay * 1000) / _timeScale),
   319  					/* timeComplete		*/	_currentTime + (((rDelay * 1000) + (rTime * 1000)) / _timeScale),
   320  					/* useFrames		*/	false,
   321  					/* transition		*/	rTransition,
   322  											p_obj.transitionParams
   323  				);
   324  			}
   325  
   326  			nTween.properties			=	undefined;
   327  			nTween.onStart				=	p_obj.onStart;
   328  			nTween.onUpdate				=	p_obj.onUpdate;
   329  			nTween.onComplete			=	p_obj.onComplete;
   330  			nTween.onOverwrite			=	p_obj.onOverwrite;
   331  			nTween.onStartParams		=	p_obj.onStartParams;
   332  			nTween.onUpdateParams		=	p_obj.onUpdateParams;
   333  			nTween.onCompleteParams		=	p_obj.onCompleteParams;
   334  			nTween.onOverwriteParams	=	p_obj.onOverwriteParams;
   335  			nTween.onStartScope			=	p_obj.onStartScope;
   336  			nTween.onUpdateScope		=	p_obj.onUpdateScope;
   337  			nTween.onCompleteScope		=	p_obj.onCompleteScope;
   338  			nTween.onOverwriteScope		=	p_obj.onOverwriteScope;
   339  			nTween.onErrorScope			=	p_obj.onErrorScope;
   340  			nTween.isCaller				=	true;
   341  			nTween.count				=	p_obj.count;
   342  			nTween.waitFrames			=	p_obj.waitFrames;
   343  
   344  			// And finally adds it to the list
   345  			_tweenList.push(nTween);
   346  
   347  			// Immediate update and removal if it's an immediate tween -- if not deleted, it executes at the end of this frame execution
   348  			if (rTime == 0 && rDelay == 0) {
   349  				myT = _tweenList.length-1;
   350  				updateTweenByIndex(myT);
   351  				removeTweenByIndex(myT);
   352  			}
   353  		}
   354  
   355  		return true;
   356  	}
   357  
   358  	/**
   359  	 * Remove an specified tweening of a specified object the tweening list, if it conflicts with the given time
   360  	 *
   361  	 * @param		p_scope				Object						List of objects affected
   362  	 * @param		p_properties		Object 						List of properties affected (PropertyInfoObj instances)
   363  	 * @param		p_timeStart			Number						Time when the new tween starts
   364  	 * @param		p_timeComplete		Number						Time when the new tween ends
   365  	 * @return							Boolean						Whether or not it actually deleted something
   366  	 */
   367  	public static function removeTweensByTime (p_scope:Object, p_properties:Object, p_timeStart:Number, p_timeComplete:Number):Boolean {
   368  		var removed:Boolean = false;
   369  		var removedLocally:Boolean;
   370  
   371  		var i:Number;
   372  		var tl:Number = _tweenList.length;
   373  		var pName:String;
   374  
   375  		for (i = 0; i < tl; i++) {
   376  			if (p_scope == _tweenList[i].scope) {
   377  				// Same object...
   378  				if (p_timeComplete > _tweenList[i].timeStart && p_timeStart < _tweenList[i].timeComplete) {
   379  					// New time should override the old one...
   380  					removedLocally = false;
   381  					for (pName in _tweenList[i].properties) {
   382  						if (p_properties[pName] != undefined) {
   383  							// Same object, same property
   384  							// Finally, remove this old tweening and use the new one
   385  							if (_tweenList[i].onOverwrite != undefined) {
   386  								var eventScope:Object = _tweenList[i].onOverwriteScope != undefined ? _tweenList[i].onOverwriteScope : _tweenList[i].scope;
   387  								try {
   388  									_tweenList[i].onOverwrite.apply(eventScope, _tweenList[i].onOverwriteParams);
   389  								} catch(e:Error) {
   390  									handleError(_tweenList[i], e, "onOverwrite");
   391  								}
   392  							}
   393  							_tweenList[i].properties[pName] = undefined;
   394  							delete _tweenList[i].properties[pName];
   395  							removedLocally = true;
   396  							removed = true;
   397  						}
   398  					}
   399  					if (removedLocally) {
   400  						// Verify if this can be deleted
   401  						if (AuxFunctions.getObjectLength(_tweenList[i].properties) == 0) removeTweenByIndex(i);
   402  					}
   403  				}
   404  			}
   405  		}
   406  
   407  		return removed;
   408  	}
   409  
   410  	/**
   411  	 * Remove tweenings from a given object from the tweening list
   412  	 *
   413  	 * @param		p_tween				Object		Object that must have its tweens removed
   414  	 * @param		(2nd-last params)	Object		Property(ies) that must be removed
   415  	 * @return							Boolean		Whether or not it successfully removed this tweening
   416  	 */
   417  	public static function removeTweens (p_scope:Object):Boolean {
   418  		// Create the property list
   419  		var properties:Array = new Array();
   420  		var i:Number;
   421  		for (i = 1; i < arguments.length; i++) {
   422  			if (typeof(arguments[i]) == "string" && !AuxFunctions.isInArray(arguments[i], properties)) properties.push(arguments[i]);
   423  		}
   424  		// Call the affect function on the specified properties
   425  		return affectTweens(removeTweenByIndex, p_scope, properties);
   426  	}
   427  
   428  	/**
   429  	 * Remove all tweenings from the engine
   430  	 *
   431  	 * @return							Boolean		Whether or not it successfully removed a tweening
   432  	 */
   433  	public static function removeAllTweens ():Boolean {
   434  		var removed:Boolean = false;
   435  		var i:Number;
   436  		for (i = 0; i < _tweenList.length; i++) {
   437  			removeTweenByIndex(i);
   438  			removed = true;
   439  		}
   440  		return removed;
   441  	}
   442  
   443  	/**
   444  	 * Pause tweenings from a given object
   445  	 *
   446  	 * @param		p_scope				Object		Object that must have its tweens paused
   447  	 * @param		(2nd-last params)	Object		Property(ies) that must be paused
   448  	 * @return							Boolean		Whether or not it successfully paused something
   449  	 */
   450  	public static function pauseTweens (p_scope:Object):Boolean {
   451  		// Create the property list
   452  		var properties:Array = new Array();
   453  		var i:Number;
   454  		for (i = 1; i < arguments.length; i++) {
   455  			if (typeof(arguments[i]) == "string" && !AuxFunctions.isInArray(arguments[i], properties)) properties.push(arguments[i]);
   456  		}
   457  		// Call the affect function on the specified properties
   458  		return affectTweens(pauseTweenByIndex, p_scope, properties);
   459  	}
   460  
   461  	/**
   462  	 * Pause all tweenings on the engine
   463  	 *
   464  	 * @return							Boolean		Whether or not it successfully paused a tweening
   465  	 */
   466  	public static function pauseAllTweens ():Boolean {
   467  		var paused:Boolean = false;
   468  		var i:Number;
   469  		for (i = 0; i < _tweenList.length; i++) {
   470  			pauseTweenByIndex(i);
   471  			paused = true;
   472  		}
   473  		return paused;
   474  	}
   475  
   476  	/**
   477  	 * Resume tweenings from a given object
   478  	 *
   479  	 * @param		p_scope				Object		Object that must have its tweens resumed
   480  	 * @param		(2nd-last params)	Object		Property(ies) that must be resumed
   481  	 * @return							Boolean		Whether or not it successfully resumed something
   482  	 */
   483  	public static function resumeTweens (p_scope:Object):Boolean {
   484  		// Create the property list
   485  		var properties:Array = new Array();
   486  		var i:Number;
   487  		for (i = 1; i < arguments.length; i++) {
   488  			if (typeof(arguments[i]) == "string" && !AuxFunctions.isInArray(arguments[i], properties)) properties.push(arguments[i]);
   489  		}
   490  		// Call the affect function on the specified properties
   491  		return affectTweens(resumeTweenByIndex, p_scope, properties);
   492  	}
   493  
   494  	/**
   495  	 * Resume all tweenings on the engine
   496  	 *
   497  	 * @return							Boolean		Whether or not it successfully resumed a tweening
   498  	 */
   499  	public static function resumeAllTweens ():Boolean {
   500  		var resumed:Boolean = false;
   501  		var i:Number;
   502  		for (i = 0; i < _tweenList.length; i++) {
   503  			resumeTweenByIndex(i);
   504  			resumed = true;
   505  		}
   506  		return resumed;
   507  	}
   508  
   509  	/**
   510  	 * Do some generic action on specific tweenings (pause, resume, remove, more?)
   511  	 *
   512  	 * @param		p_function			Function	Function to run on the tweenings that match
   513  	 * @param		p_scope				Object		Object that must have its tweens affected by the function
   514  	 * @param		p_properties		Array		Array of strings that must be affected
   515  	 * @return							Boolean		Whether or not it successfully affected something
   516  	 */
   517  	private static function affectTweens (p_affectFunction:Function, p_scope:Object, p_properties:Array):Boolean {
   518  		var affected:Boolean = false;
   519  		var i:Number;
   520  
   521  		if (!_tweenList) return false;
   522  
   523  		for (i = 0; i < _tweenList.length; i++) {
   524  			if (_tweenList[i].scope == p_scope) {
   525  				if (p_properties.length == 0) {
   526  					// Can affect everything
   527  					p_affectFunction(i);
   528  					affected = true;
   529  				} else {
   530  					// Must check whether this tween must have specific properties affected
   531  					var affectedProperties:Array = new Array();
   532  					var j:Number;
   533  					for (j = 0; j < p_properties.length; j++) {
   534  						if (_tweenList[i].properties[p_properties[j]] != undefined) {
   535  							affectedProperties.push(p_properties[j]);
   536  						}
   537  					}
   538  					if (affectedProperties.length > 0) {
   539  						// This tween has some properties that need to be affected
   540  						var objectProperties:Number = AuxFunctions.getObjectLength(_tweenList[i].properties);
   541  						if (objectProperties == affectedProperties.length) {
   542  							// The list of properties is the same as all properties, so affect it all
   543  							p_affectFunction(i);
   544  							affected = true;
   545  						} else {
   546  							// The properties are mixed, so split the tween and affect only certain specific properties
   547  							var slicedTweenIndex:Number = splitTweens(i, affectedProperties);
   548  							p_affectFunction(slicedTweenIndex);
   549  							affected = true;
   550  						}
   551  					}
   552  				}
   553  			}
   554  		}
   555  		return affected;
   556  	}
   557  
   558  	/**
   559  	 * Splits a tweening in two
   560  	 *
   561  	 * @param		p_tween				Number		Object that must have its tweens split
   562  	 * @param		p_properties		Array		Array of strings containing the list of properties that must be separated
   563  	 * @return							Number		The index number of the new tween
   564  	 */
   565  	public static function splitTweens (p_tween:Number, p_properties:Array):Number {
   566  		// First, duplicates
   567  		var originalTween:TweenListObj = _tweenList[p_tween];
   568  		var newTween:TweenListObj = originalTween.clone(false);
   569  
   570  		// Now, removes tweenings where needed
   571  		var i:Number;
   572  		var pName:String;
   573  
   574  		// Removes the specified properties from the old one
   575  		for (i = 0; i < p_properties.length; i++) {
   576  			pName = p_properties[i];
   577  			if (originalTween.properties[pName] != undefined) {
   578  				originalTween.properties[pName] = undefined;
   579  				delete originalTween.properties[pName];
   580  			}
   581  		}
   582  
   583  		// Removes the unspecified properties from the new one
   584  		var found:Boolean;
   585  		for (pName in newTween.properties) {
   586  			found = false;
   587  			for (i = 0; i < p_properties.length; i++) {
   588  				if (p_properties[i] == pName) {
   589  					found = true;
   590  					break;
   591  				}
   592  			}
   593  			if (!found) {
   594  				newTween.properties[pName] = undefined;
   595  				delete newTween.properties[pName];
   596  			}
   597  		}
   598  
   599  		// If there are empty property lists, a cleanup is done on the next updateTweens() cycle
   600  		_tweenList.push(newTween);
   601  		return (_tweenList.length - 1);
   602  		
   603  	}
   604  
   605  
   606  	// ==================================================================================================================================
   607  	// ENGINE functions -----------------------------------------------------------------------------------------------------------------
   608  
   609  	/**
   610  	 * Updates all existing tweenings
   611  	 *
   612  	 * @return							Boolean		FALSE if no update was made because there's no tweening (even delayed ones)
   613  	 */
   614  	private static function updateTweens ():Boolean {
   615  		if (_tweenList.length == 0) return false;
   616  		var i:Number;
   617  		for (i = 0; i < _tweenList.length; i++) {
   618  			// Looping throught each Tweening and updating the values accordingly
   619  			if (!_tweenList[i].isPaused) {
   620  				if (!updateTweenByIndex(i)) removeTweenByIndex(i);
   621  				if (_tweenList[i] == null) {
   622  					removeTweenByIndex(i, true);
   623  					i--;
   624  				}
   625  			}
   626  		}
   627  
   628  		return true;
   629  	}
   630  
   631  	/**
   632  	 * Remove an specific tweening from the tweening list
   633  	 *
   634  	 * @param		p_tween				Number		Index of the tween to be removed on the tweenings list
   635  	 * @return							Boolean		Whether or not it successfully removed this tweening
   636  	 */
   637  	public static function removeTweenByIndex (p_tween:Number, p_finalRemoval:Boolean):Boolean {
   638  		_tweenList[p_tween] = null;
   639  		if (p_finalRemoval) _tweenList.splice(p_tween, 1);
   640  		return true;
   641  	}
   642  
   643  	/**
   644  	 * Pauses an specific tween
   645  	 *
   646  	 * @param		p_tween				Number		Index of the tween to be paused
   647  	 * @return							Boolean		Whether or not it successfully paused this tweening
   648  	 */
   649  	public static function pauseTweenByIndex (p_tween:Number):Boolean {
   650  		var tTweening:Object = _tweenList[p_tween];	// Shortcut to this tweening
   651  		if (tTweening == null || tTweening.isPaused) return false;
   652  		tTweening.timePaused = getCurrentTweeningTime(tTweening);
   653  		tTweening.isPaused = true;
   654  
   655  		return true;
   656  	}
   657  
   658  	/**
   659  	 * Resumes an specific tween
   660  	 *
   661  	 * @param		p_tween				Number		Index of the tween to be resumed
   662  	 * @return							Boolean		Whether or not it successfully resumed this tweening
   663  	 */
   664  	public static function resumeTweenByIndex (p_tween:Number):Boolean {
   665  		var tTweening:Object = _tweenList[p_tween];	// Shortcut to this tweening
   666  		if (tTweening == null || !tTweening.isPaused) return false;
   667  		var cTime:Number = getCurrentTweeningTime(tTweening);
   668  		tTweening.timeStart += cTime - tTweening.timePaused;
   669  		tTweening.timeComplete += cTime - tTweening.timePaused;
   670  		tTweening.timePaused = undefined;
   671  		tTweening.isPaused = false;
   672  
   673  		return true;
   674  	}
   675  
   676  	/**
   677  	 * Updates an specific tween
   678  	 *
   679  	 * @param		i					Number		Index (from the tween list) of the tween that should be updated
   680  	 * @return							Boolean		FALSE if it's already finished and should be deleted, TRUE if otherwise
   681  	 */
   682  	private static function updateTweenByIndex (i:Number):Boolean {
   683  
   684  		var tTweening:Object = _tweenList[i];	// Shortcut to this tweening
   685  
   686  		if (tTweening == null || !tTweening.scope) return false;
   687  
   688  		var isOver:Boolean = false;		// Whether or not it's over the update time
   689  		var mustUpdate:Boolean;			// Whether or not it should be updated (skipped if false)
   690  
   691  		var nv:Number;					// New value for each property
   692  
   693  		var t:Number;					// current time (frames, seconds)
   694  		var b:Number;					// beginning value
   695  		var c:Number;					// change in value
   696  		var d:Number; 					// duration (frames, seconds)
   697  
   698  		var pName:String;				// Property name, used in loops
   699  		var eventScope:Object;			// Event scope, used to call functions
   700  
   701  		// Shortcut stuff for speed
   702  		var tScope:Object;				// Current scope
   703  		var cTime:Number = getCurrentTweeningTime(tTweening);
   704  		var tProperty:Object;			// Property being checked
   705  
   706  		if (cTime >= tTweening.timeStart) {
   707  			// Can already start
   708  
   709  			tScope = tTweening.scope;
   710  
   711  			if (tTweening.isCaller) {
   712  				// It's a 'caller' tween
   713  				do {
   714  					t = ((tTweening.timeComplete - tTweening.timeStart)/tTweening.count) * (tTweening.timesCalled+1);
   715  					b = tTweening.timeStart;
   716  					c = tTweening.timeComplete - tTweening.timeStart;
   717  					d = tTweening.timeComplete - tTweening.timeStart;
   718  					nv = tTweening.transition(t, b, c, d, tTweening.transitionParams);
   719  
   720  					if (cTime >= nv) {
   721  						if (tTweening.onUpdate != undefined) {
   722  							eventScope = tTweening.onUpdateScope != undefined ? tTweening.onUpdateScope : tScope;
   723  							try {
   724  								tTweening.onUpdate.apply(eventScope, tTweening.onUpdateParams);
   725  							} catch(e:Error) {
   726  								handleError(tTweening, e, "onUpdate");
   727  							}
   728  						}
   729  
   730  						tTweening.timesCalled++;
   731  						if (tTweening.timesCalled >= tTweening.count) {
   732  							isOver = true;
   733  							break;
   734  						}
   735  						if (tTweening.waitFrames) break;
   736  					}
   737  
   738  				} while (cTime >= nv);
   739  			} else {
   740  				// It's a normal transition tween
   741  
   742  				mustUpdate = tTweening.skipUpdates < 1 || tTweening.skipUpdates == undefined || tTweening.updatesSkipped >= tTweening.skipUpdates;
   743  
   744  				if (cTime >= tTweening.timeComplete) {
   745  					isOver = true;
   746  					mustUpdate = true;
   747  				}
   748  
   749  				if (!tTweening.hasStarted) {
   750  					// First update, read all default values (for proper filter tweening)
   751  					if (tTweening.onStart != undefined) {
   752  						eventScope = tTweening.onStartScope != undefined ? tTweening.onStartScope : tScope;
   753  						try {
   754  							tTweening.onStart.apply(eventScope, tTweening.onStartParams);
   755  						} catch(e:Error) {
   756  							handleError(tTweening, e, "onStart");
   757  						}
   758  					}
   759  					var pv:Number;
   760  					for (pName in tTweening.properties) {
   761  						if (tTweening.properties[pName].isSpecialProperty) {
   762  							// It's a special property, tunnel via the special property function
   763  							if (_specialPropertyList[pName].preProcess != undefined) {
   764  								tTweening.properties[pName].valueComplete = _specialPropertyList[pName].preProcess(tScope, _specialPropertyList[pName].parameters, tTweening.properties[pName].originalValueComplete, tTweening.properties[pName].extra);
   765  							}
   766  							pv = _specialPropertyList[pName].getValue(tScope, _specialPropertyList[pName].parameters, tTweening.properties[pName].extra);
   767  						} else {
   768  							// Directly read property
   769  							pv = tScope[pName];
   770  						}
   771  						tTweening.properties[pName].valueStart = isNaN(pv) ? tTweening.properties[pName].valueComplete : pv;
   772  					}
   773  					mustUpdate = true;
   774  					tTweening.hasStarted = true;
   775  				}
   776  
   777  				if (mustUpdate) {
   778  					for (pName in tTweening.properties) {
   779  						tProperty = tTweening.properties[pName];
   780  
   781  						if (isOver) {
   782  							// Tweening time has finished, just set it to the final value
   783  							nv = tProperty.valueComplete;
   784  						} else {
   785  							if (tProperty.hasModifier) {
   786  								// Modified
   787  								t = cTime - tTweening.timeStart;
   788  								d = tTweening.timeComplete - tTweening.timeStart;
   789  								nv = tTweening.transition(t, 0, 1, d, tTweening.transitionParams);
   790  								nv = tProperty.modifierFunction(tProperty.valueStart, tProperty.valueComplete, nv, tProperty.modifierParameters);
   791  							} else {
   792  								// Normal update
   793  								t = cTime - tTweening.timeStart;
   794  								b = tProperty.valueStart;
   795  								c = tProperty.valueComplete - tProperty.valueStart;
   796  								d = tTweening.timeComplete - tTweening.timeStart;
   797  								nv = tTweening.transition(t, b, c, d, tTweening.transitionParams);
   798  							}
   799  						}
   800  
   801  						if (tTweening.rounded) nv = Math.round(nv);
   802  						if (tProperty.isSpecialProperty) {
   803  							// It's a special property, tunnel via the special property method
   804  							_specialPropertyList[pName].setValue(tScope, nv, _specialPropertyList[pName].parameters, tTweening.properties[pName].extra);
   805  						} else {
   806  							// Directly set property
   807  							tScope[pName] = nv;
   808  						}
   809  					}
   810  
   811  					tTweening.updatesSkipped = 0;
   812  
   813  					if (tTweening.onUpdate != undefined) {
   814  						eventScope = tTweening.onUpdateScope != undefined ? tTweening.onUpdateScope : tScope;
   815  						try {
   816  							tTweening.onUpdate.apply(eventScope, tTweening.onUpdateParams);
   817  						} catch(e:Error) {
   818  							handleError(tTweening, e, "onUpdate");
   819  						}
   820  					}
   821  				} else {
   822  					tTweening.updatesSkipped++;
   823  				}
   824  			}
   825  
   826  			if (isOver && tTweening.onComplete != undefined) {
   827  				eventScope = tTweening.onCompleteScope != undefined ? tTweening.onCompleteScope : tScope;
   828  				try {
   829  					tTweening.onComplete.apply(eventScope, tTweening.onCompleteParams);
   830  				} catch(e:Error) {
   831  					handleError(tTweening, e, "onComplete");
   832  				}
   833  			}
   834  
   835  			return (!isOver);
   836  		}
   837  
   838  		// On delay, hasn't started, so return true
   839  		return (true);
   840  
   841  	}
   842  
   843  	/**
   844  	 * Initiates the Tweener. Should only be ran once
   845  	 */
   846  	private static function init():Void {
   847  		_inited = true;
   848  
   849  		// Registers all default equations
   850  		_transitionList = new Object();
   851  		Equations.init();
   852  
   853  		// Registers all default special properties
   854  		_specialPropertyList = new Object();
   855  		_specialPropertyModifierList = new Object();
   856  		_specialPropertySplitterList = new Object();
   857  	}
   858  
   859  	/**
   860  	 * Adds a new function to the available transition list "shortcuts"
   861  	 *
   862  	 * @param		p_name				String		Shorthand transition name
   863  	 * @param		p_function			Function	The proper equation function
   864  	 */
   865  	public static function registerTransition(p_name:String, p_function:Function): Void {
   866  		if (!_inited) init();
   867  		_transitionList[p_name] = p_function;
   868  	}
   869  
   870  	/**
   871  	 * Adds a new special property to the available special property list.
   872  	 *
   873  	 * @param		p_name				Name of the "special" property.
   874  	 * @param		p_getFunction		Function that gets the value.
   875  	 * @param		p_setFunction		Function that sets the value.
   876  	 */
   877  	public static function registerSpecialProperty(p_name:String, p_getFunction:Function, p_setFunction:Function, p_parameters:Array, p_preProcessFunction:Function): Void {
   878  		if (!_inited) init();
   879  		var sp:SpecialProperty = new SpecialProperty(p_getFunction, p_setFunction, p_parameters, p_preProcessFunction);
   880  		_specialPropertyList[p_name] = sp;
   881  	}
   882  
   883  	/**
   884  	 * Adds a new special property modifier to the available modifier list.
   885  	 *
   886  	 * @param		p_name				Name of the "special" property modifier.
   887  	 * @param		p_modifyFunction	Function that modifies the value.
   888  	 * @param		p_getFunction		Function that gets the value.
   889  	 */
   890  	public static function registerSpecialPropertyModifier(p_name:String, p_modifyFunction:Function, p_getFunction:Function): Void {
   891  		if (!_inited) init();
   892  		var spm:SpecialPropertyModifier = new SpecialPropertyModifier(p_modifyFunction, p_getFunction);
   893  		_specialPropertyModifierList[p_name] = spm;
   894  	}
   895  
   896  	/**
   897  	 * Adds a new special property splitter to the available splitter list.
   898  	 *
   899  	 * @param		p_name				Name of the "special" property splitter.
   900  	 * @param		p_splitFunction		Function that splits the value.
   901  	 */
   902  	public static function registerSpecialPropertySplitter(p_name:String, p_splitFunction:Function, p_parameters:Array): Void {
   903  		if (!_inited) init();
   904  		var sps:SpecialPropertySplitter = new SpecialPropertySplitter(p_splitFunction, p_parameters);
   905  		_specialPropertySplitterList[p_name] = sps;
   906  	}
   907  
   908  	/**
   909  	 * Starts the Tweener class engine. It is supposed to be running every time a tween exists
   910  	 */
   911  	private static function startEngine():Void {
   912  		_engineExists = true;
   913  		_tweenList = new Array();
   914  
   915  		var randomDepth:Number = Math.floor(Math.random() * 999999);
   916  		var fmc:MovieClip = _root.createEmptyMovieClip(getControllerName(), 31338+randomDepth);
   917  		fmc.onEnterFrame = function(): Void {
   918  			Tweener.onEnterFrame();
   919  		};
   920  
   921  		_currentTimeFrame = 0;
   922  		updateTime();
   923  	}
   924  
   925  	/**
   926  	 * Stops the Tweener class engine
   927  	 */
   928  	private static function stopEngine():Void {
   929  		_engineExists = false;
   930  		_tweenList = null;
   931  		_currentTime = 0;
   932  		_currentTimeFrame = 0;
   933  		delete _root[getControllerName()].onEnterFrame;
   934  		_root[getControllerName()].removeMovieClip();
   935  	}
   936  
   937  	/**
   938  	 * Updates the time to enforce time grid-based updates
   939  	 */
   940  	public static function updateTime():Void {
   941  		_currentTime = getTimer();
   942  	}
   943  
   944  	/**
   945  	 * Updates the current frame count
   946  	 */
   947  	public static function updateFrame():Void {
   948  		_currentTimeFrame++;
   949  	}
   950  
   951  	/**
   952  	 * Ran once every frame. It's the main engine, updates all existing tweenings.
   953  	 */
   954  	public static function onEnterFrame():Void {
   955  		updateTime();
   956  		updateFrame();
   957  		var hasUpdated:Boolean = false;
   958  		hasUpdated = updateTweens();
   959  		if (!hasUpdated) stopEngine();	// There's no tweening to update or wait, so it's better to stop the engine
   960  	}
   961  
   962  	/**
   963  	 * Sets the new time scale.
   964  	 *
   965  	 * @param		p_time				Number		New time scale (0.5 = slow, 1 = normal, 2 = 2x fast forward, etc)
   966  	 */
   967  	public static function setTimeScale(p_time:Number):Void {
   968  		var i:Number;
   969  		var cTime:Number;
   970  
   971  		if (isNaN(p_time)) p_time = 1;
   972  		if (p_time < 0.00001) p_time = 0.00001;
   973  		if (p_time != _timeScale) {
   974  			// Multiplies all existing tween times accordingly
   975  			for (i = 0; i<_tweenList.length; i++) {
   976  				cTime = getCurrentTweeningTime(_tweenList[i]);
   977  				_tweenList[i].timeStart = cTime - ((cTime - _tweenList[i].timeStart) * _timeScale / p_time);
   978  				_tweenList[i].timeComplete = cTime - ((cTime - _tweenList[i].timeComplete) * _timeScale / p_time);
   979  				if (_tweenList[i].timePaused != undefined) _tweenList[i].timePaused = cTime - ((cTime - _tweenList[i].timePaused) * _timeScale / p_time);
   980  			}
   981  			// Sets the new timescale value (for new tweenings)
   982  			_timeScale = p_time;
   983  		}
   984  	}
   985  
   986  	// ==================================================================================================================================
   987  	// AUXILIARY functions --------------------------------------------------------------------------------------------------------------
   988  
   989  	/**
   990  	 * Finds whether or not an object has any tweening
   991  	 *
   992  	 * @param		p_scope				Object		Target object
   993  	 * @return							Boolean		Whether or not there's a tweening occuring on this object (paused, delayed, or active)
   994  	 */
   995  	public static function isTweening(p_scope:Object):Boolean {
   996          var i:Number;
   997  
   998          for (i = 0; i<_tweenList.length; i++) {
   999              if (_tweenList[i].scope == p_scope) {
  1000                  return true;
  1001              }
  1002          }
  1003          return false;
  1004      }
  1005  
  1006  	/**
  1007  	 * Return an array containing a list of the properties being tweened for this object
  1008  	 *
  1009  	 * @param		p_scope				Object		Target object
  1010  	 * @return							Array		List of strings with properties being tweened (including delayed or paused)
  1011  	 */
  1012  	public static function getTweens(p_scope:Object):Array {
  1013          var i:Number;
  1014  		var pName:String;
  1015          var tList:Array = new Array();
  1016  
  1017          for (i = 0; i<_tweenList.length; i++) {
  1018              if (_tweenList[i].scope == p_scope) {
  1019  				for (pName in _tweenList[i].properties) tList.push(pName);
  1020              }
  1021          }
  1022  		return tList;
  1023      }
  1024  
  1025  	/**
  1026  	 * Return the number of properties being tweened for this object
  1027  	 *
  1028  	 * @param		p_scope				Object		Target object
  1029  	 * @return							Number		Total count of properties being tweened (including delayed or paused)
  1030  	 */
  1031  	public static function getTweenCount(p_scope:Object):Number {
  1032          var i:Number;
  1033  		var c:Number = 0;
  1034  
  1035          for (i = 0; i<_tweenList.length; i++) {
  1036              if (_tweenList[i].scope == p_scope) {
  1037  				c += AuxFunctions.getObjectLength(_tweenList[i].properties);
  1038              }
  1039          }
  1040  		return c;
  1041      }
  1042  
  1043      /* Handles errors when Tweener executes any callbacks (onStart, onUpdate, etc)
  1044      *  If the TweenListObj specifies an <code>onError</code> callback it well get called, passing the <code>Error</code> object and the current scope as parameters. If no <code>onError</code> callback is specified, it will trace a stackTrace.
  1045      */
  1046      private static function handleError(pTweening : Object, pError : Error, pCallBackName : String) : Void{
  1047          // do we have an error handler?
  1048          if (pTweening.onError != undefined && typeof(pTweening.onError == "function")){
  1049              // yup, there's a handler. Wrap this in a try catch in case the onError throws an error itself.
  1050  			var eventScope:Object = pTweening.onErrorScope != undefined ? pTweening.onErrorScope : pTweening.scope;
  1051              try {
  1052                  pTweening.onError.apply(eventScope, [pTweening.scope, pError]);
  1053              } catch (metaError : Error){
  1054  				printError(pTweening.scope.toString() + " raised an error while executing the 'onError' handler. Original error:\n " + pError +  "\nonError error: " + metaError);
  1055              }
  1056          } else {
  1057              // if handler is undefied or null trace the error message (allows empty onErro's to ignore errors)
  1058              if (pTweening.onError == undefined){
  1059  				printError(pTweening.scope.toString() + " raised an error while executing the '" + pCallBackName.toString() + "'handler. \n" + pError );
  1060              }
  1061          }
  1062      }
  1063  
  1064  	/**
  1065  	 * Get the current tweening time (no matter if it uses frames or time as basis), given a specific tweening
  1066  	 *
  1067  	 * @param		p_tweening				TweenListObj		Tween information
  1068  	 */
  1069  	public static function getCurrentTweeningTime(p_tweening:Object):Number {
  1070  		return p_tweening.useFrames ? _currentTimeFrame : _currentTime;
  1071  	}
  1072  
  1073  	/**
  1074  	 * Return the current tweener version
  1075  	 *
  1076  	 * @return							String		The number of the current Tweener version
  1077  	 */
  1078  	public static function getVersion():String {
  1079  		return "AS2 1.31.69";
  1080      }
  1081  
  1082  	/**
  1083  	 * Return the name for the controller movieclip
  1084  	 *
  1085  	 * @return							String		The number of the current Tweener version
  1086  	 */
  1087  	public static function getControllerName():String {
  1088  		return "__tweener_controller__"+Tweener.getVersion();
  1089      }
  1090  
  1091  
  1092  
  1093  	// ==================================================================================================================================
  1094  	// DEBUG functions ------------------------------------------------------------------------------------------------------------------
  1095  
  1096  	/**
  1097  	 * Output an error message
  1098  	 *
  1099  	 * @param		p_message				String		The error message to output
  1100  	 */
  1101  	public static function printError(p_message:String): Void {
  1102  		//
  1103  		trace("## [Tweener] Error: "+p_message);
  1104  	}
  1105  
  1106  }
  1107