Listing the properties of an object

This is easy for AS2 – you just use a for() statement to go through all the properties the object has:

for (var i in obj) trace(i+" = "+obj[i]);

There would be the property names listed in the Output panel along with their values.

But for AS3, it seems that this is not always working. You would use that for() statement but not all the properties would be listed. It happened to me quite a few times. So, how can you display all the properties of an object ? Well, ActionSscript 3.0 has a useful function called describeType(). It is located in the flash.utils package. It must receive the target object (or even a class name) as argument and it returns a XML object containing the properties and methods of that object/class. Pretty neat, huh ?

Now, I’ve played around with it a little and it seems that there’s a catch to it. If your target object is an instance of a dynamic class (it allows you to add new properties dynamically), the describeType() function will only display the properties and methods listed in the class, without the extra properties you might have added. So, practically the final solution might be the use of both the for() loop and the describeType() function, especially if you want to make sure that all of the properties are listed and you’re not sure whether the object is an instance of a dynamic class or not. Something like this:

1
2
3
4
5
6
7
8
9
10
11
12
import flash.utils.describeType;
 
var obj:Object = new Object();
obj.param1 = "param1";
obj.param2 = 23.3;
obj.param3 = 29;
obj.param4 = false;
 
for (var i:* in obj) trace(i+" :: "+obj[i]);
trace("---------");
var description:XML = describeType(obj);
trace(description);

Displays

param1 :: param1
param2 :: 23.3
param3 :: 29
param4 :: false
---------
<type name="Object" isDynamic="true" isFinal="false" isStatic="false">
  <method name="hasOwnProperty" declaredBy="Object" returnType="Boolean" uri="http://adobe.com/AS3/2006/builtin">
    <parameter index="1" type="*" optional="true"/>
  </method>
  <method name="isPrototypeOf" declaredBy="Object" returnType="Boolean" uri="http://adobe.com/AS3/2006/builtin">
    <parameter index="1" type="*" optional="true"/>
  </method>
  <method name="propertyIsEnumerable" declaredBy="Object" returnType="Boolean" uri="http://adobe.com/AS3/2006/builtin">
    <parameter index="1" type="*" optional="true"/>
  </method>
</type>

If you want to list the properties and methods separately, using the describeType() function, here’s how to do it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import flash.utils.describeType;
import flash.media.Camera;
 
var obj:Camera = new Camera();
var description:XML = describeType(obj);
 
trace("Properties:\n------------------");
for each (var a:XML in description.accessor) trace(a.@name+" : "+a.@type);
 
trace("\n\nMethods:\n------------------");
for each (var m:XML in description.method) {
	trace(m.@name+" : "+m.@returnType);
	if (m.parameter != undefined) {
		trace("     arguments");
		for each (var p:XML in m.parameter) trace("               - "+p.@type);
	}
}

Displays

Properties:
------------------
motionTimeout : int
activityLevel : Number
loopback : Boolean
fps : Number
height : int
width : int
motionLevel : int
bandwidth : int
muted : Boolean
name : String
index : int
keyFrameInterval : int
currentFPS : Number
quality : int
 
 
Methods:
------------------
setKeyFrameInterval : void
     arguments
               - int
setLoopback : void
     arguments
               - Boolean
setQuality : void
     arguments
               - int
               - int
setCursor : void
     arguments
               - Boolean
setMotionLevel : void
     arguments
               - int
               - int
setMode : void
     arguments
               - int
               - int
               - Number
               - Boolean
hasEventListener : Boolean
     arguments
               - String
dispatchEvent : Boolean
     arguments
               - flash.events::Event
removeEventListener : void
     arguments
               - String
               - Function
               - Boolean
willTrigger : Boolean
     arguments
               - String
addEventListener : void
     arguments
               - String
               - Function
               - Boolean
               - int
               - Boolean
toString : String

Please note that the function will not return any static properties the object might have. If you want to get the list of static properties too, you need to provide the class name as argument of the function and not an instance of the class:

1
2
3
4
import flash.utils.describeType;
 
var description:XML = describeType(Event);
trace(description);

Displays

