 			var t:Timer = new Timer(delay, 1);
 			t.addEventListener(TimerEvent.TIMER, function(..._):*{ func.apply(null, params); });
 			t.start();
+		}
+		
+		/**
+		 * @param state A session state.
+		 * @return An Array of Arrays, each like [path, value].
+		 */
+		public static function flattenSessionState(state:Object, pathPrefix:Array = null, output:Array = null):Array
+		{
+			if (!pathPrefix)
+				pathPrefix = [];
+			if (!output)
+				output = [];
+			if (DynamicState.isDynamicStateArray(state))
+			{
+				for each (var obj:Object in state)
+				{
+					if (DynamicState.isDynamicState(obj))
+					{
+						var objectName:String = obj[DynamicState.OBJECT_NAME];
+						var className:String = obj[DynamicState.CLASS_NAME];
+						var sessionState:Object = obj[DynamicState.SESSION_STATE];
+						pathPrefix.push(objectName);
+						if (className)
+							output.push([pathPrefix.concat('class'), className]);
+						flattenSessionState(sessionState, pathPrefix, output);
+						pathPrefix.pop();
+					}
+				}
+			}
+			else if (state is Array)
+			{
+				output.push([pathPrefix.concat(), state]);
+			}
+			else if (typeof state === 'object' && state !== null)
+			{
+				for (var key:String in state)
+				{
+					pathPrefix.push(key);
+					flattenSessionState(state[key], pathPrefix, output);
+					pathPrefix.pop();
+				}
+			}
+			else
+			{
+				output.push([pathPrefix.concat(), state]);
+			}
+			
+			return output;
 		}
 		
 		/**

			StandardLib.sortOn(keys, function(key:String):String { return key.split('::').pop(); });
			return keys.map(function(key:String, i:int, a:Array):String { return '\t' + key + ': ' + debugId(object[key]); }).join('\n');
		}
	}
}


package weave.utils
{
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.events.TimerEvent;