<type name="flash.events::Event" base="Class" isDynamic="true" isFinal="true" isStatic="true">
  <extendsClass type="Class"/>
  <extendsClass type="Object"/>
  <constant name="COMPLETE" type="String"/>
  <constant name="FULLSCREEN" type="String"/>
  <constant name="UNLOAD" type="String"/>
  <constant name="CONNECT" type="String"/>
  <constant name="CLOSE" type="String"/>
  <constant name="SCROLL" type="String"/>
  <constant name="DEACTIVATE" type="String"/>
  <constant name="ENTER_FRAME" type="String"/>
  <constant name="ID3" type="String"/>
  <constant name="SELECT" type="String"/>
  <constant name="SOUND_COMPLETE" type="String"/>
  <constant name="TAB_CHILDREN_CHANGE" type="String"/>
  <constant name="INIT" type="String"/>
  <constant name="TAB_ENABLED_CHANGE" type="String"/>
  <constant name="MOUSE_LEAVE" type="String"/>
  <constant name="ADDED_TO_STAGE" type="String"/>
  <constant name="OPEN" type="String"/>
  <constant name="CANCEL" type="String"/>
  <constant name="REMOVED" type="String"/>
  <constant name="REMOVED_FROM_STAGE" type="String"/>
  <constant name="ADDED" type="String"/>
  <constant name="RENDER" type="String"/>
  <constant name="TAB_INDEX_CHANGE" type="String"/>
  <constant name="CHANGE" type="String"/>
  <constant name="RESIZE" type="String"/>
  <constant name="ACTIVATE" type="String"/>
  <accessor name="prototype" access="readonly" type="*" declaredBy="Class"/>
  <factory type="flash.events::Event">
    <extendsClass type="Object"/>
    <constructor>
      <parameter index="1" type="*" optional="false"/>
      <parameter index="2" type="*" optional="true"/>
      <parameter index="3" type="*" optional="true"/>
    </constructor>
    <method name="stopImmediatePropagation" declaredBy="flash.events::Event" returnType="void"/>
    <accessor name="type" access="readonly" type="String" declaredBy="flash.events::Event"/>
    <method name="preventDefault" declaredBy="flash.events::Event" returnType="void"/>
    <accessor name="currentTarget" access="readonly" type="Object" declaredBy="flash.events::Event"/>
    <method name="toString" declaredBy="flash.events::Event" returnType="String"/>
    <accessor name="target" access="readonly" type="Object" declaredBy="flash.events::Event"/>
    <accessor name="bubbles" access="readonly" type="Boolean" declaredBy="flash.events::Event"/>
    <accessor name="cancelable" access="readonly" type="Boolean" declaredBy="flash.events::Event"/>
    <method name="clone" declaredBy="flash.events::Event" returnType="flash.events::Event"/>
    <method name="formatToString" declaredBy="flash.events::Event" returnType="String">
      <parameter index="1" type="String" optional="false"/>
    </method>
    <accessor name="eventPhase" access="readonly" type="uint" declaredBy="flash.events::Event"/>
    <method name="stopPropagation" declaredBy="flash.events::Event" returnType="void"/>
    <method name="isDefaultPrevented" declaredBy="flash.events::Event" returnType="Boolean"/>
  </factory>
</type>

In this case the constants are all listed as sub-nodes of the main node and the properties and events are actually found in a sub-node called <factory> so the way to get the list of properties and methods will be relative to the <type><factory> node. Also, the isStatic attribute of the base <type> node will tell you that the target object is static or not.

A Boolean value occupies 4 bytes ???

I had a little time today to go through some of the blogs I read and Lee Brimelow wrote on The Flash Blog that a Boolean takes up 4 bytes (confirmed by the Flash player team). Initially, in his article he wrote about one byte being occupied by a Boolean but he updated it later on.

While I understand having a Boolean occupy 1 byte (guess that would be a minimum size for objects), I think having 4 bytes for a Boolean is a little too much. Especially since a Boolean value can only have two values (true/false – 0/1) so it would only need a single bit. In this case, 10 Booleans would take up to 40 bytes. Now, for the great majority of Flash apps this isn’t really a problem but for those who need to keep their apps to a VERY VERY minimal, that space could count, or in case when high speed is needed.

The conclusion: don’t use too many Booleans if you don’t have to or use Lee’s method of to handle more Boolean values (a good one I might add), by using ByteArrays.

Loader class with content scaling

Some time ago I needed a loader object that would allow scaling the content. So I simply created a class that extends the Loader class itself and adds that extra feature to it. The scaling occurs write after the content has been loaded and during resizing. I’ve named the class SimpleLoader and it has a Boolean property that allows the scaling when set to true, otherwise it just behaves like the standard Loader class.

I thought I should share it with you, so here it is. The class is documented so you should have no problem understanding it. Feel free to use it as you like.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package {
 
	import flash.display.Loader;
	import flash.events.Event;
 
 
	/**
	 * class SimpleLoader
	 * 
	 * @author negush
	 */
	public class SimpleLoader extends Loader {
 
		/**
		 * The width and height setters have effect only after the content has been loaded.
		 */
 
		private var _scale:Boolean = false;
		private var _width:Number = 0;
		private var _height:Number = 0;
 
		private var scaleRatio:Number;
 
 
		/**
		 * Constructor.
		 */
		public function SimpleLoader() {
			super();
			this.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
		}
 
 
		/**
		 * Handler function for the Event.COMPLETE event. Initializes the width and height of the
		 * content and calculates the scale ratio of the image, used when the loader is resized
		 * with the content being scaled. Catches the Loader's event and dispatches it as SimpleLoader.
		 */
		private function completeHandler(evt:Event):void {
			// The initial size of the content.
			this._width = this.contentLoaderInfo.width;
			this._height = this.contentLoaderInfo.height;
			// The scale ratio content_width / content_height.
			this.scaleRatio = this.contentLoaderInfo.width / this.contentLoaderInfo.height;
			if (this.scale) scaleToWidth();
			var newEvent:Event = new Event(Event.COMPLETE);
			dispatchEvent(evt);
		}
 
 
		/**
		 * Resizes the loader keeping the aspect ratio according to the new width. In this case the
		 * new height is calculated so that the content keeps its aspect ratio according to the
		 * loader's new width.
		 */
		private function scaleToWidth():void {
			this._height = Math.round(this.width / this.scaleRatio);
			super.width = this._width;
			super.height = this._height;
		}
 
 
		/**
		 * Resizes the loader keeping the aspect ratio according to the new height. In this case the
		 * new width is calculated so that the content keeps its aspect ratio according to the
		 * loader's new height.
		 */
		private function scaleToHeight():void {
			this._width = Math.round(this.height * this.scaleRatio);
			super.width = this._width;
			super.height = this._height;
		}
 
 
		//***********************************************************************
		//
		//	getters and setters
		//
		//***********************************************************************
 
		/**
		 * Setter/getter for the _scale property. Activates or deactivates the scaling for the
		 * current loader instance. If the scale is set to true, the content's height will be
		 * recalculated so that the content keeps the current width but will have the original
		 * aspect ratio.
		 * 
		 * true = resize keeping content aspect ratio
		 * false = resize without keeping content aspect ratio
		 * 
		 */
		public function set scale(value:Boolean):void {
			if (this._scale == value) return;
			this._scale = value;
			if (value) scaleToWidth();
		}
 
		public function get scale():Boolean {
			return this._scale;
		}
 
 
		/**
		 * Overrides the Loader's width setter and getter. The setter resizes the content
		 * according to the scale property's value.
		 */
		override public function set width(value:Number):void {
			if (this._width == value) return;
			this._width = value;
			if (this.scale) scaleToWidth();
			else super.width = value;
		}
 
		override public function get width():Number {
			return this._width;
		}
 
 
		/**
		 * Overrides the Loader's height setter and getter. The setter resizes the content
		 * according to the scale property's value.
		 */
		override public function set height(value:Number):void {
			if (this._height == value) return;
			this._height = value;
			if (this.scale) scaleToHeight();
			else super.height = value;
		}
 
		override public function get height():Number {
			return this._height;
		}
 
	}
 
}

UPDATE:
And here is how you can use it. It works just like the Loader class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import SimpleLoader;
 
var ldr:SimpleLoader = new SimpleLoader();
ldr.load(new URLRequest("myimage.jpg"));
ldr.x = 20;
ldr.y = 20;
ldr.scale = true;
addChild(ldr);
 
ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
 
function completeHandler(evt:Event):void {
	ldr.width = 300;
	ldr.height = 100;
	trace("loader size: "+ldr.width+" x "+ldr.height);
}